Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_backing_store.cc
blob5f604a3f5ad178e7208108326798e1e569ad7cce
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.
5 #include "content/browser/indexed_db/indexed_db_backing_store.h"
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.h"
9 #include "base/format_macros.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "content/browser/child_process_security_policy_impl.h"
18 #include "content/browser/indexed_db/indexed_db_blob_info.h"
19 #include "content/browser/indexed_db/indexed_db_class_factory.h"
20 #include "content/browser/indexed_db/indexed_db_database_error.h"
21 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
22 #include "content/browser/indexed_db/indexed_db_metadata.h"
23 #include "content/browser/indexed_db/indexed_db_tracing.h"
24 #include "content/browser/indexed_db/indexed_db_value.h"
25 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
26 #include "content/browser/indexed_db/leveldb/leveldb_database.h"
27 #include "content/browser/indexed_db/leveldb/leveldb_factory.h"
28 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
29 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
30 #include "content/common/indexed_db/indexed_db_key.h"
31 #include "content/common/indexed_db/indexed_db_key_path.h"
32 #include "content/common/indexed_db/indexed_db_key_range.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "net/url_request/url_request_context.h"
35 #include "storage/browser/blob/blob_data_handle.h"
36 #include "storage/browser/fileapi/file_stream_writer.h"
37 #include "storage/browser/fileapi/file_writer_delegate.h"
38 #include "storage/browser/fileapi/local_file_stream_writer.h"
39 #include "storage/common/database/database_identifier.h"
40 #include "third_party/WebKit/public/platform/WebIDBTypes.h"
41 #include "third_party/WebKit/public/web/WebSerializedScriptValueVersion.h"
42 #include "third_party/leveldatabase/env_chromium.h"
44 using base::FilePath;
45 using base::StringPiece;
46 using storage::FileWriterDelegate;
48 namespace content {
50 namespace {
52 FilePath GetBlobDirectoryName(const FilePath& pathBase, int64 database_id) {
53 return pathBase.AppendASCII(base::StringPrintf("%" PRIx64, database_id));
56 FilePath GetBlobDirectoryNameForKey(const FilePath& pathBase,
57 int64 database_id,
58 int64 key) {
59 FilePath path = GetBlobDirectoryName(pathBase, database_id);
60 path = path.AppendASCII(base::StringPrintf(
61 "%02x", static_cast<int>(key & 0x000000000000ff00) >> 8));
62 return path;
65 FilePath GetBlobFileNameForKey(const FilePath& pathBase,
66 int64 database_id,
67 int64 key) {
68 FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
69 path = path.AppendASCII(base::StringPrintf("%" PRIx64, key));
70 return path;
73 bool MakeIDBBlobDirectory(const FilePath& pathBase,
74 int64 database_id,
75 int64 key) {
76 FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
77 return base::CreateDirectory(path);
80 static std::string ComputeOriginIdentifier(const GURL& origin_url) {
81 return storage::GetIdentifierFromOrigin(origin_url) + "@1";
84 static base::FilePath ComputeFileName(const GURL& origin_url) {
85 return base::FilePath()
86 .AppendASCII(storage::GetIdentifierFromOrigin(origin_url))
87 .AddExtension(FILE_PATH_LITERAL(".indexeddb.leveldb"));
90 static base::FilePath ComputeBlobPath(const GURL& origin_url) {
91 return base::FilePath()
92 .AppendASCII(storage::GetIdentifierFromOrigin(origin_url))
93 .AddExtension(FILE_PATH_LITERAL(".indexeddb.blob"));
96 static base::FilePath ComputeCorruptionFileName(const GURL& origin_url) {
97 return ComputeFileName(origin_url)
98 .Append(FILE_PATH_LITERAL("corruption_info.json"));
101 } // namespace
103 static const int64 kKeyGeneratorInitialNumber =
104 1; // From the IndexedDB specification.
106 enum IndexedDBBackingStoreErrorSource {
107 // 0 - 2 are no longer used.
108 FIND_KEY_IN_INDEX = 3,
109 GET_IDBDATABASE_METADATA,
110 GET_INDEXES,
111 GET_KEY_GENERATOR_CURRENT_NUMBER,
112 GET_OBJECT_STORES,
113 GET_RECORD,
114 KEY_EXISTS_IN_OBJECT_STORE,
115 LOAD_CURRENT_ROW,
116 SET_UP_METADATA,
117 GET_PRIMARY_KEY_VIA_INDEX,
118 KEY_EXISTS_IN_INDEX,
119 VERSION_EXISTS,
120 DELETE_OBJECT_STORE,
121 SET_MAX_OBJECT_STORE_ID,
122 SET_MAX_INDEX_ID,
123 GET_NEW_DATABASE_ID,
124 GET_NEW_VERSION_NUMBER,
125 CREATE_IDBDATABASE_METADATA,
126 DELETE_DATABASE,
127 TRANSACTION_COMMIT_METHOD, // TRANSACTION_COMMIT is a WinNT.h macro
128 GET_DATABASE_NAMES,
129 DELETE_INDEX,
130 CLEAR_OBJECT_STORE,
131 READ_BLOB_JOURNAL,
132 DECODE_BLOB_JOURNAL,
133 GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER,
134 GET_BLOB_INFO_FOR_RECORD,
135 INTERNAL_ERROR_MAX,
138 static void RecordInternalError(const char* type,
139 IndexedDBBackingStoreErrorSource location) {
140 std::string name;
141 name.append("WebCore.IndexedDB.BackingStore.").append(type).append("Error");
142 base::Histogram::FactoryGet(name,
144 INTERNAL_ERROR_MAX,
145 INTERNAL_ERROR_MAX + 1,
146 base::HistogramBase::kUmaTargetedHistogramFlag)
147 ->Add(location);
150 // Use to signal conditions caused by data corruption.
151 // A macro is used instead of an inline function so that the assert and log
152 // report the line number.
153 #define REPORT_ERROR(type, location) \
154 do { \
155 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
156 RecordInternalError(type, location); \
157 } while (0)
159 #define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location)
160 #define INTERNAL_CONSISTENCY_ERROR(location) \
161 REPORT_ERROR("Consistency", location)
162 #define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
164 // Use to signal conditions that usually indicate developer error, but
165 // could be caused by data corruption. A macro is used instead of an
166 // inline function so that the assert and log report the line number.
167 // TODO(cmumford): Improve test coverage so that all error conditions are
168 // "tested" and then delete this macro.
169 #define REPORT_ERROR_UNTESTED(type, location) \
170 do { \
171 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
172 NOTREACHED(); \
173 RecordInternalError(type, location); \
174 } while (0)
176 #define INTERNAL_READ_ERROR_UNTESTED(location) \
177 REPORT_ERROR_UNTESTED("Read", location)
178 #define INTERNAL_CONSISTENCY_ERROR_UNTESTED(location) \
179 REPORT_ERROR_UNTESTED("Consistency", location)
180 #define INTERNAL_WRITE_ERROR_UNTESTED(location) \
181 REPORT_ERROR_UNTESTED("Write", location)
183 static void PutBool(LevelDBTransaction* transaction,
184 const StringPiece& key,
185 bool value) {
186 std::string buffer;
187 EncodeBool(value, &buffer);
188 transaction->Put(key, &buffer);
191 // Was able to use LevelDB to read the data w/o error, but the data read was not
192 // in the expected format.
193 static leveldb::Status InternalInconsistencyStatus() {
194 return leveldb::Status::Corruption("Internal inconsistency");
197 static leveldb::Status InvalidDBKeyStatus() {
198 return leveldb::Status::InvalidArgument("Invalid database key ID");
201 static leveldb::Status IOErrorStatus() {
202 return leveldb::Status::IOError("IO Error");
205 template <typename DBOrTransaction>
206 static leveldb::Status GetInt(DBOrTransaction* db,
207 const StringPiece& key,
208 int64* found_int,
209 bool* found) {
210 std::string result;
211 leveldb::Status s = db->Get(key, &result, found);
212 if (!s.ok())
213 return s;
214 if (!*found)
215 return leveldb::Status::OK();
216 StringPiece slice(result);
217 if (DecodeInt(&slice, found_int) && slice.empty())
218 return s;
219 return InternalInconsistencyStatus();
222 static void PutInt(LevelDBTransaction* transaction,
223 const StringPiece& key,
224 int64 value) {
225 DCHECK_GE(value, 0);
226 std::string buffer;
227 EncodeInt(value, &buffer);
228 transaction->Put(key, &buffer);
231 template <typename DBOrTransaction>
232 WARN_UNUSED_RESULT static leveldb::Status GetVarInt(DBOrTransaction* db,
233 const StringPiece& key,
234 int64* found_int,
235 bool* found) {
236 std::string result;
237 leveldb::Status s = db->Get(key, &result, found);
238 if (!s.ok())
239 return s;
240 if (!*found)
241 return leveldb::Status::OK();
242 StringPiece slice(result);
243 if (DecodeVarInt(&slice, found_int) && slice.empty())
244 return s;
245 return InternalInconsistencyStatus();
248 static void PutVarInt(LevelDBTransaction* transaction,
249 const StringPiece& key,
250 int64 value) {
251 std::string buffer;
252 EncodeVarInt(value, &buffer);
253 transaction->Put(key, &buffer);
256 template <typename DBOrTransaction>
257 WARN_UNUSED_RESULT static leveldb::Status GetString(
258 DBOrTransaction* db,
259 const StringPiece& key,
260 base::string16* found_string,
261 bool* found) {
262 std::string result;
263 *found = false;
264 leveldb::Status s = db->Get(key, &result, found);
265 if (!s.ok())
266 return s;
267 if (!*found)
268 return leveldb::Status::OK();
269 StringPiece slice(result);
270 if (DecodeString(&slice, found_string) && slice.empty())
271 return s;
272 return InternalInconsistencyStatus();
275 static void PutString(LevelDBTransaction* transaction,
276 const StringPiece& key,
277 const base::string16& value) {
278 std::string buffer;
279 EncodeString(value, &buffer);
280 transaction->Put(key, &buffer);
283 static void PutIDBKeyPath(LevelDBTransaction* transaction,
284 const StringPiece& key,
285 const IndexedDBKeyPath& value) {
286 std::string buffer;
287 EncodeIDBKeyPath(value, &buffer);
288 transaction->Put(key, &buffer);
291 static int CompareKeys(const StringPiece& a, const StringPiece& b) {
292 return Compare(a, b, false /*index_keys*/);
295 static int CompareIndexKeys(const StringPiece& a, const StringPiece& b) {
296 return Compare(a, b, true /*index_keys*/);
299 int IndexedDBBackingStore::Comparator::Compare(const StringPiece& a,
300 const StringPiece& b) const {
301 return content::Compare(a, b, false /*index_keys*/);
304 const char* IndexedDBBackingStore::Comparator::Name() const {
305 return "idb_cmp1";
308 // 0 - Initial version.
309 // 1 - Adds UserIntVersion to DatabaseMetaData.
310 // 2 - Adds DataVersion to to global metadata.
311 // 3 - Adds metadata needed for blob support.
312 static const int64 kLatestKnownSchemaVersion = 3;
313 WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
314 int64 db_schema_version = 0;
315 bool found = false;
316 leveldb::Status s =
317 GetInt(db, SchemaVersionKey::Encode(), &db_schema_version, &found);
318 if (!s.ok())
319 return false;
320 if (!found) {
321 *known = true;
322 return true;
324 if (db_schema_version > kLatestKnownSchemaVersion) {
325 *known = false;
326 return true;
329 const uint32 latest_known_data_version =
330 blink::kSerializedScriptValueVersion;
331 int64 db_data_version = 0;
332 s = GetInt(db, DataVersionKey::Encode(), &db_data_version, &found);
333 if (!s.ok())
334 return false;
335 if (!found) {
336 *known = true;
337 return true;
340 if (db_data_version > latest_known_data_version) {
341 *known = false;
342 return true;
345 *known = true;
346 return true;
349 // TODO(ericu): Move this down into the member section of this file. I'm
350 // leaving it here for this CL as it's easier to see the diffs in place.
351 WARN_UNUSED_RESULT leveldb::Status IndexedDBBackingStore::SetUpMetadata() {
352 const uint32 latest_known_data_version =
353 blink::kSerializedScriptValueVersion;
354 const std::string schema_version_key = SchemaVersionKey::Encode();
355 const std::string data_version_key = DataVersionKey::Encode();
357 scoped_refptr<LevelDBTransaction> transaction =
358 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
360 int64 db_schema_version = 0;
361 int64 db_data_version = 0;
362 bool found = false;
363 leveldb::Status s =
364 GetInt(transaction.get(), schema_version_key, &db_schema_version, &found);
365 if (!s.ok()) {
366 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
367 return s;
369 if (!found) {
370 // Initialize new backing store.
371 db_schema_version = kLatestKnownSchemaVersion;
372 PutInt(transaction.get(), schema_version_key, db_schema_version);
373 db_data_version = latest_known_data_version;
374 PutInt(transaction.get(), data_version_key, db_data_version);
375 // If a blob directory already exists for this database, blow it away. It's
376 // leftover from a partially-purged previous generation of data.
377 if (!base::DeleteFile(blob_path_, true)) {
378 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
379 return IOErrorStatus();
381 } else {
382 // Upgrade old backing store.
383 DCHECK_LE(db_schema_version, kLatestKnownSchemaVersion);
384 if (db_schema_version < 1) {
385 db_schema_version = 1;
386 PutInt(transaction.get(), schema_version_key, db_schema_version);
387 const std::string start_key =
388 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
389 const std::string stop_key =
390 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
391 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
392 for (s = it->Seek(start_key);
393 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
394 s = it->Next()) {
395 int64 database_id = 0;
396 found = false;
397 s = GetInt(transaction.get(), it->Key(), &database_id, &found);
398 if (!s.ok()) {
399 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
400 return s;
402 if (!found) {
403 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
404 return InternalInconsistencyStatus();
406 std::string int_version_key = DatabaseMetaDataKey::Encode(
407 database_id, DatabaseMetaDataKey::USER_INT_VERSION);
408 PutVarInt(transaction.get(),
409 int_version_key,
410 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
413 if (s.ok() && db_schema_version < 2) {
414 db_schema_version = 2;
415 PutInt(transaction.get(), schema_version_key, db_schema_version);
416 db_data_version = blink::kSerializedScriptValueVersion;
417 PutInt(transaction.get(), data_version_key, db_data_version);
419 if (db_schema_version < 3) {
420 db_schema_version = 3;
421 if (!base::DeleteFile(blob_path_, true)) {
422 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
423 return IOErrorStatus();
428 if (!s.ok()) {
429 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
430 return s;
433 // All new values will be written using this serialization version.
434 found = false;
435 s = GetInt(transaction.get(), data_version_key, &db_data_version, &found);
436 if (!s.ok()) {
437 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
438 return s;
440 if (!found) {
441 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
442 return InternalInconsistencyStatus();
444 if (db_data_version < latest_known_data_version) {
445 db_data_version = latest_known_data_version;
446 PutInt(transaction.get(), data_version_key, db_data_version);
449 DCHECK_EQ(db_schema_version, kLatestKnownSchemaVersion);
450 DCHECK_EQ(db_data_version, latest_known_data_version);
452 s = transaction->Commit();
453 if (!s.ok())
454 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
455 return s;
458 template <typename DBOrTransaction>
459 WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
460 DBOrTransaction* db,
461 int64 database_id,
462 int64* max_object_store_id) {
463 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
464 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
465 return GetMaxObjectStoreId(db, max_object_store_id_key, max_object_store_id);
468 template <typename DBOrTransaction>
469 WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
470 DBOrTransaction* db,
471 const std::string& max_object_store_id_key,
472 int64* max_object_store_id) {
473 *max_object_store_id = -1;
474 bool found = false;
475 leveldb::Status s =
476 GetInt(db, max_object_store_id_key, max_object_store_id, &found);
477 if (!s.ok())
478 return s;
479 if (!found)
480 *max_object_store_id = 0;
482 DCHECK_GE(*max_object_store_id, 0);
483 return s;
486 class DefaultLevelDBFactory : public LevelDBFactory {
487 public:
488 DefaultLevelDBFactory() {}
489 leveldb::Status OpenLevelDB(const base::FilePath& file_name,
490 const LevelDBComparator* comparator,
491 scoped_ptr<LevelDBDatabase>* db,
492 bool* is_disk_full) override {
493 return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full);
495 leveldb::Status DestroyLevelDB(const base::FilePath& file_name) override {
496 return LevelDBDatabase::Destroy(file_name);
499 private:
500 DISALLOW_COPY_AND_ASSIGN(DefaultLevelDBFactory);
503 static bool GetBlobKeyGeneratorCurrentNumber(
504 LevelDBTransaction* leveldb_transaction,
505 int64 database_id,
506 int64* blob_key_generator_current_number) {
507 const std::string key_gen_key = DatabaseMetaDataKey::Encode(
508 database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
510 // Default to initial number if not found.
511 int64 cur_number = DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber;
512 std::string data;
514 bool found = false;
515 bool ok = leveldb_transaction->Get(key_gen_key, &data, &found).ok();
516 if (!ok) {
517 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
518 return false;
520 if (found) {
521 StringPiece slice(data);
522 if (!DecodeVarInt(&slice, &cur_number) || !slice.empty() ||
523 !DatabaseMetaDataKey::IsValidBlobKey(cur_number)) {
524 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
525 return false;
528 *blob_key_generator_current_number = cur_number;
529 return true;
532 static bool UpdateBlobKeyGeneratorCurrentNumber(
533 LevelDBTransaction* leveldb_transaction,
534 int64 database_id,
535 int64 blob_key_generator_current_number) {
536 #ifndef NDEBUG
537 int64 old_number;
538 if (!GetBlobKeyGeneratorCurrentNumber(
539 leveldb_transaction, database_id, &old_number))
540 return false;
541 DCHECK_LT(old_number, blob_key_generator_current_number);
542 #endif
543 DCHECK(
544 DatabaseMetaDataKey::IsValidBlobKey(blob_key_generator_current_number));
545 const std::string key = DatabaseMetaDataKey::Encode(
546 database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
548 PutVarInt(leveldb_transaction, key, blob_key_generator_current_number);
549 return true;
552 // TODO(ericu): Error recovery. If we persistently can't read the
553 // blob journal, the safe thing to do is to clear it and leak the blobs,
554 // though that may be costly. Still, database/directory deletion should always
555 // clean things up, and we can write an fsck that will do a full correction if
556 // need be.
557 template <typename T>
558 static leveldb::Status GetBlobJournal(const StringPiece& leveldb_key,
559 T* leveldb_transaction,
560 BlobJournalType* journal) {
561 std::string data;
562 bool found = false;
563 leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
564 if (!s.ok()) {
565 INTERNAL_READ_ERROR(READ_BLOB_JOURNAL);
566 return s;
568 journal->clear();
569 if (!found || !data.size())
570 return leveldb::Status::OK();
571 StringPiece slice(data);
572 if (!DecodeBlobJournal(&slice, journal)) {
573 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DECODE_BLOB_JOURNAL);
574 s = InternalInconsistencyStatus();
576 return s;
579 static void ClearBlobJournal(LevelDBTransaction* leveldb_transaction,
580 const std::string& level_db_key) {
581 leveldb_transaction->Remove(level_db_key);
584 static void UpdatePrimaryJournalWithBlobList(
585 LevelDBTransaction* leveldb_transaction,
586 const BlobJournalType& journal) {
587 const std::string leveldb_key = BlobJournalKey::Encode();
588 std::string data;
589 EncodeBlobJournal(journal, &data);
590 leveldb_transaction->Put(leveldb_key, &data);
593 static void UpdateLiveBlobJournalWithBlobList(
594 LevelDBTransaction* leveldb_transaction,
595 const BlobJournalType& journal) {
596 const std::string leveldb_key = LiveBlobJournalKey::Encode();
597 std::string data;
598 EncodeBlobJournal(journal, &data);
599 leveldb_transaction->Put(leveldb_key, &data);
602 static leveldb::Status MergeBlobsIntoLiveBlobJournal(
603 LevelDBTransaction* leveldb_transaction,
604 const BlobJournalType& journal) {
605 BlobJournalType old_journal;
606 const std::string key = LiveBlobJournalKey::Encode();
607 leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &old_journal);
608 if (!s.ok())
609 return s;
611 old_journal.insert(old_journal.end(), journal.begin(), journal.end());
613 UpdateLiveBlobJournalWithBlobList(leveldb_transaction, old_journal);
614 return leveldb::Status::OK();
617 static void UpdateBlobJournalWithDatabase(
618 LevelDBDirectTransaction* leveldb_transaction,
619 int64 database_id) {
620 BlobJournalType journal;
621 journal.push_back(
622 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
623 const std::string key = BlobJournalKey::Encode();
624 std::string data;
625 EncodeBlobJournal(journal, &data);
626 leveldb_transaction->Put(key, &data);
629 static leveldb::Status MergeDatabaseIntoLiveBlobJournal(
630 LevelDBDirectTransaction* leveldb_transaction,
631 int64 database_id) {
632 BlobJournalType journal;
633 const std::string key = LiveBlobJournalKey::Encode();
634 leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &journal);
635 if (!s.ok())
636 return s;
637 journal.push_back(
638 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
639 std::string data;
640 EncodeBlobJournal(journal, &data);
641 leveldb_transaction->Put(key, &data);
642 return leveldb::Status::OK();
645 // Blob Data is encoded as a series of:
646 // { is_file [bool], key [int64 as varInt],
647 // type [string-with-length, may be empty],
648 // (for Blobs only) size [int64 as varInt]
649 // (for Files only) fileName [string-with-length]
650 // }
651 // There is no length field; just read until you run out of data.
652 static std::string EncodeBlobData(
653 const std::vector<IndexedDBBlobInfo*>& blob_info) {
654 std::string ret;
655 for (const auto* info : blob_info) {
656 EncodeBool(info->is_file(), &ret);
657 EncodeVarInt(info->key(), &ret);
658 EncodeStringWithLength(info->type(), &ret);
659 if (info->is_file())
660 EncodeStringWithLength(info->file_name(), &ret);
661 else
662 EncodeVarInt(info->size(), &ret);
664 return ret;
667 static bool DecodeBlobData(const std::string& data,
668 std::vector<IndexedDBBlobInfo>* output) {
669 std::vector<IndexedDBBlobInfo> ret;
670 output->clear();
671 StringPiece slice(data);
672 while (!slice.empty()) {
673 bool is_file;
674 int64 key;
675 base::string16 type;
676 int64 size;
677 base::string16 file_name;
679 if (!DecodeBool(&slice, &is_file))
680 return false;
681 if (!DecodeVarInt(&slice, &key) ||
682 !DatabaseMetaDataKey::IsValidBlobKey(key))
683 return false;
684 if (!DecodeStringWithLength(&slice, &type))
685 return false;
686 if (is_file) {
687 if (!DecodeStringWithLength(&slice, &file_name))
688 return false;
689 ret.push_back(IndexedDBBlobInfo(key, type, file_name));
690 } else {
691 if (!DecodeVarInt(&slice, &size) || size < 0)
692 return false;
693 ret.push_back(IndexedDBBlobInfo(type, static_cast<uint64>(size), key));
696 output->swap(ret);
698 return true;
701 IndexedDBBackingStore::IndexedDBBackingStore(
702 IndexedDBFactory* indexed_db_factory,
703 const GURL& origin_url,
704 const base::FilePath& blob_path,
705 net::URLRequestContext* request_context,
706 scoped_ptr<LevelDBDatabase> db,
707 scoped_ptr<LevelDBComparator> comparator,
708 base::SequencedTaskRunner* task_runner)
709 : indexed_db_factory_(indexed_db_factory),
710 origin_url_(origin_url),
711 blob_path_(blob_path),
712 origin_identifier_(ComputeOriginIdentifier(origin_url)),
713 request_context_(request_context),
714 task_runner_(task_runner),
715 db_(db.Pass()),
716 comparator_(comparator.Pass()),
717 active_blob_registry_(this) {
720 IndexedDBBackingStore::~IndexedDBBackingStore() {
721 if (!blob_path_.empty() && !child_process_ids_granted_.empty()) {
722 ChildProcessSecurityPolicyImpl* policy =
723 ChildProcessSecurityPolicyImpl::GetInstance();
724 for (const auto& pid : child_process_ids_granted_)
725 policy->RevokeAllPermissionsForFile(pid, blob_path_);
727 STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
728 incognito_blob_map_.end());
729 // db_'s destructor uses comparator_. The order of destruction is important.
730 db_.reset();
731 comparator_.reset();
734 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
735 const std::string& primary_key,
736 int64 version)
737 : primary_key_(primary_key), version_(version) {
738 DCHECK(!primary_key.empty());
740 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
741 : primary_key_(), version_(-1) {}
742 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
744 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
745 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
747 enum IndexedDBBackingStoreOpenResult {
748 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS,
749 INDEXED_DB_BACKING_STORE_OPEN_SUCCESS,
750 INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
751 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
752 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
753 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
754 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
755 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
756 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR,
757 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
758 INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
759 INDEXED_DB_BACKING_STORE_OPEN_DISK_FULL_DEPRECATED,
760 INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
761 INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
762 INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
763 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR,
764 INDEXED_DB_BACKING_STORE_OPEN_MAX,
767 // static
768 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
769 IndexedDBFactory* indexed_db_factory,
770 const GURL& origin_url,
771 const base::FilePath& path_base,
772 net::URLRequestContext* request_context,
773 blink::WebIDBDataLoss* data_loss,
774 std::string* data_loss_message,
775 bool* disk_full,
776 base::SequencedTaskRunner* task_runner,
777 bool clean_journal,
778 leveldb::Status* status) {
779 *data_loss = blink::WebIDBDataLossNone;
780 DefaultLevelDBFactory leveldb_factory;
781 return IndexedDBBackingStore::Open(indexed_db_factory,
782 origin_url,
783 path_base,
784 request_context,
785 data_loss,
786 data_loss_message,
787 disk_full,
788 &leveldb_factory,
789 task_runner,
790 clean_journal,
791 status);
794 static std::string OriginToCustomHistogramSuffix(const GURL& origin_url) {
795 if (origin_url.host() == "docs.google.com")
796 return ".Docs";
797 return std::string();
800 static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result,
801 const GURL& origin_url) {
802 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.BackingStore.OpenStatus",
803 result,
804 INDEXED_DB_BACKING_STORE_OPEN_MAX);
805 const std::string suffix = OriginToCustomHistogramSuffix(origin_url);
806 // Data from the WebCore.IndexedDB.BackingStore.OpenStatus histogram is used
807 // to generate a graph. So as not to alter the meaning of that graph,
808 // continue to collect all stats there (above) but also now collect docs stats
809 // separately (below).
810 if (!suffix.empty()) {
811 base::LinearHistogram::FactoryGet(
812 "WebCore.IndexedDB.BackingStore.OpenStatus" + suffix,
814 INDEXED_DB_BACKING_STORE_OPEN_MAX,
815 INDEXED_DB_BACKING_STORE_OPEN_MAX + 1,
816 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(result);
820 static bool IsPathTooLong(const base::FilePath& leveldb_dir) {
821 int limit = base::GetMaximumPathComponentLength(leveldb_dir.DirName());
822 if (limit == -1) {
823 DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
824 // In limited testing, ChromeOS returns 143, other OSes 255.
825 #if defined(OS_CHROMEOS)
826 limit = 143;
827 #else
828 limit = 255;
829 #endif
831 size_t component_length = leveldb_dir.BaseName().value().length();
832 if (component_length > static_cast<uint32_t>(limit)) {
833 DLOG(WARNING) << "Path component length (" << component_length
834 << ") exceeds maximum (" << limit
835 << ") allowed by this filesystem.";
836 const int min = 140;
837 const int max = 300;
838 const int num_buckets = 12;
839 UMA_HISTOGRAM_CUSTOM_COUNTS(
840 "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
841 component_length,
842 min,
843 max,
844 num_buckets);
845 return true;
847 return false;
850 leveldb::Status IndexedDBBackingStore::DestroyBackingStore(
851 const base::FilePath& path_base,
852 const GURL& origin_url) {
853 const base::FilePath file_path =
854 path_base.Append(ComputeFileName(origin_url));
855 DefaultLevelDBFactory leveldb_factory;
856 return leveldb_factory.DestroyLevelDB(file_path);
859 bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath& path_base,
860 const GURL& origin_url,
861 std::string* message) {
862 const base::FilePath info_path =
863 path_base.Append(ComputeCorruptionFileName(origin_url));
865 if (IsPathTooLong(info_path))
866 return false;
868 const int64 max_json_len = 4096;
869 int64 file_size(0);
870 if (!GetFileSize(info_path, &file_size) || file_size > max_json_len)
871 return false;
872 if (!file_size) {
873 NOTREACHED();
874 return false;
877 base::File file(info_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
878 bool success = false;
879 if (file.IsValid()) {
880 std::vector<char> bytes(file_size);
881 if (file_size == file.Read(0, &bytes[0], file_size)) {
882 std::string input_js(&bytes[0], file_size);
883 base::JSONReader reader;
884 scoped_ptr<base::Value> val(reader.ReadToValue(input_js));
885 if (val && val->GetType() == base::Value::TYPE_DICTIONARY) {
886 base::DictionaryValue* dict_val =
887 static_cast<base::DictionaryValue*>(val.get());
888 success = dict_val->GetString("message", message);
891 file.Close();
894 base::DeleteFile(info_path, false);
896 return success;
899 bool IndexedDBBackingStore::RecordCorruptionInfo(
900 const base::FilePath& path_base,
901 const GURL& origin_url,
902 const std::string& message) {
903 const base::FilePath info_path =
904 path_base.Append(ComputeCorruptionFileName(origin_url));
905 if (IsPathTooLong(info_path))
906 return false;
908 base::DictionaryValue root_dict;
909 root_dict.SetString("message", message);
910 std::string output_js;
911 base::JSONWriter::Write(&root_dict, &output_js);
913 base::File file(info_path,
914 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
915 if (!file.IsValid())
916 return false;
917 int written = file.Write(0, output_js.c_str(), output_js.length());
918 return size_t(written) == output_js.length();
921 // static
922 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
923 IndexedDBFactory* indexed_db_factory,
924 const GURL& origin_url,
925 const base::FilePath& path_base,
926 net::URLRequestContext* request_context,
927 blink::WebIDBDataLoss* data_loss,
928 std::string* data_loss_message,
929 bool* is_disk_full,
930 LevelDBFactory* leveldb_factory,
931 base::SequencedTaskRunner* task_runner,
932 bool clean_journal,
933 leveldb::Status* status) {
934 IDB_TRACE("IndexedDBBackingStore::Open");
935 DCHECK(!path_base.empty());
936 *data_loss = blink::WebIDBDataLossNone;
937 *data_loss_message = "";
938 *is_disk_full = false;
940 *status = leveldb::Status::OK();
942 scoped_ptr<LevelDBComparator> comparator(new Comparator());
944 if (!base::IsStringASCII(path_base.AsUTF8Unsafe())) {
945 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
946 origin_url);
948 if (!base::CreateDirectory(path_base)) {
949 *status =
950 leveldb::Status::IOError("Unable to create IndexedDB database path");
951 LOG(ERROR) << status->ToString() << ": \"" << path_base.AsUTF8Unsafe()
952 << "\"";
953 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
954 origin_url);
955 return scoped_refptr<IndexedDBBackingStore>();
958 const base::FilePath file_path =
959 path_base.Append(ComputeFileName(origin_url));
960 const base::FilePath blob_path =
961 path_base.Append(ComputeBlobPath(origin_url));
963 if (IsPathTooLong(file_path)) {
964 *status = leveldb::Status::IOError("File path too long");
965 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
966 origin_url);
967 return scoped_refptr<IndexedDBBackingStore>();
970 scoped_ptr<LevelDBDatabase> db;
971 *status = leveldb_factory->OpenLevelDB(
972 file_path, comparator.get(), &db, is_disk_full);
974 DCHECK(!db == !status->ok());
975 if (!status->ok()) {
976 if (leveldb_env::IndicatesDiskFull(*status)) {
977 *is_disk_full = true;
978 } else if (status->IsCorruption()) {
979 *data_loss = blink::WebIDBDataLossTotal;
980 *data_loss_message = leveldb_env::GetCorruptionMessage(*status);
984 bool is_schema_known = false;
985 if (db) {
986 std::string corruption_message;
987 if (ReadCorruptionInfo(path_base, origin_url, &corruption_message)) {
988 LOG(ERROR) << "IndexedDB recovering from a corrupted (and deleted) "
989 "database.";
990 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
991 origin_url);
992 db.reset();
993 *data_loss = blink::WebIDBDataLossTotal;
994 *data_loss_message =
995 "IndexedDB (database was corrupt): " + corruption_message;
996 } else if (!IsSchemaKnown(db.get(), &is_schema_known)) {
997 LOG(ERROR) << "IndexedDB had IO error checking schema, treating it as "
998 "failure to open";
999 HistogramOpenStatus(
1000 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
1001 origin_url);
1002 db.reset();
1003 *data_loss = blink::WebIDBDataLossTotal;
1004 *data_loss_message = "I/O error checking schema";
1005 } else if (!is_schema_known) {
1006 LOG(ERROR) << "IndexedDB backing store had unknown schema, treating it "
1007 "as failure to open";
1008 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
1009 origin_url);
1010 db.reset();
1011 *data_loss = blink::WebIDBDataLossTotal;
1012 *data_loss_message = "Unknown schema";
1016 DCHECK(status->ok() || !is_schema_known || leveldb_env::IsIOError(*status) ||
1017 status->IsCorruption());
1019 if (db) {
1020 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_SUCCESS, origin_url);
1021 } else if (leveldb_env::IsIOError(*status)) {
1022 LOG(ERROR) << "Unable to open backing store, not trying to recover - "
1023 << status->ToString();
1024 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY, origin_url);
1025 return scoped_refptr<IndexedDBBackingStore>();
1026 } else {
1027 DCHECK(!is_schema_known || status->IsCorruption());
1028 LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup";
1029 *status = leveldb_factory->DestroyLevelDB(file_path);
1030 if (!status->ok()) {
1031 LOG(ERROR) << "IndexedDB backing store cleanup failed";
1032 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
1033 origin_url);
1034 return scoped_refptr<IndexedDBBackingStore>();
1037 LOG(ERROR) << "IndexedDB backing store cleanup succeeded, reopening";
1038 leveldb_factory->OpenLevelDB(file_path, comparator.get(), &db, NULL);
1039 if (!db) {
1040 LOG(ERROR) << "IndexedDB backing store reopen after recovery failed";
1041 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
1042 origin_url);
1043 return scoped_refptr<IndexedDBBackingStore>();
1045 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
1046 origin_url);
1049 if (!db) {
1050 NOTREACHED();
1051 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR,
1052 origin_url);
1053 return scoped_refptr<IndexedDBBackingStore>();
1056 scoped_refptr<IndexedDBBackingStore> backing_store =
1057 Create(indexed_db_factory,
1058 origin_url,
1059 blob_path,
1060 request_context,
1061 db.Pass(),
1062 comparator.Pass(),
1063 task_runner,
1064 status);
1066 if (clean_journal && backing_store.get() &&
1067 !backing_store->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
1068 HistogramOpenStatus(
1069 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR, origin_url);
1070 return scoped_refptr<IndexedDBBackingStore>();
1072 return backing_store;
1075 // static
1076 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
1077 const GURL& origin_url,
1078 base::SequencedTaskRunner* task_runner,
1079 leveldb::Status* status) {
1080 DefaultLevelDBFactory leveldb_factory;
1081 return IndexedDBBackingStore::OpenInMemory(
1082 origin_url, &leveldb_factory, task_runner, status);
1085 // static
1086 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
1087 const GURL& origin_url,
1088 LevelDBFactory* leveldb_factory,
1089 base::SequencedTaskRunner* task_runner,
1090 leveldb::Status* status) {
1091 IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
1093 scoped_ptr<LevelDBComparator> comparator(new Comparator());
1094 scoped_ptr<LevelDBDatabase> db =
1095 LevelDBDatabase::OpenInMemory(comparator.get());
1096 if (!db) {
1097 LOG(ERROR) << "LevelDBDatabase::OpenInMemory failed.";
1098 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
1099 origin_url);
1100 return scoped_refptr<IndexedDBBackingStore>();
1102 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS, origin_url);
1104 return Create(NULL /* indexed_db_factory */,
1105 origin_url,
1106 base::FilePath(),
1107 NULL /* request_context */,
1108 db.Pass(),
1109 comparator.Pass(),
1110 task_runner,
1111 status);
1114 // static
1115 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
1116 IndexedDBFactory* indexed_db_factory,
1117 const GURL& origin_url,
1118 const base::FilePath& blob_path,
1119 net::URLRequestContext* request_context,
1120 scoped_ptr<LevelDBDatabase> db,
1121 scoped_ptr<LevelDBComparator> comparator,
1122 base::SequencedTaskRunner* task_runner,
1123 leveldb::Status* status) {
1124 // TODO(jsbell): Handle comparator name changes.
1125 scoped_refptr<IndexedDBBackingStore> backing_store(
1126 new IndexedDBBackingStore(indexed_db_factory,
1127 origin_url,
1128 blob_path,
1129 request_context,
1130 db.Pass(),
1131 comparator.Pass(),
1132 task_runner));
1133 *status = backing_store->SetUpMetadata();
1134 if (!status->ok())
1135 return scoped_refptr<IndexedDBBackingStore>();
1137 return backing_store;
1140 void IndexedDBBackingStore::GrantChildProcessPermissions(int child_process_id) {
1141 if (!child_process_ids_granted_.count(child_process_id)) {
1142 child_process_ids_granted_.insert(child_process_id);
1143 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
1144 child_process_id, blob_path_);
1148 std::vector<base::string16> IndexedDBBackingStore::GetDatabaseNames(
1149 leveldb::Status* s) {
1150 *s = leveldb::Status::OK();
1151 std::vector<base::string16> found_names;
1152 const std::string start_key =
1153 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
1154 const std::string stop_key =
1155 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
1157 DCHECK(found_names.empty());
1159 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1160 for (*s = it->Seek(start_key);
1161 s->ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1162 *s = it->Next()) {
1163 // Decode database name (in iterator key).
1164 StringPiece slice(it->Key());
1165 DatabaseNameKey database_name_key;
1166 if (!DatabaseNameKey::Decode(&slice, &database_name_key) ||
1167 !slice.empty()) {
1168 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
1169 continue;
1172 // Decode database id (in iterator value).
1173 int64 database_id = 0;
1174 StringPiece valueSlice(it->Value());
1175 if (!DecodeInt(&valueSlice, &database_id) || !valueSlice.empty()) {
1176 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
1177 continue;
1180 // Look up version by id.
1181 bool found = false;
1182 int64 database_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1183 *s = GetVarInt(db_.get(),
1184 DatabaseMetaDataKey::Encode(
1185 database_id, DatabaseMetaDataKey::USER_INT_VERSION),
1186 &database_version,
1187 &found);
1188 if (!s->ok() || !found) {
1189 INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES);
1190 continue;
1193 // Ignore stale metadata from failed initial opens.
1194 if (database_version != IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
1195 found_names.push_back(database_name_key.database_name());
1198 if (!s->ok())
1199 INTERNAL_READ_ERROR(GET_DATABASE_NAMES);
1201 return found_names;
1204 leveldb::Status IndexedDBBackingStore::GetIDBDatabaseMetaData(
1205 const base::string16& name,
1206 IndexedDBDatabaseMetadata* metadata,
1207 bool* found) {
1208 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1209 *found = false;
1211 leveldb::Status s = GetInt(db_.get(), key, &metadata->id, found);
1212 if (!s.ok()) {
1213 INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
1214 return s;
1216 if (!*found)
1217 return leveldb::Status::OK();
1219 s = GetString(db_.get(),
1220 DatabaseMetaDataKey::Encode(metadata->id,
1221 DatabaseMetaDataKey::USER_VERSION),
1222 &metadata->version,
1223 found);
1224 if (!s.ok()) {
1225 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1226 return s;
1228 if (!*found) {
1229 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1230 return InternalInconsistencyStatus();
1233 s = GetVarInt(db_.get(),
1234 DatabaseMetaDataKey::Encode(
1235 metadata->id, DatabaseMetaDataKey::USER_INT_VERSION),
1236 &metadata->int_version,
1237 found);
1238 if (!s.ok()) {
1239 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1240 return s;
1242 if (!*found) {
1243 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1244 return InternalInconsistencyStatus();
1247 if (metadata->int_version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
1248 metadata->int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1250 s = GetMaxObjectStoreId(
1251 db_.get(), metadata->id, &metadata->max_object_store_id);
1252 if (!s.ok()) {
1253 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1256 // We don't cache this, we just check it if it's there.
1257 int64 blob_key_generator_current_number =
1258 DatabaseMetaDataKey::kInvalidBlobKey;
1260 s = GetVarInt(
1261 db_.get(),
1262 DatabaseMetaDataKey::Encode(
1263 metadata->id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
1264 &blob_key_generator_current_number,
1265 found);
1266 if (!s.ok()) {
1267 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1268 return s;
1270 if (!*found) {
1271 // This database predates blob support.
1272 *found = true;
1273 } else if (!DatabaseMetaDataKey::IsValidBlobKey(
1274 blob_key_generator_current_number)) {
1275 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1276 return InternalInconsistencyStatus();
1279 return s;
1282 WARN_UNUSED_RESULT static leveldb::Status GetNewDatabaseId(
1283 LevelDBTransaction* transaction,
1284 int64* new_id) {
1285 *new_id = -1;
1286 int64 max_database_id = -1;
1287 bool found = false;
1288 leveldb::Status s =
1289 GetInt(transaction, MaxDatabaseIdKey::Encode(), &max_database_id, &found);
1290 if (!s.ok()) {
1291 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID);
1292 return s;
1294 if (!found)
1295 max_database_id = 0;
1297 DCHECK_GE(max_database_id, 0);
1299 int64 database_id = max_database_id + 1;
1300 PutInt(transaction, MaxDatabaseIdKey::Encode(), database_id);
1301 *new_id = database_id;
1302 return leveldb::Status::OK();
1305 leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData(
1306 const base::string16& name,
1307 const base::string16& version,
1308 int64 int_version,
1309 int64* row_id) {
1310 // TODO(jsbell): Don't persist metadata if open fails. http://crbug.com/395472
1311 scoped_refptr<LevelDBTransaction> transaction =
1312 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
1314 leveldb::Status s = GetNewDatabaseId(transaction.get(), row_id);
1315 if (!s.ok())
1316 return s;
1317 DCHECK_GE(*row_id, 0);
1319 if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1320 int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1322 PutInt(transaction.get(),
1323 DatabaseNameKey::Encode(origin_identifier_, name),
1324 *row_id);
1325 PutString(
1326 transaction.get(),
1327 DatabaseMetaDataKey::Encode(*row_id, DatabaseMetaDataKey::USER_VERSION),
1328 version);
1329 PutVarInt(transaction.get(),
1330 DatabaseMetaDataKey::Encode(*row_id,
1331 DatabaseMetaDataKey::USER_INT_VERSION),
1332 int_version);
1333 PutVarInt(
1334 transaction.get(),
1335 DatabaseMetaDataKey::Encode(
1336 *row_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
1337 DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber);
1339 s = transaction->Commit();
1340 if (!s.ok())
1341 INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA);
1342 return s;
1345 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
1346 IndexedDBBackingStore::Transaction* transaction,
1347 int64 row_id,
1348 int64 int_version) {
1349 if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1350 int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1351 DCHECK_GE(int_version, 0) << "int_version was " << int_version;
1352 PutVarInt(transaction->transaction(),
1353 DatabaseMetaDataKey::Encode(row_id,
1354 DatabaseMetaDataKey::USER_INT_VERSION),
1355 int_version);
1356 return true;
1359 // If you're deleting a range that contains user keys that have blob info, this
1360 // won't clean up the blobs.
1361 static leveldb::Status DeleteRangeBasic(LevelDBTransaction* transaction,
1362 const std::string& begin,
1363 const std::string& end,
1364 bool upper_open) {
1365 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
1366 leveldb::Status s;
1367 for (s = it->Seek(begin); s.ok() && it->IsValid() &&
1368 (upper_open ? CompareKeys(it->Key(), end) < 0
1369 : CompareKeys(it->Key(), end) <= 0);
1370 s = it->Next())
1371 transaction->Remove(it->Key());
1372 return s;
1375 static leveldb::Status DeleteBlobsInRange(
1376 IndexedDBBackingStore::Transaction* transaction,
1377 int64 database_id,
1378 int64 object_store_id,
1379 const std::string& start_key,
1380 const std::string& end_key,
1381 bool upper_open) {
1382 scoped_ptr<LevelDBIterator> it = transaction->transaction()->CreateIterator();
1383 leveldb::Status s = it->Seek(start_key);
1384 for (; s.ok() && it->IsValid() &&
1385 (upper_open ? CompareKeys(it->Key(), end_key) < 0
1386 : CompareKeys(it->Key(), end_key) <= 0);
1387 s = it->Next()) {
1388 StringPiece key_piece(it->Key());
1389 std::string user_key =
1390 BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece);
1391 if (!user_key.size()) {
1392 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1393 return InternalInconsistencyStatus();
1395 transaction->PutBlobInfo(
1396 database_id, object_store_id, user_key, NULL, NULL);
1398 return s;
1401 static leveldb::Status DeleteBlobsInObjectStore(
1402 IndexedDBBackingStore::Transaction* transaction,
1403 int64 database_id,
1404 int64 object_store_id) {
1405 std::string start_key, stop_key;
1406 start_key =
1407 BlobEntryKey::EncodeMinKeyForObjectStore(database_id, object_store_id);
1408 stop_key =
1409 BlobEntryKey::EncodeStopKeyForObjectStore(database_id, object_store_id);
1410 return DeleteBlobsInRange(
1411 transaction, database_id, object_store_id, start_key, stop_key, true);
1414 leveldb::Status IndexedDBBackingStore::DeleteDatabase(
1415 const base::string16& name) {
1416 IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
1417 scoped_ptr<LevelDBDirectTransaction> transaction =
1418 LevelDBDirectTransaction::Create(db_.get());
1420 leveldb::Status s;
1421 s = CleanUpBlobJournal(BlobJournalKey::Encode());
1422 if (!s.ok())
1423 return s;
1425 IndexedDBDatabaseMetadata metadata;
1426 bool success = false;
1427 s = GetIDBDatabaseMetaData(name, &metadata, &success);
1428 if (!s.ok())
1429 return s;
1430 if (!success)
1431 return leveldb::Status::OK();
1433 const std::string start_key = DatabaseMetaDataKey::Encode(
1434 metadata.id, DatabaseMetaDataKey::ORIGIN_NAME);
1435 const std::string stop_key = DatabaseMetaDataKey::Encode(
1436 metadata.id + 1, DatabaseMetaDataKey::ORIGIN_NAME);
1437 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1438 for (s = it->Seek(start_key);
1439 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1440 s = it->Next())
1441 transaction->Remove(it->Key());
1442 if (!s.ok()) {
1443 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1444 return s;
1447 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1448 transaction->Remove(key);
1450 bool need_cleanup = false;
1451 if (active_blob_registry()->MarkDeletedCheckIfUsed(
1452 metadata.id, DatabaseMetaDataKey::kAllBlobsKey)) {
1453 s = MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id);
1454 if (!s.ok())
1455 return s;
1456 } else {
1457 UpdateBlobJournalWithDatabase(transaction.get(), metadata.id);
1458 need_cleanup = true;
1461 s = transaction->Commit();
1462 if (!s.ok()) {
1463 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1464 return s;
1467 if (need_cleanup)
1468 CleanUpBlobJournal(BlobJournalKey::Encode());
1470 db_->Compact(start_key, stop_key);
1471 return s;
1474 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it,
1475 const std::string& stop_key,
1476 int64 object_store_id,
1477 int64 meta_data_type) {
1478 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
1479 return false;
1481 StringPiece slice(it->Key());
1482 ObjectStoreMetaDataKey meta_data_key;
1483 bool ok =
1484 ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
1485 DCHECK(ok);
1486 if (meta_data_key.ObjectStoreId() != object_store_id)
1487 return false;
1488 if (meta_data_key.MetaDataType() != meta_data_type)
1489 return false;
1490 return ok;
1493 // TODO(jsbell): This should do some error handling rather than
1494 // plowing ahead when bad data is encountered.
1495 leveldb::Status IndexedDBBackingStore::GetObjectStores(
1496 int64 database_id,
1497 IndexedDBDatabaseMetadata::ObjectStoreMap* object_stores) {
1498 IDB_TRACE("IndexedDBBackingStore::GetObjectStores");
1499 if (!KeyPrefix::IsValidDatabaseId(database_id))
1500 return InvalidDBKeyStatus();
1501 const std::string start_key =
1502 ObjectStoreMetaDataKey::Encode(database_id, 1, 0);
1503 const std::string stop_key =
1504 ObjectStoreMetaDataKey::EncodeMaxKey(database_id);
1506 DCHECK(object_stores->empty());
1508 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1509 leveldb::Status s = it->Seek(start_key);
1510 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
1511 StringPiece slice(it->Key());
1512 ObjectStoreMetaDataKey meta_data_key;
1513 bool ok =
1514 ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
1515 DCHECK(ok);
1516 if (!ok || meta_data_key.MetaDataType() != ObjectStoreMetaDataKey::NAME) {
1517 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1518 // Possible stale metadata, but don't fail the load.
1519 s = it->Next();
1520 if (!s.ok())
1521 break;
1522 continue;
1525 int64 object_store_id = meta_data_key.ObjectStoreId();
1527 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
1528 // simplify.
1529 base::string16 object_store_name;
1531 StringPiece slice(it->Value());
1532 if (!DecodeString(&slice, &object_store_name) || !slice.empty())
1533 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1536 s = it->Next();
1537 if (!s.ok())
1538 break;
1539 if (!CheckObjectStoreAndMetaDataType(it.get(),
1540 stop_key,
1541 object_store_id,
1542 ObjectStoreMetaDataKey::KEY_PATH)) {
1543 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1544 break;
1546 IndexedDBKeyPath key_path;
1548 StringPiece slice(it->Value());
1549 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
1550 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1553 s = it->Next();
1554 if (!s.ok())
1555 break;
1556 if (!CheckObjectStoreAndMetaDataType(
1557 it.get(),
1558 stop_key,
1559 object_store_id,
1560 ObjectStoreMetaDataKey::AUTO_INCREMENT)) {
1561 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1562 break;
1564 bool auto_increment;
1566 StringPiece slice(it->Value());
1567 if (!DecodeBool(&slice, &auto_increment) || !slice.empty())
1568 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1571 s = it->Next(); // Is evictable.
1572 if (!s.ok())
1573 break;
1574 if (!CheckObjectStoreAndMetaDataType(it.get(),
1575 stop_key,
1576 object_store_id,
1577 ObjectStoreMetaDataKey::EVICTABLE)) {
1578 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1579 break;
1582 s = it->Next(); // Last version.
1583 if (!s.ok())
1584 break;
1585 if (!CheckObjectStoreAndMetaDataType(
1586 it.get(),
1587 stop_key,
1588 object_store_id,
1589 ObjectStoreMetaDataKey::LAST_VERSION)) {
1590 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1591 break;
1594 s = it->Next(); // Maximum index id allocated.
1595 if (!s.ok())
1596 break;
1597 if (!CheckObjectStoreAndMetaDataType(
1598 it.get(),
1599 stop_key,
1600 object_store_id,
1601 ObjectStoreMetaDataKey::MAX_INDEX_ID)) {
1602 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1603 break;
1605 int64 max_index_id;
1607 StringPiece slice(it->Value());
1608 if (!DecodeInt(&slice, &max_index_id) || !slice.empty())
1609 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1612 s = it->Next(); // [optional] has key path (is not null)
1613 if (!s.ok())
1614 break;
1615 if (CheckObjectStoreAndMetaDataType(it.get(),
1616 stop_key,
1617 object_store_id,
1618 ObjectStoreMetaDataKey::HAS_KEY_PATH)) {
1619 bool has_key_path;
1621 StringPiece slice(it->Value());
1622 if (!DecodeBool(&slice, &has_key_path))
1623 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1625 // This check accounts for two layers of legacy coding:
1626 // (1) Initially, has_key_path was added to distinguish null vs. string.
1627 // (2) Later, null vs. string vs. array was stored in the key_path itself.
1628 // So this check is only relevant for string-type key_paths.
1629 if (!has_key_path &&
1630 (key_path.type() == blink::WebIDBKeyPathTypeString &&
1631 !key_path.string().empty())) {
1632 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1633 break;
1635 if (!has_key_path)
1636 key_path = IndexedDBKeyPath();
1637 s = it->Next();
1638 if (!s.ok())
1639 break;
1642 int64 key_generator_current_number = -1;
1643 if (CheckObjectStoreAndMetaDataType(
1644 it.get(),
1645 stop_key,
1646 object_store_id,
1647 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER)) {
1648 StringPiece slice(it->Value());
1649 if (!DecodeInt(&slice, &key_generator_current_number) || !slice.empty())
1650 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1652 // TODO(jsbell): Return key_generator_current_number, cache in
1653 // object store, and write lazily to backing store. For now,
1654 // just assert that if it was written it was valid.
1655 DCHECK_GE(key_generator_current_number, kKeyGeneratorInitialNumber);
1656 s = it->Next();
1657 if (!s.ok())
1658 break;
1661 IndexedDBObjectStoreMetadata metadata(object_store_name,
1662 object_store_id,
1663 key_path,
1664 auto_increment,
1665 max_index_id);
1666 s = GetIndexes(database_id, object_store_id, &metadata.indexes);
1667 if (!s.ok())
1668 break;
1669 (*object_stores)[object_store_id] = metadata;
1672 if (!s.ok())
1673 INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES);
1675 return s;
1678 WARN_UNUSED_RESULT static leveldb::Status SetMaxObjectStoreId(
1679 LevelDBTransaction* transaction,
1680 int64 database_id,
1681 int64 object_store_id) {
1682 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
1683 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
1684 int64 max_object_store_id = -1;
1685 leveldb::Status s = GetMaxObjectStoreId(
1686 transaction, max_object_store_id_key, &max_object_store_id);
1687 if (!s.ok()) {
1688 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1689 return s;
1692 if (object_store_id <= max_object_store_id) {
1693 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1694 return InternalInconsistencyStatus();
1696 PutInt(transaction, max_object_store_id_key, object_store_id);
1697 return s;
1700 void IndexedDBBackingStore::Compact() { db_->CompactAll(); }
1702 leveldb::Status IndexedDBBackingStore::CreateObjectStore(
1703 IndexedDBBackingStore::Transaction* transaction,
1704 int64 database_id,
1705 int64 object_store_id,
1706 const base::string16& name,
1707 const IndexedDBKeyPath& key_path,
1708 bool auto_increment) {
1709 IDB_TRACE("IndexedDBBackingStore::CreateObjectStore");
1710 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1711 return InvalidDBKeyStatus();
1712 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1713 leveldb::Status s =
1714 SetMaxObjectStoreId(leveldb_transaction, database_id, object_store_id);
1715 if (!s.ok())
1716 return s;
1718 const std::string name_key = ObjectStoreMetaDataKey::Encode(
1719 database_id, object_store_id, ObjectStoreMetaDataKey::NAME);
1720 const std::string key_path_key = ObjectStoreMetaDataKey::Encode(
1721 database_id, object_store_id, ObjectStoreMetaDataKey::KEY_PATH);
1722 const std::string auto_increment_key = ObjectStoreMetaDataKey::Encode(
1723 database_id, object_store_id, ObjectStoreMetaDataKey::AUTO_INCREMENT);
1724 const std::string evictable_key = ObjectStoreMetaDataKey::Encode(
1725 database_id, object_store_id, ObjectStoreMetaDataKey::EVICTABLE);
1726 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1727 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1728 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
1729 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
1730 const std::string has_key_path_key = ObjectStoreMetaDataKey::Encode(
1731 database_id, object_store_id, ObjectStoreMetaDataKey::HAS_KEY_PATH);
1732 const std::string key_generator_current_number_key =
1733 ObjectStoreMetaDataKey::Encode(
1734 database_id,
1735 object_store_id,
1736 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
1737 const std::string names_key = ObjectStoreNamesKey::Encode(database_id, name);
1739 PutString(leveldb_transaction, name_key, name);
1740 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
1741 PutInt(leveldb_transaction, auto_increment_key, auto_increment);
1742 PutInt(leveldb_transaction, evictable_key, false);
1743 PutInt(leveldb_transaction, last_version_key, 1);
1744 PutInt(leveldb_transaction, max_index_id_key, kMinimumIndexId);
1745 PutBool(leveldb_transaction, has_key_path_key, !key_path.IsNull());
1746 PutInt(leveldb_transaction,
1747 key_generator_current_number_key,
1748 kKeyGeneratorInitialNumber);
1749 PutInt(leveldb_transaction, names_key, object_store_id);
1750 return s;
1753 leveldb::Status IndexedDBBackingStore::DeleteObjectStore(
1754 IndexedDBBackingStore::Transaction* transaction,
1755 int64 database_id,
1756 int64 object_store_id) {
1757 IDB_TRACE("IndexedDBBackingStore::DeleteObjectStore");
1758 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1759 return InvalidDBKeyStatus();
1760 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1762 base::string16 object_store_name;
1763 bool found = false;
1764 leveldb::Status s =
1765 GetString(leveldb_transaction,
1766 ObjectStoreMetaDataKey::Encode(
1767 database_id, object_store_id, ObjectStoreMetaDataKey::NAME),
1768 &object_store_name,
1769 &found);
1770 if (!s.ok()) {
1771 INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1772 return s;
1774 if (!found) {
1775 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1776 return InternalInconsistencyStatus();
1779 s = DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
1780 if (!s.ok()) {
1781 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1782 return s;
1785 s = DeleteRangeBasic(
1786 leveldb_transaction,
1787 ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0),
1788 ObjectStoreMetaDataKey::EncodeMaxKey(database_id, object_store_id),
1789 true);
1791 if (s.ok()) {
1792 leveldb_transaction->Remove(
1793 ObjectStoreNamesKey::Encode(database_id, object_store_name));
1795 s = DeleteRangeBasic(
1796 leveldb_transaction,
1797 IndexFreeListKey::Encode(database_id, object_store_id, 0),
1798 IndexFreeListKey::EncodeMaxKey(database_id, object_store_id),
1799 true);
1802 if (s.ok()) {
1803 s = DeleteRangeBasic(
1804 leveldb_transaction,
1805 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0),
1806 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id),
1807 true);
1810 if (!s.ok()) {
1811 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1812 return s;
1815 return ClearObjectStore(transaction, database_id, object_store_id);
1818 leveldb::Status IndexedDBBackingStore::GetRecord(
1819 IndexedDBBackingStore::Transaction* transaction,
1820 int64 database_id,
1821 int64 object_store_id,
1822 const IndexedDBKey& key,
1823 IndexedDBValue* record) {
1824 IDB_TRACE("IndexedDBBackingStore::GetRecord");
1825 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1826 return InvalidDBKeyStatus();
1827 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1829 const std::string leveldb_key =
1830 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1831 std::string data;
1833 record->clear();
1835 bool found = false;
1836 leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
1837 if (!s.ok()) {
1838 INTERNAL_READ_ERROR(GET_RECORD);
1839 return s;
1841 if (!found)
1842 return s;
1843 if (data.empty()) {
1844 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1845 return leveldb::Status::NotFound("Record contained no data");
1848 int64 version;
1849 StringPiece slice(data);
1850 if (!DecodeVarInt(&slice, &version)) {
1851 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1852 return InternalInconsistencyStatus();
1855 record->bits = slice.as_string();
1856 return transaction->GetBlobInfoForRecord(database_id, leveldb_key, record);
1859 WARN_UNUSED_RESULT static leveldb::Status GetNewVersionNumber(
1860 LevelDBTransaction* transaction,
1861 int64 database_id,
1862 int64 object_store_id,
1863 int64* new_version_number) {
1864 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1865 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1867 *new_version_number = -1;
1868 int64 last_version = -1;
1869 bool found = false;
1870 leveldb::Status s =
1871 GetInt(transaction, last_version_key, &last_version, &found);
1872 if (!s.ok()) {
1873 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER);
1874 return s;
1876 if (!found)
1877 last_version = 0;
1879 DCHECK_GE(last_version, 0);
1881 int64 version = last_version + 1;
1882 PutInt(transaction, last_version_key, version);
1884 // TODO(jsbell): Think about how we want to handle the overflow scenario.
1885 DCHECK(version > last_version);
1887 *new_version_number = version;
1888 return s;
1891 leveldb::Status IndexedDBBackingStore::PutRecord(
1892 IndexedDBBackingStore::Transaction* transaction,
1893 int64 database_id,
1894 int64 object_store_id,
1895 const IndexedDBKey& key,
1896 IndexedDBValue* value,
1897 ScopedVector<storage::BlobDataHandle>* handles,
1898 RecordIdentifier* record_identifier) {
1899 IDB_TRACE("IndexedDBBackingStore::PutRecord");
1900 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1901 return InvalidDBKeyStatus();
1902 DCHECK(key.IsValid());
1904 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1905 int64 version = -1;
1906 leveldb::Status s = GetNewVersionNumber(
1907 leveldb_transaction, database_id, object_store_id, &version);
1908 if (!s.ok())
1909 return s;
1910 DCHECK_GE(version, 0);
1911 const std::string object_store_data_key =
1912 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1914 std::string v;
1915 EncodeVarInt(version, &v);
1916 v.append(value->bits);
1918 leveldb_transaction->Put(object_store_data_key, &v);
1919 s = transaction->PutBlobInfoIfNeeded(database_id,
1920 object_store_id,
1921 object_store_data_key,
1922 &value->blob_info,
1923 handles);
1924 if (!s.ok())
1925 return s;
1926 DCHECK(!handles->size());
1928 const std::string exists_entry_key =
1929 ExistsEntryKey::Encode(database_id, object_store_id, key);
1930 std::string version_encoded;
1931 EncodeInt(version, &version_encoded);
1932 leveldb_transaction->Put(exists_entry_key, &version_encoded);
1934 std::string key_encoded;
1935 EncodeIDBKey(key, &key_encoded);
1936 record_identifier->Reset(key_encoded, version);
1937 return s;
1940 leveldb::Status IndexedDBBackingStore::ClearObjectStore(
1941 IndexedDBBackingStore::Transaction* transaction,
1942 int64 database_id,
1943 int64 object_store_id) {
1944 IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
1945 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1946 return InvalidDBKeyStatus();
1947 const std::string start_key =
1948 KeyPrefix(database_id, object_store_id).Encode();
1949 const std::string stop_key =
1950 KeyPrefix(database_id, object_store_id + 1).Encode();
1952 leveldb::Status s =
1953 DeleteRangeBasic(transaction->transaction(), start_key, stop_key, true);
1954 if (!s.ok()) {
1955 INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE);
1956 return s;
1958 return DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
1961 leveldb::Status IndexedDBBackingStore::DeleteRecord(
1962 IndexedDBBackingStore::Transaction* transaction,
1963 int64 database_id,
1964 int64 object_store_id,
1965 const RecordIdentifier& record_identifier) {
1966 IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
1967 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1968 return InvalidDBKeyStatus();
1969 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1971 const std::string object_store_data_key = ObjectStoreDataKey::Encode(
1972 database_id, object_store_id, record_identifier.primary_key());
1973 leveldb_transaction->Remove(object_store_data_key);
1974 leveldb::Status s = transaction->PutBlobInfoIfNeeded(
1975 database_id, object_store_id, object_store_data_key, NULL, NULL);
1976 if (!s.ok())
1977 return s;
1979 const std::string exists_entry_key = ExistsEntryKey::Encode(
1980 database_id, object_store_id, record_identifier.primary_key());
1981 leveldb_transaction->Remove(exists_entry_key);
1982 return leveldb::Status::OK();
1985 leveldb::Status IndexedDBBackingStore::DeleteRange(
1986 IndexedDBBackingStore::Transaction* transaction,
1987 int64 database_id,
1988 int64 object_store_id,
1989 const IndexedDBKeyRange& key_range) {
1990 leveldb::Status s;
1991 scoped_ptr<IndexedDBBackingStore::Cursor> start_cursor =
1992 OpenObjectStoreCursor(transaction,
1993 database_id,
1994 object_store_id,
1995 key_range,
1996 blink::WebIDBCursorDirectionNext,
1997 &s);
1998 if (!s.ok())
1999 return s;
2000 if (!start_cursor)
2001 return leveldb::Status::OK(); // Empty range == delete success.
2003 scoped_ptr<IndexedDBBackingStore::Cursor> end_cursor =
2004 OpenObjectStoreCursor(transaction,
2005 database_id,
2006 object_store_id,
2007 key_range,
2008 blink::WebIDBCursorDirectionPrev,
2009 &s);
2011 if (!s.ok())
2012 return s;
2013 if (!end_cursor)
2014 return leveldb::Status::OK(); // Empty range == delete success.
2016 BlobEntryKey start_blob_key, end_blob_key;
2018 std::string start_key = ObjectStoreDataKey::Encode(
2019 database_id, object_store_id, start_cursor->key());
2020 base::StringPiece start_key_piece(start_key);
2021 if (!BlobEntryKey::FromObjectStoreDataKey(&start_key_piece, &start_blob_key))
2022 return InternalInconsistencyStatus();
2023 std::string stop_key = ObjectStoreDataKey::Encode(
2024 database_id, object_store_id, end_cursor->key());
2025 base::StringPiece stop_key_piece(stop_key);
2026 if (!BlobEntryKey::FromObjectStoreDataKey(&stop_key_piece, &end_blob_key))
2027 return InternalInconsistencyStatus();
2029 s = DeleteBlobsInRange(transaction,
2030 database_id,
2031 object_store_id,
2032 start_blob_key.Encode(),
2033 end_blob_key.Encode(),
2034 false);
2035 if (!s.ok())
2036 return s;
2037 s = DeleteRangeBasic(transaction->transaction(), start_key, stop_key, false);
2038 if (!s.ok())
2039 return s;
2040 start_key =
2041 ExistsEntryKey::Encode(database_id, object_store_id, start_cursor->key());
2042 stop_key =
2043 ExistsEntryKey::Encode(database_id, object_store_id, end_cursor->key());
2044 return DeleteRangeBasic(
2045 transaction->transaction(), start_key, stop_key, false);
2048 leveldb::Status IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
2049 IndexedDBBackingStore::Transaction* transaction,
2050 int64 database_id,
2051 int64 object_store_id,
2052 int64* key_generator_current_number) {
2053 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2054 return InvalidDBKeyStatus();
2055 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2057 const std::string key_generator_current_number_key =
2058 ObjectStoreMetaDataKey::Encode(
2059 database_id,
2060 object_store_id,
2061 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
2063 *key_generator_current_number = -1;
2064 std::string data;
2066 bool found = false;
2067 leveldb::Status s =
2068 leveldb_transaction->Get(key_generator_current_number_key, &data, &found);
2069 if (!s.ok()) {
2070 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2071 return s;
2073 if (found && !data.empty()) {
2074 StringPiece slice(data);
2075 if (!DecodeInt(&slice, key_generator_current_number) || !slice.empty()) {
2076 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2077 return InternalInconsistencyStatus();
2079 return s;
2082 // Previously, the key generator state was not stored explicitly
2083 // but derived from the maximum numeric key present in existing
2084 // data. This violates the spec as the data may be cleared but the
2085 // key generator state must be preserved.
2086 // TODO(jsbell): Fix this for all stores on database open?
2087 const std::string start_key =
2088 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
2089 const std::string stop_key =
2090 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
2092 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
2093 int64 max_numeric_key = 0;
2095 for (s = it->Seek(start_key);
2096 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
2097 s = it->Next()) {
2098 StringPiece slice(it->Key());
2099 ObjectStoreDataKey data_key;
2100 if (!ObjectStoreDataKey::Decode(&slice, &data_key) || !slice.empty()) {
2101 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2102 return InternalInconsistencyStatus();
2104 scoped_ptr<IndexedDBKey> user_key = data_key.user_key();
2105 if (user_key->type() == blink::WebIDBKeyTypeNumber) {
2106 int64 n = static_cast<int64>(user_key->number());
2107 if (n > max_numeric_key)
2108 max_numeric_key = n;
2112 if (s.ok())
2113 *key_generator_current_number = max_numeric_key + 1;
2114 else
2115 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2117 return s;
2120 leveldb::Status IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
2121 IndexedDBBackingStore::Transaction* transaction,
2122 int64 database_id,
2123 int64 object_store_id,
2124 int64 new_number,
2125 bool check_current) {
2126 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2127 return InvalidDBKeyStatus();
2129 if (check_current) {
2130 int64 current_number;
2131 leveldb::Status s = GetKeyGeneratorCurrentNumber(
2132 transaction, database_id, object_store_id, &current_number);
2133 if (!s.ok())
2134 return s;
2135 if (new_number <= current_number)
2136 return s;
2139 const std::string key_generator_current_number_key =
2140 ObjectStoreMetaDataKey::Encode(
2141 database_id,
2142 object_store_id,
2143 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
2144 PutInt(
2145 transaction->transaction(), key_generator_current_number_key, new_number);
2146 return leveldb::Status::OK();
2149 leveldb::Status IndexedDBBackingStore::KeyExistsInObjectStore(
2150 IndexedDBBackingStore::Transaction* transaction,
2151 int64 database_id,
2152 int64 object_store_id,
2153 const IndexedDBKey& key,
2154 RecordIdentifier* found_record_identifier,
2155 bool* found) {
2156 IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
2157 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2158 return InvalidDBKeyStatus();
2159 *found = false;
2160 const std::string leveldb_key =
2161 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
2162 std::string data;
2164 leveldb::Status s =
2165 transaction->transaction()->Get(leveldb_key, &data, found);
2166 if (!s.ok()) {
2167 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
2168 return s;
2170 if (!*found)
2171 return leveldb::Status::OK();
2172 if (!data.size()) {
2173 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
2174 return InternalInconsistencyStatus();
2177 int64 version;
2178 StringPiece slice(data);
2179 if (!DecodeVarInt(&slice, &version))
2180 return InternalInconsistencyStatus();
2182 std::string encoded_key;
2183 EncodeIDBKey(key, &encoded_key);
2184 found_record_identifier->Reset(encoded_key, version);
2185 return s;
2188 class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
2189 : public IndexedDBBackingStore::Transaction::ChainedBlobWriter {
2190 public:
2191 typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
2192 WriteDescriptorVec;
2193 ChainedBlobWriterImpl(
2194 int64 database_id,
2195 IndexedDBBackingStore* backing_store,
2196 WriteDescriptorVec* blobs,
2197 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback)
2198 : waiting_for_callback_(false),
2199 database_id_(database_id),
2200 backing_store_(backing_store),
2201 callback_(callback),
2202 aborted_(false) {
2203 blobs_.swap(*blobs);
2204 iter_ = blobs_.begin();
2205 backing_store->task_runner()->PostTask(
2206 FROM_HERE, base::Bind(&ChainedBlobWriterImpl::WriteNextFile, this));
2209 void set_delegate(scoped_ptr<FileWriterDelegate> delegate) override {
2210 delegate_.reset(delegate.release());
2213 void ReportWriteCompletion(bool succeeded, int64 bytes_written) override {
2214 DCHECK(waiting_for_callback_);
2215 DCHECK(!succeeded || bytes_written >= 0);
2216 waiting_for_callback_ = false;
2217 if (delegate_.get()) // Only present for Blob, not File.
2218 content::BrowserThread::DeleteSoon(
2219 content::BrowserThread::IO, FROM_HERE, delegate_.release());
2220 if (aborted_) {
2221 self_ref_ = NULL;
2222 return;
2224 if (iter_->size() != -1 && iter_->size() != bytes_written)
2225 succeeded = false;
2226 if (succeeded) {
2227 ++iter_;
2228 WriteNextFile();
2229 } else {
2230 callback_->Run(false);
2234 void Abort() override {
2235 if (!waiting_for_callback_)
2236 return;
2237 self_ref_ = this;
2238 aborted_ = true;
2241 private:
2242 ~ChainedBlobWriterImpl() override {}
2244 void WriteNextFile() {
2245 DCHECK(!waiting_for_callback_);
2246 DCHECK(!aborted_);
2247 if (iter_ == blobs_.end()) {
2248 DCHECK(!self_ref_.get());
2249 callback_->Run(true);
2250 return;
2251 } else {
2252 if (!backing_store_->WriteBlobFile(database_id_, *iter_, this)) {
2253 callback_->Run(false);
2254 return;
2256 waiting_for_callback_ = true;
2260 bool waiting_for_callback_;
2261 scoped_refptr<ChainedBlobWriterImpl> self_ref_;
2262 WriteDescriptorVec blobs_;
2263 WriteDescriptorVec::const_iterator iter_;
2264 int64 database_id_;
2265 IndexedDBBackingStore* backing_store_;
2266 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback_;
2267 scoped_ptr<FileWriterDelegate> delegate_;
2268 bool aborted_;
2270 DISALLOW_COPY_AND_ASSIGN(ChainedBlobWriterImpl);
2273 class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
2274 public base::RefCountedThreadSafe<LocalWriteClosure> {
2275 public:
2276 LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter*
2277 chained_blob_writer,
2278 base::SequencedTaskRunner* task_runner)
2279 : chained_blob_writer_(chained_blob_writer),
2280 task_runner_(task_runner),
2281 bytes_written_(0) {}
2283 void Run(base::File::Error rv,
2284 int64 bytes,
2285 FileWriterDelegate::WriteProgressStatus write_status) {
2286 DCHECK_GE(bytes, 0);
2287 bytes_written_ += bytes;
2288 if (write_status == FileWriterDelegate::SUCCESS_IO_PENDING)
2289 return; // We don't care about progress events.
2290 if (rv == base::File::FILE_OK) {
2291 DCHECK_EQ(write_status, FileWriterDelegate::SUCCESS_COMPLETED);
2292 } else {
2293 DCHECK(write_status == FileWriterDelegate::ERROR_WRITE_STARTED ||
2294 write_status == FileWriterDelegate::ERROR_WRITE_NOT_STARTED);
2296 task_runner_->PostTask(
2297 FROM_HERE,
2298 base::Bind(&IndexedDBBackingStore::Transaction::ChainedBlobWriter::
2299 ReportWriteCompletion,
2300 chained_blob_writer_,
2301 write_status == FileWriterDelegate::SUCCESS_COMPLETED,
2302 bytes_written_));
2305 void writeBlobToFileOnIOThread(const FilePath& file_path,
2306 const GURL& blob_url,
2307 net::URLRequestContext* request_context) {
2308 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
2309 scoped_ptr<storage::FileStreamWriter> writer(
2310 storage::FileStreamWriter::CreateForLocalFile(
2311 task_runner_.get(),
2312 file_path,
2314 storage::FileStreamWriter::CREATE_NEW_FILE));
2315 scoped_ptr<FileWriterDelegate> delegate(
2316 new FileWriterDelegate(writer.Pass(),
2317 FileWriterDelegate::FLUSH_ON_COMPLETION));
2319 DCHECK(blob_url.is_valid());
2320 scoped_ptr<net::URLRequest> blob_request(request_context->CreateRequest(
2321 blob_url, net::DEFAULT_PRIORITY, delegate.get(), NULL));
2323 delegate->Start(blob_request.Pass(),
2324 base::Bind(&LocalWriteClosure::Run, this));
2325 chained_blob_writer_->set_delegate(delegate.Pass());
2328 private:
2329 virtual ~LocalWriteClosure() {
2330 // Make sure the last reference to a ChainedBlobWriter is released (and
2331 // deleted) on the IDB thread since it owns a transaction which has thread
2332 // affinity.
2333 IndexedDBBackingStore::Transaction::ChainedBlobWriter* raw_tmp =
2334 chained_blob_writer_.get();
2335 raw_tmp->AddRef();
2336 chained_blob_writer_ = NULL;
2337 task_runner_->ReleaseSoon(FROM_HERE, raw_tmp);
2339 friend class base::RefCountedThreadSafe<LocalWriteClosure>;
2341 scoped_refptr<IndexedDBBackingStore::Transaction::ChainedBlobWriter>
2342 chained_blob_writer_;
2343 scoped_refptr<base::SequencedTaskRunner> task_runner_;
2344 int64 bytes_written_;
2346 DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure);
2349 bool IndexedDBBackingStore::WriteBlobFile(
2350 int64 database_id,
2351 const Transaction::WriteDescriptor& descriptor,
2352 Transaction::ChainedBlobWriter* chained_blob_writer) {
2354 if (!MakeIDBBlobDirectory(blob_path_, database_id, descriptor.key()))
2355 return false;
2357 FilePath path = GetBlobFileName(database_id, descriptor.key());
2359 if (descriptor.is_file()) {
2360 DCHECK(!descriptor.file_path().empty());
2361 if (!base::CopyFile(descriptor.file_path(), path))
2362 return false;
2364 base::File::Info info;
2365 if (base::GetFileInfo(descriptor.file_path(), &info)) {
2366 if (descriptor.size() != -1) {
2367 if (descriptor.size() != info.size)
2368 return false;
2369 // The round-trip can be lossy; round to nearest millisecond.
2370 int64 delta = (descriptor.last_modified() -
2371 info.last_modified).InMilliseconds();
2372 if (std::abs(delta) > 1)
2373 return false;
2375 if (!base::TouchFile(path, info.last_accessed, info.last_modified)) {
2376 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2378 } else {
2379 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2382 task_runner_->PostTask(
2383 FROM_HERE,
2384 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion,
2385 chained_blob_writer,
2386 true,
2387 info.size));
2388 } else {
2389 DCHECK(descriptor.url().is_valid());
2390 scoped_refptr<LocalWriteClosure> write_closure(
2391 new LocalWriteClosure(chained_blob_writer, task_runner_.get()));
2392 content::BrowserThread::PostTask(
2393 content::BrowserThread::IO,
2394 FROM_HERE,
2395 base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread,
2396 write_closure.get(),
2397 path,
2398 descriptor.url(),
2399 request_context_));
2401 return true;
2404 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
2405 int64 blob_key) {
2406 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2407 bool all_blobs = blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2408 DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2409 scoped_refptr<LevelDBTransaction> transaction =
2410 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2412 std::string live_blob_key = LiveBlobJournalKey::Encode();
2413 BlobJournalType live_blob_journal;
2414 if (!GetBlobJournal(live_blob_key, transaction.get(), &live_blob_journal)
2415 .ok())
2416 return;
2417 DCHECK(live_blob_journal.size());
2419 std::string primary_key = BlobJournalKey::Encode();
2420 BlobJournalType primary_journal;
2421 if (!GetBlobJournal(primary_key, transaction.get(), &primary_journal).ok())
2422 return;
2424 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2425 // remove all entries with database_id from the live_blob journal and add only
2426 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2427 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2428 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2429 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2430 // matching (database_id, blob_key) tuple, we should move it to the primary
2431 // journal.
2432 BlobJournalType new_live_blob_journal;
2433 for (BlobJournalType::iterator journal_iter = live_blob_journal.begin();
2434 journal_iter != live_blob_journal.end();
2435 ++journal_iter) {
2436 int64 current_database_id = journal_iter->first;
2437 int64 current_blob_key = journal_iter->second;
2438 bool current_all_blobs =
2439 current_blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2440 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id) ||
2441 current_all_blobs);
2442 if (current_database_id == database_id &&
2443 (all_blobs || current_all_blobs || blob_key == current_blob_key)) {
2444 if (!all_blobs) {
2445 primary_journal.push_back(
2446 std::make_pair(database_id, current_blob_key));
2447 if (current_all_blobs)
2448 new_live_blob_journal.push_back(*journal_iter);
2449 new_live_blob_journal.insert(new_live_blob_journal.end(),
2450 ++journal_iter,
2451 live_blob_journal.end()); // All the rest.
2452 break;
2454 } else {
2455 new_live_blob_journal.push_back(*journal_iter);
2458 if (all_blobs) {
2459 primary_journal.push_back(
2460 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
2462 UpdatePrimaryJournalWithBlobList(transaction.get(), primary_journal);
2463 UpdateLiveBlobJournalWithBlobList(transaction.get(), new_live_blob_journal);
2464 transaction->Commit();
2465 // We could just do the deletions/cleaning here, but if there are a lot of
2466 // blobs about to be garbage collected, it'd be better to wait and do them all
2467 // at once.
2468 StartJournalCleaningTimer();
2471 // The this reference is a raw pointer that's declared Unretained inside the
2472 // timer code, so this won't confuse IndexedDBFactory's check for
2473 // HasLastBackingStoreReference. It's safe because if the backing store is
2474 // deleted, the timer will automatically be canceled on destruction.
2475 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2476 journal_cleaning_timer_.Start(
2477 FROM_HERE,
2478 base::TimeDelta::FromSeconds(5),
2479 this,
2480 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn);
2483 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
2484 FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, int64 key) {
2485 return GetBlobFileNameForKey(blob_path_, database_id, key);
2488 static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
2489 const std::string& stop_key,
2490 int64 index_id,
2491 unsigned char meta_data_type) {
2492 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
2493 return false;
2495 StringPiece slice(it->Key());
2496 IndexMetaDataKey meta_data_key;
2497 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2498 DCHECK(ok);
2499 if (meta_data_key.IndexId() != index_id)
2500 return false;
2501 if (meta_data_key.meta_data_type() != meta_data_type)
2502 return false;
2503 return true;
2506 // TODO(jsbell): This should do some error handling rather than plowing ahead
2507 // when bad data is encountered.
2508 leveldb::Status IndexedDBBackingStore::GetIndexes(
2509 int64 database_id,
2510 int64 object_store_id,
2511 IndexedDBObjectStoreMetadata::IndexMap* indexes) {
2512 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2513 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2514 return InvalidDBKeyStatus();
2515 const std::string start_key =
2516 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0);
2517 const std::string stop_key =
2518 IndexMetaDataKey::Encode(database_id, object_store_id + 1, 0, 0);
2520 DCHECK(indexes->empty());
2522 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
2523 leveldb::Status s = it->Seek(start_key);
2524 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
2525 StringPiece slice(it->Key());
2526 IndexMetaDataKey meta_data_key;
2527 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2528 DCHECK(ok);
2529 if (meta_data_key.meta_data_type() != IndexMetaDataKey::NAME) {
2530 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2531 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2532 // the load.
2533 s = it->Next();
2534 if (!s.ok())
2535 break;
2536 continue;
2539 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2540 // simplify.
2541 int64 index_id = meta_data_key.IndexId();
2542 base::string16 index_name;
2544 StringPiece slice(it->Value());
2545 if (!DecodeString(&slice, &index_name) || !slice.empty())
2546 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2549 s = it->Next(); // unique flag
2550 if (!s.ok())
2551 break;
2552 if (!CheckIndexAndMetaDataKey(
2553 it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
2554 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2555 break;
2557 bool index_unique;
2559 StringPiece slice(it->Value());
2560 if (!DecodeBool(&slice, &index_unique) || !slice.empty())
2561 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2564 s = it->Next(); // key_path
2565 if (!s.ok())
2566 break;
2567 if (!CheckIndexAndMetaDataKey(
2568 it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
2569 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2570 break;
2572 IndexedDBKeyPath key_path;
2574 StringPiece slice(it->Value());
2575 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
2576 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2579 s = it->Next(); // [optional] multi_entry flag
2580 if (!s.ok())
2581 break;
2582 bool index_multi_entry = false;
2583 if (CheckIndexAndMetaDataKey(
2584 it.get(), stop_key, index_id, IndexMetaDataKey::MULTI_ENTRY)) {
2585 StringPiece slice(it->Value());
2586 if (!DecodeBool(&slice, &index_multi_entry) || !slice.empty())
2587 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2589 s = it->Next();
2590 if (!s.ok())
2591 break;
2594 (*indexes)[index_id] = IndexedDBIndexMetadata(
2595 index_name, index_id, key_path, index_unique, index_multi_entry);
2598 if (!s.ok())
2599 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES);
2601 return s;
2604 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) {
2605 FilePath fileName = GetBlobFileName(database_id, key);
2606 return base::DeleteFile(fileName, false);
2609 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) {
2610 FilePath dirName = GetBlobDirectoryName(blob_path_, database_id);
2611 return base::DeleteFile(dirName, true);
2614 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal(
2615 const std::string& level_db_key) {
2616 scoped_refptr<LevelDBTransaction> journal_transaction =
2617 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2618 BlobJournalType journal;
2619 leveldb::Status s =
2620 GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
2621 if (!s.ok())
2622 return s;
2623 if (!journal.size())
2624 return leveldb::Status::OK();
2625 for (const auto& entry : journal) {
2626 int64 database_id = entry.first;
2627 int64 blob_key = entry.second;
2628 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2629 if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
2630 if (!RemoveBlobDirectory(database_id))
2631 return IOErrorStatus();
2632 } else {
2633 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2634 if (!RemoveBlobFile(database_id, blob_key))
2635 return IOErrorStatus();
2638 ClearBlobJournal(journal_transaction.get(), level_db_key);
2639 return journal_transaction->Commit();
2642 leveldb::Status IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
2643 int64 database_id,
2644 const std::string& object_store_data_key,
2645 IndexedDBValue* value) {
2646 BlobChangeRecord* change_record = NULL;
2647 BlobChangeMap::const_iterator blob_iter =
2648 blob_change_map_.find(object_store_data_key);
2649 if (blob_iter != blob_change_map_.end()) {
2650 change_record = blob_iter->second;
2651 } else {
2652 blob_iter = incognito_blob_map_.find(object_store_data_key);
2653 if (blob_iter != incognito_blob_map_.end())
2654 change_record = blob_iter->second;
2656 if (change_record) {
2657 // Either we haven't written the blob to disk yet or we're in incognito
2658 // mode, so we have to send back the one they sent us. This change record
2659 // includes the original UUID.
2660 value->blob_info = change_record->blob_info();
2661 return leveldb::Status::OK();
2664 BlobEntryKey blob_entry_key;
2665 StringPiece leveldb_key_piece(object_store_data_key);
2666 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
2667 &blob_entry_key)) {
2668 NOTREACHED();
2669 return InternalInconsistencyStatus();
2671 std::string encoded_key = blob_entry_key.Encode();
2672 bool found;
2673 std::string encoded_value;
2674 leveldb::Status s = transaction()->Get(encoded_key, &encoded_value, &found);
2675 if (!s.ok())
2676 return s;
2677 if (found) {
2678 if (!DecodeBlobData(encoded_value, &value->blob_info)) {
2679 INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD);
2680 return InternalInconsistencyStatus();
2682 for (auto& entry : value->blob_info) {
2683 entry.set_file_path(
2684 backing_store_->GetBlobFileName(database_id, entry.key()));
2685 entry.set_mark_used_callback(
2686 backing_store_->active_blob_registry()->GetAddBlobRefCallback(
2687 database_id, entry.key()));
2688 entry.set_release_callback(
2689 backing_store_->active_blob_registry()->GetFinalReleaseCallback(
2690 database_id, entry.key()));
2691 if (entry.is_file()) {
2692 base::File::Info info;
2693 if (base::GetFileInfo(entry.file_path(), &info)) {
2694 // This should always work, but it isn't fatal if it doesn't; it just
2695 // means a potential slow synchronous call from the renderer later.
2696 entry.set_last_modified(info.last_modified);
2697 entry.set_size(info.size);
2702 return leveldb::Status::OK();
2705 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2706 CleanUpBlobJournal(BlobJournalKey::Encode());
2709 WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId(
2710 LevelDBTransaction* transaction,
2711 int64 database_id,
2712 int64 object_store_id,
2713 int64 index_id) {
2714 int64 max_index_id = -1;
2715 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
2716 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
2717 bool found = false;
2718 leveldb::Status s =
2719 GetInt(transaction, max_index_id_key, &max_index_id, &found);
2720 if (!s.ok()) {
2721 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2722 return s;
2724 if (!found)
2725 max_index_id = kMinimumIndexId;
2727 if (index_id <= max_index_id) {
2728 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2729 return InternalInconsistencyStatus();
2732 PutInt(transaction, max_index_id_key, index_id);
2733 return s;
2736 leveldb::Status IndexedDBBackingStore::CreateIndex(
2737 IndexedDBBackingStore::Transaction* transaction,
2738 int64 database_id,
2739 int64 object_store_id,
2740 int64 index_id,
2741 const base::string16& name,
2742 const IndexedDBKeyPath& key_path,
2743 bool is_unique,
2744 bool is_multi_entry) {
2745 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2746 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2747 return InvalidDBKeyStatus();
2748 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2749 leveldb::Status s = SetMaxIndexId(
2750 leveldb_transaction, database_id, object_store_id, index_id);
2752 if (!s.ok())
2753 return s;
2755 const std::string name_key = IndexMetaDataKey::Encode(
2756 database_id, object_store_id, index_id, IndexMetaDataKey::NAME);
2757 const std::string unique_key = IndexMetaDataKey::Encode(
2758 database_id, object_store_id, index_id, IndexMetaDataKey::UNIQUE);
2759 const std::string key_path_key = IndexMetaDataKey::Encode(
2760 database_id, object_store_id, index_id, IndexMetaDataKey::KEY_PATH);
2761 const std::string multi_entry_key = IndexMetaDataKey::Encode(
2762 database_id, object_store_id, index_id, IndexMetaDataKey::MULTI_ENTRY);
2764 PutString(leveldb_transaction, name_key, name);
2765 PutBool(leveldb_transaction, unique_key, is_unique);
2766 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
2767 PutBool(leveldb_transaction, multi_entry_key, is_multi_entry);
2768 return s;
2771 leveldb::Status IndexedDBBackingStore::DeleteIndex(
2772 IndexedDBBackingStore::Transaction* transaction,
2773 int64 database_id,
2774 int64 object_store_id,
2775 int64 index_id) {
2776 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2777 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2778 return InvalidDBKeyStatus();
2779 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2781 const std::string index_meta_data_start =
2782 IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0);
2783 const std::string index_meta_data_end =
2784 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2785 leveldb::Status s = DeleteRangeBasic(
2786 leveldb_transaction, index_meta_data_start, index_meta_data_end, true);
2788 if (s.ok()) {
2789 const std::string index_data_start =
2790 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
2791 const std::string index_data_end =
2792 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2793 s = DeleteRangeBasic(
2794 leveldb_transaction, index_data_start, index_data_end, true);
2797 if (!s.ok())
2798 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX);
2800 return s;
2803 leveldb::Status IndexedDBBackingStore::PutIndexDataForRecord(
2804 IndexedDBBackingStore::Transaction* transaction,
2805 int64 database_id,
2806 int64 object_store_id,
2807 int64 index_id,
2808 const IndexedDBKey& key,
2809 const RecordIdentifier& record_identifier) {
2810 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2811 DCHECK(key.IsValid());
2812 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2813 return InvalidDBKeyStatus();
2815 std::string encoded_key;
2816 EncodeIDBKey(key, &encoded_key);
2818 const std::string index_data_key =
2819 IndexDataKey::Encode(database_id,
2820 object_store_id,
2821 index_id,
2822 encoded_key,
2823 record_identifier.primary_key(),
2826 std::string data;
2827 EncodeVarInt(record_identifier.version(), &data);
2828 data.append(record_identifier.primary_key());
2830 transaction->transaction()->Put(index_data_key, &data);
2831 return leveldb::Status::OK();
2834 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
2835 const std::string& target,
2836 std::string* found_key,
2837 leveldb::Status* s) {
2838 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
2839 *s = it->Seek(target);
2840 if (!s->ok())
2841 return false;
2843 if (!it->IsValid()) {
2844 *s = it->SeekToLast();
2845 if (!s->ok() || !it->IsValid())
2846 return false;
2849 while (CompareIndexKeys(it->Key(), target) > 0) {
2850 *s = it->Prev();
2851 if (!s->ok() || !it->IsValid())
2852 return false;
2855 do {
2856 *found_key = it->Key().as_string();
2858 // There can be several index keys that compare equal. We want the last one.
2859 *s = it->Next();
2860 } while (s->ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
2862 return true;
2865 static leveldb::Status VersionExists(LevelDBTransaction* transaction,
2866 int64 database_id,
2867 int64 object_store_id,
2868 int64 version,
2869 const std::string& encoded_primary_key,
2870 bool* exists) {
2871 const std::string key =
2872 ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
2873 std::string data;
2875 leveldb::Status s = transaction->Get(key, &data, exists);
2876 if (!s.ok()) {
2877 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS);
2878 return s;
2880 if (!*exists)
2881 return s;
2883 StringPiece slice(data);
2884 int64 decoded;
2885 if (!DecodeInt(&slice, &decoded) || !slice.empty())
2886 return InternalInconsistencyStatus();
2887 *exists = (decoded == version);
2888 return s;
2891 leveldb::Status IndexedDBBackingStore::FindKeyInIndex(
2892 IndexedDBBackingStore::Transaction* transaction,
2893 int64 database_id,
2894 int64 object_store_id,
2895 int64 index_id,
2896 const IndexedDBKey& key,
2897 std::string* found_encoded_primary_key,
2898 bool* found) {
2899 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
2900 DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id));
2902 DCHECK(found_encoded_primary_key->empty());
2903 *found = false;
2905 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2906 const std::string leveldb_key =
2907 IndexDataKey::Encode(database_id, object_store_id, index_id, key);
2908 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
2909 leveldb::Status s = it->Seek(leveldb_key);
2910 if (!s.ok()) {
2911 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
2912 return s;
2915 for (;;) {
2916 if (!it->IsValid())
2917 return leveldb::Status::OK();
2918 if (CompareIndexKeys(it->Key(), leveldb_key) > 0)
2919 return leveldb::Status::OK();
2921 StringPiece slice(it->Value());
2923 int64 version;
2924 if (!DecodeVarInt(&slice, &version)) {
2925 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
2926 return InternalInconsistencyStatus();
2928 *found_encoded_primary_key = slice.as_string();
2930 bool exists = false;
2931 s = VersionExists(leveldb_transaction,
2932 database_id,
2933 object_store_id,
2934 version,
2935 *found_encoded_primary_key,
2936 &exists);
2937 if (!s.ok())
2938 return s;
2939 if (!exists) {
2940 // Delete stale index data entry and continue.
2941 leveldb_transaction->Remove(it->Key());
2942 s = it->Next();
2943 continue;
2945 *found = true;
2946 return s;
2950 leveldb::Status IndexedDBBackingStore::GetPrimaryKeyViaIndex(
2951 IndexedDBBackingStore::Transaction* transaction,
2952 int64 database_id,
2953 int64 object_store_id,
2954 int64 index_id,
2955 const IndexedDBKey& key,
2956 scoped_ptr<IndexedDBKey>* primary_key) {
2957 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
2958 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2959 return InvalidDBKeyStatus();
2961 bool found = false;
2962 std::string found_encoded_primary_key;
2963 leveldb::Status s = FindKeyInIndex(transaction,
2964 database_id,
2965 object_store_id,
2966 index_id,
2967 key,
2968 &found_encoded_primary_key,
2969 &found);
2970 if (!s.ok()) {
2971 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
2972 return s;
2974 if (!found)
2975 return s;
2976 if (!found_encoded_primary_key.size()) {
2977 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
2978 return InvalidDBKeyStatus();
2981 StringPiece slice(found_encoded_primary_key);
2982 if (DecodeIDBKey(&slice, primary_key) && slice.empty())
2983 return s;
2984 else
2985 return InvalidDBKeyStatus();
2988 leveldb::Status IndexedDBBackingStore::KeyExistsInIndex(
2989 IndexedDBBackingStore::Transaction* transaction,
2990 int64 database_id,
2991 int64 object_store_id,
2992 int64 index_id,
2993 const IndexedDBKey& index_key,
2994 scoped_ptr<IndexedDBKey>* found_primary_key,
2995 bool* exists) {
2996 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
2997 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2998 return InvalidDBKeyStatus();
3000 *exists = false;
3001 std::string found_encoded_primary_key;
3002 leveldb::Status s = FindKeyInIndex(transaction,
3003 database_id,
3004 object_store_id,
3005 index_id,
3006 index_key,
3007 &found_encoded_primary_key,
3008 exists);
3009 if (!s.ok()) {
3010 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
3011 return s;
3013 if (!*exists)
3014 return leveldb::Status::OK();
3015 if (found_encoded_primary_key.empty()) {
3016 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
3017 return InvalidDBKeyStatus();
3020 StringPiece slice(found_encoded_primary_key);
3021 if (DecodeIDBKey(&slice, found_primary_key) && slice.empty())
3022 return s;
3023 else
3024 return InvalidDBKeyStatus();
3027 IndexedDBBackingStore::Cursor::Cursor(
3028 const IndexedDBBackingStore::Cursor* other)
3029 : backing_store_(other->backing_store_),
3030 transaction_(other->transaction_),
3031 database_id_(other->database_id_),
3032 cursor_options_(other->cursor_options_),
3033 current_key_(new IndexedDBKey(*other->current_key_)) {
3034 if (other->iterator_) {
3035 iterator_ = transaction_->transaction()->CreateIterator();
3037 if (other->iterator_->IsValid()) {
3038 leveldb::Status s = iterator_->Seek(other->iterator_->Key());
3039 // TODO(cmumford): Handle this error (crbug.com/363397)
3040 DCHECK(iterator_->IsValid());
3045 IndexedDBBackingStore::Cursor::Cursor(
3046 scoped_refptr<IndexedDBBackingStore> backing_store,
3047 IndexedDBBackingStore::Transaction* transaction,
3048 int64 database_id,
3049 const CursorOptions& cursor_options)
3050 : backing_store_(backing_store.get()),
3051 transaction_(transaction),
3052 database_id_(database_id),
3053 cursor_options_(cursor_options) {
3055 IndexedDBBackingStore::Cursor::~Cursor() {}
3057 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) {
3058 iterator_ = transaction_->transaction()->CreateIterator();
3059 if (cursor_options_.forward)
3060 *s = iterator_->Seek(cursor_options_.low_key);
3061 else
3062 *s = iterator_->Seek(cursor_options_.high_key);
3063 if (!s->ok())
3064 return false;
3066 return Continue(0, READY, s);
3069 bool IndexedDBBackingStore::Cursor::Advance(uint32 count, leveldb::Status* s) {
3070 *s = leveldb::Status::OK();
3071 while (count--) {
3072 if (!Continue(s))
3073 return false;
3075 return true;
3078 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
3079 const IndexedDBKey* primary_key,
3080 IteratorState next_state,
3081 leveldb::Status* s) {
3082 DCHECK(!key || key->IsValid());
3083 DCHECK(!primary_key || primary_key->IsValid());
3084 *s = leveldb::Status::OK();
3086 // TODO(alecflett): avoid a copy here?
3087 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3089 // When iterating with PrevNoDuplicate, spec requires that the
3090 // value we yield for each key is the first duplicate in forwards
3091 // order.
3092 IndexedDBKey last_duplicate_key;
3094 bool forward = cursor_options_.forward;
3095 bool first_iteration_forward = forward;
3096 bool flipped = false;
3098 for (;;) {
3099 if (next_state == SEEK) {
3100 // TODO(jsbell): Optimize seeking for reverse cursors as well.
3101 if (first_iteration_forward && key) {
3102 first_iteration_forward = false;
3103 std::string leveldb_key;
3104 if (primary_key) {
3105 leveldb_key = EncodeKey(*key, *primary_key);
3106 } else {
3107 leveldb_key = EncodeKey(*key);
3109 *s = iterator_->Seek(leveldb_key);
3110 } else if (forward) {
3111 *s = iterator_->Next();
3112 } else {
3113 *s = iterator_->Prev();
3115 if (!s->ok())
3116 return false;
3117 } else {
3118 next_state = SEEK; // for subsequent iterations
3121 if (!iterator_->IsValid()) {
3122 if (!forward && last_duplicate_key.IsValid()) {
3123 // We need to walk forward because we hit the end of
3124 // the data.
3125 forward = true;
3126 flipped = true;
3127 continue;
3130 return false;
3133 if (IsPastBounds()) {
3134 if (!forward && last_duplicate_key.IsValid()) {
3135 // We need to walk forward because now we're beyond the
3136 // bounds defined by the cursor.
3137 forward = true;
3138 flipped = true;
3139 continue;
3142 return false;
3145 if (!HaveEnteredRange())
3146 continue;
3148 // The row may not load because there's a stale entry in the
3149 // index. This is not fatal.
3150 if (!LoadCurrentRow())
3151 continue;
3153 if (key) {
3154 if (forward) {
3155 if (primary_key && current_key_->Equals(*key) &&
3156 this->primary_key().IsLessThan(*primary_key))
3157 continue;
3158 if (!flipped && current_key_->IsLessThan(*key))
3159 continue;
3160 } else {
3161 if (primary_key && key->Equals(*current_key_) &&
3162 primary_key->IsLessThan(this->primary_key()))
3163 continue;
3164 if (key->IsLessThan(*current_key_))
3165 continue;
3169 if (cursor_options_.unique) {
3170 if (previous_key.IsValid() && current_key_->Equals(previous_key)) {
3171 // We should never be able to walk forward all the way
3172 // to the previous key.
3173 DCHECK(!last_duplicate_key.IsValid());
3174 continue;
3177 if (!forward) {
3178 if (!last_duplicate_key.IsValid()) {
3179 last_duplicate_key = *current_key_;
3180 continue;
3183 // We need to walk forward because we hit the boundary
3184 // between key ranges.
3185 if (!last_duplicate_key.Equals(*current_key_)) {
3186 forward = true;
3187 flipped = true;
3188 continue;
3191 continue;
3194 break;
3197 DCHECK(!last_duplicate_key.IsValid() ||
3198 (forward && last_duplicate_key.Equals(*current_key_)));
3199 return true;
3202 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3203 if (cursor_options_.forward) {
3204 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3205 if (cursor_options_.low_open) {
3206 return compare > 0;
3208 return compare >= 0;
3210 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3211 if (cursor_options_.high_open) {
3212 return compare < 0;
3214 return compare <= 0;
3217 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
3218 if (cursor_options_.forward) {
3219 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3220 if (cursor_options_.high_open) {
3221 return compare >= 0;
3223 return compare > 0;
3225 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3226 if (cursor_options_.low_open) {
3227 return compare <= 0;
3229 return compare < 0;
3232 const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const {
3233 return *current_key_;
3236 const IndexedDBBackingStore::RecordIdentifier&
3237 IndexedDBBackingStore::Cursor::record_identifier() const {
3238 return record_identifier_;
3241 class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3242 public:
3243 ObjectStoreKeyCursorImpl(
3244 scoped_refptr<IndexedDBBackingStore> backing_store,
3245 IndexedDBBackingStore::Transaction* transaction,
3246 int64 database_id,
3247 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3248 : IndexedDBBackingStore::Cursor(backing_store,
3249 transaction,
3250 database_id,
3251 cursor_options) {}
3253 Cursor* Clone() override { return new ObjectStoreKeyCursorImpl(this); }
3255 // IndexedDBBackingStore::Cursor
3256 IndexedDBValue* value() override {
3257 NOTREACHED();
3258 return NULL;
3260 bool LoadCurrentRow() override;
3262 protected:
3263 std::string EncodeKey(const IndexedDBKey& key) override {
3264 return ObjectStoreDataKey::Encode(
3265 cursor_options_.database_id, cursor_options_.object_store_id, key);
3267 std::string EncodeKey(const IndexedDBKey& key,
3268 const IndexedDBKey& primary_key) override {
3269 NOTREACHED();
3270 return std::string();
3273 private:
3274 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other)
3275 : IndexedDBBackingStore::Cursor(other) {}
3277 DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl);
3280 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
3281 StringPiece slice(iterator_->Key());
3282 ObjectStoreDataKey object_store_data_key;
3283 if (!ObjectStoreDataKey::Decode(&slice, &object_store_data_key)) {
3284 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3285 return false;
3288 current_key_ = object_store_data_key.user_key();
3290 int64 version;
3291 slice = StringPiece(iterator_->Value());
3292 if (!DecodeVarInt(&slice, &version)) {
3293 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3294 return false;
3297 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3298 std::string encoded_key;
3299 EncodeIDBKey(*current_key_, &encoded_key);
3300 record_identifier_.Reset(encoded_key, version);
3302 return true;
3305 class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
3306 public:
3307 ObjectStoreCursorImpl(
3308 scoped_refptr<IndexedDBBackingStore> backing_store,
3309 IndexedDBBackingStore::Transaction* transaction,
3310 int64 database_id,
3311 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3312 : IndexedDBBackingStore::Cursor(backing_store,
3313 transaction,
3314 database_id,
3315 cursor_options) {}
3317 Cursor* Clone() override { return new ObjectStoreCursorImpl(this); }
3319 // IndexedDBBackingStore::Cursor
3320 IndexedDBValue* value() override { return &current_value_; }
3321 bool LoadCurrentRow() override;
3323 protected:
3324 std::string EncodeKey(const IndexedDBKey& key) override {
3325 return ObjectStoreDataKey::Encode(
3326 cursor_options_.database_id, cursor_options_.object_store_id, key);
3328 std::string EncodeKey(const IndexedDBKey& key,
3329 const IndexedDBKey& primary_key) override {
3330 NOTREACHED();
3331 return std::string();
3334 private:
3335 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other)
3336 : IndexedDBBackingStore::Cursor(other),
3337 current_value_(other->current_value_) {}
3339 IndexedDBValue current_value_;
3341 DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl);
3344 bool ObjectStoreCursorImpl::LoadCurrentRow() {
3345 StringPiece key_slice(iterator_->Key());
3346 ObjectStoreDataKey object_store_data_key;
3347 if (!ObjectStoreDataKey::Decode(&key_slice, &object_store_data_key)) {
3348 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3349 return false;
3352 current_key_ = object_store_data_key.user_key();
3354 int64 version;
3355 StringPiece value_slice = StringPiece(iterator_->Value());
3356 if (!DecodeVarInt(&value_slice, &version)) {
3357 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3358 return false;
3361 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3362 std::string encoded_key;
3363 EncodeIDBKey(*current_key_, &encoded_key);
3364 record_identifier_.Reset(encoded_key, version);
3366 if (!transaction_->GetBlobInfoForRecord(database_id_,
3367 iterator_->Key().as_string(),
3368 &current_value_).ok()) {
3369 return false;
3371 current_value_.bits = value_slice.as_string();
3372 return true;
3375 class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3376 public:
3377 IndexKeyCursorImpl(
3378 scoped_refptr<IndexedDBBackingStore> backing_store,
3379 IndexedDBBackingStore::Transaction* transaction,
3380 int64 database_id,
3381 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3382 : IndexedDBBackingStore::Cursor(backing_store,
3383 transaction,
3384 database_id,
3385 cursor_options) {}
3387 Cursor* Clone() override { return new IndexKeyCursorImpl(this); }
3389 // IndexedDBBackingStore::Cursor
3390 IndexedDBValue* value() override {
3391 NOTREACHED();
3392 return NULL;
3394 const IndexedDBKey& primary_key() const override { return *primary_key_; }
3395 const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3396 const override {
3397 NOTREACHED();
3398 return record_identifier_;
3400 bool LoadCurrentRow() override;
3402 protected:
3403 std::string EncodeKey(const IndexedDBKey& key) override {
3404 return IndexDataKey::Encode(cursor_options_.database_id,
3405 cursor_options_.object_store_id,
3406 cursor_options_.index_id,
3407 key);
3409 std::string EncodeKey(const IndexedDBKey& key,
3410 const IndexedDBKey& primary_key) override {
3411 return IndexDataKey::Encode(cursor_options_.database_id,
3412 cursor_options_.object_store_id,
3413 cursor_options_.index_id,
3414 key,
3415 primary_key);
3418 private:
3419 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl* other)
3420 : IndexedDBBackingStore::Cursor(other),
3421 primary_key_(new IndexedDBKey(*other->primary_key_)) {}
3423 scoped_ptr<IndexedDBKey> primary_key_;
3425 DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl);
3428 bool IndexKeyCursorImpl::LoadCurrentRow() {
3429 StringPiece slice(iterator_->Key());
3430 IndexDataKey index_data_key;
3431 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3432 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3433 return false;
3436 current_key_ = index_data_key.user_key();
3437 DCHECK(current_key_);
3439 slice = StringPiece(iterator_->Value());
3440 int64 index_data_version;
3441 if (!DecodeVarInt(&slice, &index_data_version)) {
3442 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3443 return false;
3446 if (!DecodeIDBKey(&slice, &primary_key_) || !slice.empty()) {
3447 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3448 return false;
3451 std::string primary_leveldb_key =
3452 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3453 index_data_key.ObjectStoreId(),
3454 *primary_key_);
3456 std::string result;
3457 bool found = false;
3458 leveldb::Status s =
3459 transaction_->transaction()->Get(primary_leveldb_key, &result, &found);
3460 if (!s.ok()) {
3461 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3462 return false;
3464 if (!found) {
3465 transaction_->transaction()->Remove(iterator_->Key());
3466 return false;
3468 if (!result.size()) {
3469 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3470 return false;
3473 int64 object_store_data_version;
3474 slice = StringPiece(result);
3475 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3476 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3477 return false;
3480 if (object_store_data_version != index_data_version) {
3481 transaction_->transaction()->Remove(iterator_->Key());
3482 return false;
3485 return true;
3488 class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
3489 public:
3490 IndexCursorImpl(
3491 scoped_refptr<IndexedDBBackingStore> backing_store,
3492 IndexedDBBackingStore::Transaction* transaction,
3493 int64 database_id,
3494 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3495 : IndexedDBBackingStore::Cursor(backing_store,
3496 transaction,
3497 database_id,
3498 cursor_options) {}
3500 Cursor* Clone() override { return new IndexCursorImpl(this); }
3502 // IndexedDBBackingStore::Cursor
3503 IndexedDBValue* value() override { return &current_value_; }
3504 const IndexedDBKey& primary_key() const override { return *primary_key_; }
3505 const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3506 const override {
3507 NOTREACHED();
3508 return record_identifier_;
3510 bool LoadCurrentRow() override;
3512 protected:
3513 std::string EncodeKey(const IndexedDBKey& key) override {
3514 return IndexDataKey::Encode(cursor_options_.database_id,
3515 cursor_options_.object_store_id,
3516 cursor_options_.index_id,
3517 key);
3519 std::string EncodeKey(const IndexedDBKey& key,
3520 const IndexedDBKey& primary_key) override {
3521 return IndexDataKey::Encode(cursor_options_.database_id,
3522 cursor_options_.object_store_id,
3523 cursor_options_.index_id,
3524 key,
3525 primary_key);
3528 private:
3529 explicit IndexCursorImpl(const IndexCursorImpl* other)
3530 : IndexedDBBackingStore::Cursor(other),
3531 primary_key_(new IndexedDBKey(*other->primary_key_)),
3532 current_value_(other->current_value_),
3533 primary_leveldb_key_(other->primary_leveldb_key_) {}
3535 scoped_ptr<IndexedDBKey> primary_key_;
3536 IndexedDBValue current_value_;
3537 std::string primary_leveldb_key_;
3539 DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl);
3542 bool IndexCursorImpl::LoadCurrentRow() {
3543 StringPiece slice(iterator_->Key());
3544 IndexDataKey index_data_key;
3545 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3546 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3547 return false;
3550 current_key_ = index_data_key.user_key();
3551 DCHECK(current_key_);
3553 slice = StringPiece(iterator_->Value());
3554 int64 index_data_version;
3555 if (!DecodeVarInt(&slice, &index_data_version)) {
3556 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3557 return false;
3559 if (!DecodeIDBKey(&slice, &primary_key_)) {
3560 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3561 return false;
3564 DCHECK_EQ(index_data_key.DatabaseId(), database_id_);
3565 primary_leveldb_key_ =
3566 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3567 index_data_key.ObjectStoreId(),
3568 *primary_key_);
3570 std::string result;
3571 bool found = false;
3572 leveldb::Status s =
3573 transaction_->transaction()->Get(primary_leveldb_key_, &result, &found);
3574 if (!s.ok()) {
3575 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3576 return false;
3578 if (!found) {
3579 transaction_->transaction()->Remove(iterator_->Key());
3580 return false;
3582 if (!result.size()) {
3583 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3584 return false;
3587 int64 object_store_data_version;
3588 slice = StringPiece(result);
3589 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3590 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3591 return false;
3594 if (object_store_data_version != index_data_version) {
3595 transaction_->transaction()->Remove(iterator_->Key());
3596 return false;
3599 current_value_.bits = slice.as_string();
3600 return transaction_->GetBlobInfoForRecord(database_id_,
3601 primary_leveldb_key_,
3602 &current_value_).ok();
3605 bool ObjectStoreCursorOptions(
3606 LevelDBTransaction* transaction,
3607 int64 database_id,
3608 int64 object_store_id,
3609 const IndexedDBKeyRange& range,
3610 blink::WebIDBCursorDirection direction,
3611 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3612 cursor_options->database_id = database_id;
3613 cursor_options->object_store_id = object_store_id;
3615 bool lower_bound = range.lower().IsValid();
3616 bool upper_bound = range.upper().IsValid();
3617 cursor_options->forward =
3618 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3619 direction == blink::WebIDBCursorDirectionNext);
3620 cursor_options->unique =
3621 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3622 direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
3624 if (!lower_bound) {
3625 cursor_options->low_key =
3626 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
3627 cursor_options->low_open = true; // Not included.
3628 } else {
3629 cursor_options->low_key =
3630 ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower());
3631 cursor_options->low_open = range.lowerOpen();
3634 leveldb::Status s;
3636 if (!upper_bound) {
3637 cursor_options->high_key =
3638 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
3640 if (cursor_options->forward) {
3641 cursor_options->high_open = true; // Not included.
3642 } else {
3643 // We need a key that exists.
3644 // TODO(cmumford): Handle this error (crbug.com/363397)
3645 if (!FindGreatestKeyLessThanOrEqual(transaction,
3646 cursor_options->high_key,
3647 &cursor_options->high_key,
3648 &s))
3649 return false;
3650 cursor_options->high_open = false;
3652 } else {
3653 cursor_options->high_key =
3654 ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper());
3655 cursor_options->high_open = range.upperOpen();
3657 if (!cursor_options->forward) {
3658 // For reverse cursors, we need a key that exists.
3659 std::string found_high_key;
3660 // TODO(cmumford): Handle this error (crbug.com/363397)
3661 if (!FindGreatestKeyLessThanOrEqual(
3662 transaction, cursor_options->high_key, &found_high_key, &s))
3663 return false;
3665 // If the target key should not be included, but we end up with a smaller
3666 // key, we should include that.
3667 if (cursor_options->high_open &&
3668 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3669 cursor_options->high_open = false;
3671 cursor_options->high_key = found_high_key;
3675 return true;
3678 bool IndexCursorOptions(
3679 LevelDBTransaction* transaction,
3680 int64 database_id,
3681 int64 object_store_id,
3682 int64 index_id,
3683 const IndexedDBKeyRange& range,
3684 blink::WebIDBCursorDirection direction,
3685 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3686 DCHECK(transaction);
3687 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3688 return false;
3690 cursor_options->database_id = database_id;
3691 cursor_options->object_store_id = object_store_id;
3692 cursor_options->index_id = index_id;
3694 bool lower_bound = range.lower().IsValid();
3695 bool upper_bound = range.upper().IsValid();
3696 cursor_options->forward =
3697 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3698 direction == blink::WebIDBCursorDirectionNext);
3699 cursor_options->unique =
3700 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3701 direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
3703 if (!lower_bound) {
3704 cursor_options->low_key =
3705 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
3706 cursor_options->low_open = false; // Included.
3707 } else {
3708 cursor_options->low_key = IndexDataKey::Encode(
3709 database_id, object_store_id, index_id, range.lower());
3710 cursor_options->low_open = range.lowerOpen();
3713 leveldb::Status s;
3715 if (!upper_bound) {
3716 cursor_options->high_key =
3717 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
3718 cursor_options->high_open = false; // Included.
3720 if (!cursor_options->forward) { // We need a key that exists.
3721 if (!FindGreatestKeyLessThanOrEqual(transaction,
3722 cursor_options->high_key,
3723 &cursor_options->high_key,
3724 &s))
3725 return false;
3726 cursor_options->high_open = false;
3728 } else {
3729 cursor_options->high_key = IndexDataKey::Encode(
3730 database_id, object_store_id, index_id, range.upper());
3731 cursor_options->high_open = range.upperOpen();
3733 std::string found_high_key;
3734 // Seek to the *last* key in the set of non-unique keys
3735 // TODO(cmumford): Handle this error (crbug.com/363397)
3736 if (!FindGreatestKeyLessThanOrEqual(
3737 transaction, cursor_options->high_key, &found_high_key, &s))
3738 return false;
3740 // If the target key should not be included, but we end up with a smaller
3741 // key, we should include that.
3742 if (cursor_options->high_open &&
3743 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3744 cursor_options->high_open = false;
3746 cursor_options->high_key = found_high_key;
3749 return true;
3752 scoped_ptr<IndexedDBBackingStore::Cursor>
3753 IndexedDBBackingStore::OpenObjectStoreCursor(
3754 IndexedDBBackingStore::Transaction* transaction,
3755 int64 database_id,
3756 int64 object_store_id,
3757 const IndexedDBKeyRange& range,
3758 blink::WebIDBCursorDirection direction,
3759 leveldb::Status* s) {
3760 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3761 *s = leveldb::Status::OK();
3762 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3763 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3764 if (!ObjectStoreCursorOptions(leveldb_transaction,
3765 database_id,
3766 object_store_id,
3767 range,
3768 direction,
3769 &cursor_options))
3770 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3771 scoped_ptr<ObjectStoreCursorImpl> cursor(new ObjectStoreCursorImpl(
3772 this, transaction, database_id, cursor_options));
3773 if (!cursor->FirstSeek(s))
3774 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3776 return cursor.Pass();
3779 scoped_ptr<IndexedDBBackingStore::Cursor>
3780 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3781 IndexedDBBackingStore::Transaction* transaction,
3782 int64 database_id,
3783 int64 object_store_id,
3784 const IndexedDBKeyRange& range,
3785 blink::WebIDBCursorDirection direction,
3786 leveldb::Status* s) {
3787 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3788 *s = leveldb::Status::OK();
3789 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3790 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3791 if (!ObjectStoreCursorOptions(leveldb_transaction,
3792 database_id,
3793 object_store_id,
3794 range,
3795 direction,
3796 &cursor_options))
3797 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3798 scoped_ptr<ObjectStoreKeyCursorImpl> cursor(new ObjectStoreKeyCursorImpl(
3799 this, transaction, database_id, cursor_options));
3800 if (!cursor->FirstSeek(s))
3801 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3803 return cursor.Pass();
3806 scoped_ptr<IndexedDBBackingStore::Cursor>
3807 IndexedDBBackingStore::OpenIndexKeyCursor(
3808 IndexedDBBackingStore::Transaction* transaction,
3809 int64 database_id,
3810 int64 object_store_id,
3811 int64 index_id,
3812 const IndexedDBKeyRange& range,
3813 blink::WebIDBCursorDirection direction,
3814 leveldb::Status* s) {
3815 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3816 *s = leveldb::Status::OK();
3817 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3818 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3819 if (!IndexCursorOptions(leveldb_transaction,
3820 database_id,
3821 object_store_id,
3822 index_id,
3823 range,
3824 direction,
3825 &cursor_options))
3826 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3827 scoped_ptr<IndexKeyCursorImpl> cursor(
3828 new IndexKeyCursorImpl(this, transaction, database_id, cursor_options));
3829 if (!cursor->FirstSeek(s))
3830 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3832 return cursor.Pass();
3835 scoped_ptr<IndexedDBBackingStore::Cursor>
3836 IndexedDBBackingStore::OpenIndexCursor(
3837 IndexedDBBackingStore::Transaction* transaction,
3838 int64 database_id,
3839 int64 object_store_id,
3840 int64 index_id,
3841 const IndexedDBKeyRange& range,
3842 blink::WebIDBCursorDirection direction,
3843 leveldb::Status* s) {
3844 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
3845 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3846 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3847 if (!IndexCursorOptions(leveldb_transaction,
3848 database_id,
3849 object_store_id,
3850 index_id,
3851 range,
3852 direction,
3853 &cursor_options))
3854 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3855 scoped_ptr<IndexCursorImpl> cursor(
3856 new IndexCursorImpl(this, transaction, database_id, cursor_options));
3857 if (!cursor->FirstSeek(s))
3858 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3860 return cursor.Pass();
3863 IndexedDBBackingStore::Transaction::Transaction(
3864 IndexedDBBackingStore* backing_store)
3865 : backing_store_(backing_store), database_id_(-1) {
3868 IndexedDBBackingStore::Transaction::~Transaction() {
3869 STLDeleteContainerPairSecondPointers(
3870 blob_change_map_.begin(), blob_change_map_.end());
3871 STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
3872 incognito_blob_map_.end());
3875 void IndexedDBBackingStore::Transaction::Begin() {
3876 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
3877 DCHECK(!transaction_.get());
3878 transaction_ = IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3879 backing_store_->db_.get());
3881 // If incognito, this snapshots blobs just as the above transaction_
3882 // constructor snapshots the leveldb.
3883 for (const auto& iter : backing_store_->incognito_blob_map_)
3884 incognito_blob_map_[iter.first] = iter.second->Clone().release();
3887 static GURL getURLFromUUID(const string& uuid) {
3888 return GURL("blob:uuid/" + uuid);
3891 leveldb::Status IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
3892 BlobEntryKeyValuePairVec* new_blob_entries,
3893 WriteDescriptorVec* new_files_to_write) {
3894 if (backing_store_->is_incognito())
3895 return leveldb::Status::OK();
3897 new_blob_entries->clear();
3898 new_files_to_write->clear();
3899 if (!blob_change_map_.empty()) {
3900 // Create LevelDBTransaction for the name generator seed and add-journal.
3901 scoped_refptr<LevelDBTransaction> pre_transaction =
3902 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3903 backing_store_->db_.get());
3904 BlobJournalType journal;
3905 for (auto& iter : blob_change_map_) {
3906 std::vector<IndexedDBBlobInfo*> new_blob_keys;
3907 for (auto& entry : iter.second->mutable_blob_info()) {
3908 int64 next_blob_key = -1;
3909 bool result = GetBlobKeyGeneratorCurrentNumber(
3910 pre_transaction.get(), database_id_, &next_blob_key);
3911 if (!result || next_blob_key < 0)
3912 return InternalInconsistencyStatus();
3913 BlobJournalEntryType journal_entry =
3914 std::make_pair(database_id_, next_blob_key);
3915 journal.push_back(journal_entry);
3916 if (entry.is_file()) {
3917 new_files_to_write->push_back(
3918 WriteDescriptor(entry.file_path(),
3919 next_blob_key,
3920 entry.size(),
3921 entry.last_modified()));
3922 } else {
3923 new_files_to_write->push_back(
3924 WriteDescriptor(getURLFromUUID(entry.uuid()),
3925 next_blob_key,
3926 entry.size()));
3928 entry.set_key(next_blob_key);
3929 new_blob_keys.push_back(&entry);
3930 result = UpdateBlobKeyGeneratorCurrentNumber(
3931 pre_transaction.get(), database_id_, next_blob_key + 1);
3932 if (!result)
3933 return InternalInconsistencyStatus();
3935 BlobEntryKey blob_entry_key;
3936 StringPiece key_piece(iter.second->key());
3937 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
3938 NOTREACHED();
3939 return InternalInconsistencyStatus();
3941 new_blob_entries->push_back(
3942 std::make_pair(blob_entry_key, EncodeBlobData(new_blob_keys)));
3944 UpdatePrimaryJournalWithBlobList(pre_transaction.get(), journal);
3945 leveldb::Status s = pre_transaction->Commit();
3946 if (!s.ok())
3947 return InternalInconsistencyStatus();
3949 return leveldb::Status::OK();
3952 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
3953 if (backing_store_->is_incognito())
3954 return true;
3956 // Look up all old files to remove as part of the transaction, store their
3957 // names in blobs_to_remove_, and remove their old blob data entries.
3958 for (const auto& iter : blob_change_map_) {
3959 BlobEntryKey blob_entry_key;
3960 StringPiece key_piece(iter.second->key());
3961 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
3962 NOTREACHED();
3963 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
3964 transaction_ = NULL;
3965 return false;
3967 if (database_id_ < 0)
3968 database_id_ = blob_entry_key.database_id();
3969 else
3970 DCHECK_EQ(database_id_, blob_entry_key.database_id());
3971 std::string blob_entry_key_bytes = blob_entry_key.Encode();
3972 bool found;
3973 std::string blob_entry_value_bytes;
3974 leveldb::Status s = transaction_->Get(
3975 blob_entry_key_bytes, &blob_entry_value_bytes, &found);
3976 if (s.ok() && found) {
3977 std::vector<IndexedDBBlobInfo> blob_info;
3978 if (!DecodeBlobData(blob_entry_value_bytes, &blob_info)) {
3979 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
3980 transaction_ = NULL;
3981 return false;
3983 for (const auto& blob : blob_info) {
3984 blobs_to_remove_.push_back(std::make_pair(database_id_, blob.key()));
3985 transaction_->Remove(blob_entry_key_bytes);
3989 return true;
3992 leveldb::Status IndexedDBBackingStore::Transaction::SortBlobsToRemove() {
3993 IndexedDBActiveBlobRegistry* registry =
3994 backing_store_->active_blob_registry();
3995 BlobJournalType primary_journal, live_blob_journal;
3996 for (const auto& iter : blobs_to_remove_) {
3997 if (registry->MarkDeletedCheckIfUsed(iter.first, iter.second))
3998 live_blob_journal.push_back(iter);
3999 else
4000 primary_journal.push_back(iter);
4002 UpdatePrimaryJournalWithBlobList(transaction_.get(), primary_journal);
4003 leveldb::Status s =
4004 MergeBlobsIntoLiveBlobJournal(transaction_.get(), live_blob_journal);
4005 if (!s.ok())
4006 return s;
4007 // To signal how many blobs need attention right now.
4008 blobs_to_remove_.swap(primary_journal);
4009 return leveldb::Status::OK();
4012 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne(
4013 scoped_refptr<BlobWriteCallback> callback) {
4014 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
4015 DCHECK(transaction_.get());
4016 DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
4018 leveldb::Status s;
4020 s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
4021 if (!s.ok()) {
4022 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
4023 transaction_ = NULL;
4024 return s;
4027 BlobEntryKeyValuePairVec new_blob_entries;
4028 WriteDescriptorVec new_files_to_write;
4029 s = HandleBlobPreTransaction(&new_blob_entries, &new_files_to_write);
4030 if (!s.ok()) {
4031 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4032 transaction_ = NULL;
4033 return s;
4036 DCHECK(!new_files_to_write.size() ||
4037 KeyPrefix::IsValidDatabaseId(database_id_));
4038 if (!CollectBlobFilesToRemove()) {
4039 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4040 transaction_ = NULL;
4041 return InternalInconsistencyStatus();
4044 if (new_files_to_write.size()) {
4045 // This kicks off the writes of the new blobs, if any.
4046 // This call will zero out new_blob_entries and new_files_to_write.
4047 WriteNewBlobs(&new_blob_entries, &new_files_to_write, callback);
4048 // Remove the add journal, if any; once the blobs are written, and we
4049 // commit, this will do the cleanup.
4050 ClearBlobJournal(transaction_.get(), BlobJournalKey::Encode());
4051 } else {
4052 callback->Run(true);
4055 return leveldb::Status::OK();
4058 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
4059 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
4060 leveldb::Status s;
4061 if (blobs_to_remove_.size()) {
4062 s = SortBlobsToRemove();
4063 if (!s.ok()) {
4064 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4065 transaction_ = NULL;
4066 return s;
4070 s = transaction_->Commit();
4071 transaction_ = NULL;
4073 if (s.ok() && backing_store_->is_incognito() && !blob_change_map_.empty()) {
4074 BlobChangeMap& target_map = backing_store_->incognito_blob_map_;
4075 for (auto& iter : blob_change_map_) {
4076 BlobChangeMap::iterator target_record = target_map.find(iter.first);
4077 if (target_record != target_map.end()) {
4078 delete target_record->second;
4079 target_map.erase(target_record);
4081 if (iter.second) {
4082 target_map[iter.first] = iter.second;
4083 iter.second = NULL;
4087 if (!s.ok())
4088 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
4089 else if (blobs_to_remove_.size())
4090 s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
4092 return s;
4096 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
4097 : public IndexedDBBackingStore::BlobWriteCallback {
4098 public:
4099 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction,
4100 scoped_refptr<BlobWriteCallback> callback)
4101 : transaction_(transaction), callback_(callback) {}
4102 void Run(bool succeeded) override {
4103 callback_->Run(succeeded);
4104 if (succeeded) // Else it's already been deleted during rollback.
4105 transaction_->chained_blob_writer_ = NULL;
4108 private:
4109 ~BlobWriteCallbackWrapper() override {}
4110 friend class base::RefCounted<IndexedDBBackingStore::BlobWriteCallback>;
4112 IndexedDBBackingStore::Transaction* transaction_;
4113 scoped_refptr<BlobWriteCallback> callback_;
4115 DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper);
4118 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
4119 BlobEntryKeyValuePairVec* new_blob_entries,
4120 WriteDescriptorVec* new_files_to_write,
4121 scoped_refptr<BlobWriteCallback> callback) {
4122 DCHECK_GT(new_files_to_write->size(), 0UL);
4123 DCHECK_GT(database_id_, 0);
4124 for (auto& blob_entry_iter : *new_blob_entries) {
4125 // Add the new blob-table entry for each blob to the main transaction, or
4126 // remove any entry that may exist if there's no new one.
4127 if (!blob_entry_iter.second.size())
4128 transaction_->Remove(blob_entry_iter.first.Encode());
4129 else
4130 transaction_->Put(blob_entry_iter.first.Encode(),
4131 &blob_entry_iter.second);
4133 // Creating the writer will start it going asynchronously.
4134 chained_blob_writer_ =
4135 new ChainedBlobWriterImpl(database_id_,
4136 backing_store_,
4137 new_files_to_write,
4138 new BlobWriteCallbackWrapper(this, callback));
4141 void IndexedDBBackingStore::Transaction::Rollback() {
4142 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
4143 if (chained_blob_writer_.get()) {
4144 chained_blob_writer_->Abort();
4145 chained_blob_writer_ = NULL;
4147 if (transaction_.get() == NULL)
4148 return;
4149 transaction_->Rollback();
4150 transaction_ = NULL;
4153 IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
4154 const std::string& key,
4155 int64 object_store_id)
4156 : key_(key), object_store_id_(object_store_id) {
4159 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
4162 void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
4163 std::vector<IndexedDBBlobInfo>* blob_info) {
4164 blob_info_.clear();
4165 if (blob_info)
4166 blob_info_.swap(*blob_info);
4169 void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
4170 ScopedVector<storage::BlobDataHandle>* handles) {
4171 handles_.clear();
4172 if (handles)
4173 handles_.swap(*handles);
4176 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord>
4177 IndexedDBBackingStore::BlobChangeRecord::Clone() const {
4178 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord> record(
4179 new BlobChangeRecord(key_, object_store_id_));
4180 record->blob_info_ = blob_info_;
4182 for (const auto* handle : handles_)
4183 record->handles_.push_back(new storage::BlobDataHandle(*handle));
4184 return record.Pass();
4187 leveldb::Status IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
4188 int64 database_id,
4189 int64 object_store_id,
4190 const std::string& object_store_data_key,
4191 std::vector<IndexedDBBlobInfo>* blob_info,
4192 ScopedVector<storage::BlobDataHandle>* handles) {
4193 if (!blob_info || blob_info->empty()) {
4194 blob_change_map_.erase(object_store_data_key);
4195 incognito_blob_map_.erase(object_store_data_key);
4197 BlobEntryKey blob_entry_key;
4198 StringPiece leveldb_key_piece(object_store_data_key);
4199 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
4200 &blob_entry_key)) {
4201 NOTREACHED();
4202 return InternalInconsistencyStatus();
4204 std::string value;
4205 bool found = false;
4206 leveldb::Status s =
4207 transaction()->Get(blob_entry_key.Encode(), &value, &found);
4208 if (!s.ok())
4209 return s;
4210 if (!found)
4211 return leveldb::Status::OK();
4213 PutBlobInfo(
4214 database_id, object_store_id, object_store_data_key, blob_info, handles);
4215 return leveldb::Status::OK();
4218 // This is storing an info, even if empty, even if the previous key had no blob
4219 // info that we know of. It duplicates a bunch of information stored in the
4220 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
4221 // changes to exists or index keys here.
4222 void IndexedDBBackingStore::Transaction::PutBlobInfo(
4223 int64 database_id,
4224 int64 object_store_id,
4225 const std::string& object_store_data_key,
4226 std::vector<IndexedDBBlobInfo>* blob_info,
4227 ScopedVector<storage::BlobDataHandle>* handles) {
4228 DCHECK_GT(object_store_data_key.size(), 0UL);
4229 if (database_id_ < 0)
4230 database_id_ = database_id;
4231 DCHECK_EQ(database_id_, database_id);
4233 BlobChangeMap::iterator it = blob_change_map_.find(object_store_data_key);
4234 BlobChangeRecord* record = NULL;
4235 if (it == blob_change_map_.end()) {
4236 record = new BlobChangeRecord(object_store_data_key, object_store_id);
4237 blob_change_map_[object_store_data_key] = record;
4238 } else {
4239 record = it->second;
4241 DCHECK_EQ(record->object_store_id(), object_store_id);
4242 record->SetBlobInfo(blob_info);
4243 record->SetHandles(handles);
4244 DCHECK(!handles || !handles->size());
4247 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4248 const GURL& url,
4249 int64_t key,
4250 int64_t size)
4251 : is_file_(false), url_(url), key_(key), size_(size) {
4254 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4255 const FilePath& file_path,
4256 int64_t key,
4257 int64_t size,
4258 base::Time last_modified)
4259 : is_file_(true),
4260 file_path_(file_path),
4261 key_(key),
4262 size_(size),
4263 last_modified_(last_modified) {
4266 } // namespace content