Linux: Depend on liberation-fonts package for RPMs.
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_backing_store.cc
blob7eb73aaafc5ce466a6f862a6eabc540686c5cba0
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 <algorithm>
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/format_macros.h"
12 #include "base/json/json_reader.h"
13 #include "base/json/json_writer.h"
14 #include "base/logging.h"
15 #include "base/metrics/histogram.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "content/browser/child_process_security_policy_impl.h"
20 #include "content/browser/indexed_db/indexed_db_blob_info.h"
21 #include "content/browser/indexed_db/indexed_db_class_factory.h"
22 #include "content/browser/indexed_db/indexed_db_database_error.h"
23 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
24 #include "content/browser/indexed_db/indexed_db_metadata.h"
25 #include "content/browser/indexed_db/indexed_db_tracing.h"
26 #include "content/browser/indexed_db/indexed_db_value.h"
27 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
28 #include "content/browser/indexed_db/leveldb/leveldb_database.h"
29 #include "content/browser/indexed_db/leveldb/leveldb_factory.h"
30 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
31 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
32 #include "content/common/indexed_db/indexed_db_key.h"
33 #include "content/common/indexed_db/indexed_db_key_path.h"
34 #include "content/common/indexed_db/indexed_db_key_range.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "net/url_request/url_request_context.h"
37 #include "storage/browser/blob/blob_data_handle.h"
38 #include "storage/browser/fileapi/file_stream_writer.h"
39 #include "storage/browser/fileapi/file_writer_delegate.h"
40 #include "storage/browser/fileapi/local_file_stream_writer.h"
41 #include "storage/common/database/database_identifier.h"
42 #include "storage/common/fileapi/file_system_mount_option.h"
43 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBTypes.h"
44 #include "third_party/WebKit/public/web/WebSerializedScriptValueVersion.h"
45 #include "third_party/leveldatabase/env_chromium.h"
47 using base::FilePath;
48 using base::StringPiece;
49 using std::string;
50 using storage::FileWriterDelegate;
52 namespace content {
54 namespace {
56 FilePath GetBlobDirectoryName(const FilePath& path_base, int64 database_id) {
57 return path_base.AppendASCII(base::StringPrintf("%" PRIx64, database_id));
60 FilePath GetBlobDirectoryNameForKey(const FilePath& path_base,
61 int64 database_id,
62 int64 key) {
63 FilePath path = GetBlobDirectoryName(path_base, database_id);
64 path = path.AppendASCII(base::StringPrintf(
65 "%02x", static_cast<int>(key & 0x000000000000ff00) >> 8));
66 return path;
69 FilePath GetBlobFileNameForKey(const FilePath& path_base,
70 int64 database_id,
71 int64 key) {
72 FilePath path = GetBlobDirectoryNameForKey(path_base, database_id, key);
73 path = path.AppendASCII(base::StringPrintf("%" PRIx64, key));
74 return path;
77 bool MakeIDBBlobDirectory(const FilePath& path_base,
78 int64 database_id,
79 int64 key) {
80 FilePath path = GetBlobDirectoryNameForKey(path_base, database_id, key);
81 return base::CreateDirectory(path);
84 static std::string ComputeOriginIdentifier(const GURL& origin_url) {
85 return storage::GetIdentifierFromOrigin(origin_url) + "@1";
88 static base::FilePath ComputeFileName(const GURL& origin_url) {
89 return base::FilePath()
90 .AppendASCII(storage::GetIdentifierFromOrigin(origin_url))
91 .AddExtension(FILE_PATH_LITERAL(".indexeddb.leveldb"));
94 static base::FilePath ComputeBlobPath(const GURL& origin_url) {
95 return base::FilePath()
96 .AppendASCII(storage::GetIdentifierFromOrigin(origin_url))
97 .AddExtension(FILE_PATH_LITERAL(".indexeddb.blob"));
100 static base::FilePath ComputeCorruptionFileName(const GURL& origin_url) {
101 return ComputeFileName(origin_url)
102 .Append(FILE_PATH_LITERAL("corruption_info.json"));
105 } // namespace
107 static const int64 kKeyGeneratorInitialNumber =
108 1; // From the IndexedDB specification.
110 enum IndexedDBBackingStoreErrorSource {
111 // 0 - 2 are no longer used.
112 FIND_KEY_IN_INDEX = 3,
113 GET_IDBDATABASE_METADATA,
114 GET_INDEXES,
115 GET_KEY_GENERATOR_CURRENT_NUMBER,
116 GET_OBJECT_STORES,
117 GET_RECORD,
118 KEY_EXISTS_IN_OBJECT_STORE,
119 LOAD_CURRENT_ROW,
120 SET_UP_METADATA,
121 GET_PRIMARY_KEY_VIA_INDEX,
122 KEY_EXISTS_IN_INDEX,
123 VERSION_EXISTS,
124 DELETE_OBJECT_STORE,
125 SET_MAX_OBJECT_STORE_ID,
126 SET_MAX_INDEX_ID,
127 GET_NEW_DATABASE_ID,
128 GET_NEW_VERSION_NUMBER,
129 CREATE_IDBDATABASE_METADATA,
130 DELETE_DATABASE,
131 TRANSACTION_COMMIT_METHOD, // TRANSACTION_COMMIT is a WinNT.h macro
132 GET_DATABASE_NAMES,
133 DELETE_INDEX,
134 CLEAR_OBJECT_STORE,
135 READ_BLOB_JOURNAL,
136 DECODE_BLOB_JOURNAL,
137 GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER,
138 GET_BLOB_INFO_FOR_RECORD,
139 INTERNAL_ERROR_MAX,
142 static void RecordInternalError(const char* type,
143 IndexedDBBackingStoreErrorSource location) {
144 std::string name;
145 name.append("WebCore.IndexedDB.BackingStore.").append(type).append("Error");
146 base::Histogram::FactoryGet(name,
148 INTERNAL_ERROR_MAX,
149 INTERNAL_ERROR_MAX + 1,
150 base::HistogramBase::kUmaTargetedHistogramFlag)
151 ->Add(location);
154 // Use to signal conditions caused by data corruption.
155 // A macro is used instead of an inline function so that the assert and log
156 // report the line number.
157 #define REPORT_ERROR(type, location) \
158 do { \
159 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
160 RecordInternalError(type, location); \
161 } while (0)
163 #define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location)
164 #define INTERNAL_CONSISTENCY_ERROR(location) \
165 REPORT_ERROR("Consistency", location)
166 #define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
168 // Use to signal conditions that usually indicate developer error, but
169 // could be caused by data corruption. A macro is used instead of an
170 // inline function so that the assert and log report the line number.
171 // TODO(cmumford): Improve test coverage so that all error conditions are
172 // "tested" and then delete this macro.
173 #define REPORT_ERROR_UNTESTED(type, location) \
174 do { \
175 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
176 NOTREACHED(); \
177 RecordInternalError(type, location); \
178 } while (0)
180 #define INTERNAL_READ_ERROR_UNTESTED(location) \
181 REPORT_ERROR_UNTESTED("Read", location)
182 #define INTERNAL_CONSISTENCY_ERROR_UNTESTED(location) \
183 REPORT_ERROR_UNTESTED("Consistency", location)
184 #define INTERNAL_WRITE_ERROR_UNTESTED(location) \
185 REPORT_ERROR_UNTESTED("Write", location)
187 static void PutBool(LevelDBTransaction* transaction,
188 const StringPiece& key,
189 bool value) {
190 std::string buffer;
191 EncodeBool(value, &buffer);
192 transaction->Put(key, &buffer);
195 // Was able to use LevelDB to read the data w/o error, but the data read was not
196 // in the expected format.
197 static leveldb::Status InternalInconsistencyStatus() {
198 return leveldb::Status::Corruption("Internal inconsistency");
201 static leveldb::Status InvalidDBKeyStatus() {
202 return leveldb::Status::InvalidArgument("Invalid database key ID");
205 static leveldb::Status IOErrorStatus() {
206 return leveldb::Status::IOError("IO Error");
209 template <typename DBOrTransaction>
210 static leveldb::Status GetInt(DBOrTransaction* db,
211 const StringPiece& key,
212 int64* found_int,
213 bool* found) {
214 std::string result;
215 leveldb::Status s = db->Get(key, &result, found);
216 if (!s.ok())
217 return s;
218 if (!*found)
219 return leveldb::Status::OK();
220 StringPiece slice(result);
221 if (DecodeInt(&slice, found_int) && slice.empty())
222 return s;
223 return InternalInconsistencyStatus();
226 static void PutInt(LevelDBTransaction* transaction,
227 const StringPiece& key,
228 int64 value) {
229 DCHECK_GE(value, 0);
230 std::string buffer;
231 EncodeInt(value, &buffer);
232 transaction->Put(key, &buffer);
235 template <typename DBOrTransaction>
236 WARN_UNUSED_RESULT static leveldb::Status GetVarInt(DBOrTransaction* db,
237 const StringPiece& key,
238 int64* found_int,
239 bool* found) {
240 std::string result;
241 leveldb::Status s = db->Get(key, &result, found);
242 if (!s.ok())
243 return s;
244 if (!*found)
245 return leveldb::Status::OK();
246 StringPiece slice(result);
247 if (DecodeVarInt(&slice, found_int) && slice.empty())
248 return s;
249 return InternalInconsistencyStatus();
252 static void PutVarInt(LevelDBTransaction* transaction,
253 const StringPiece& key,
254 int64 value) {
255 std::string buffer;
256 EncodeVarInt(value, &buffer);
257 transaction->Put(key, &buffer);
260 template <typename DBOrTransaction>
261 WARN_UNUSED_RESULT static leveldb::Status GetString(
262 DBOrTransaction* db,
263 const StringPiece& key,
264 base::string16* found_string,
265 bool* found) {
266 std::string result;
267 *found = false;
268 leveldb::Status s = db->Get(key, &result, found);
269 if (!s.ok())
270 return s;
271 if (!*found)
272 return leveldb::Status::OK();
273 StringPiece slice(result);
274 if (DecodeString(&slice, found_string) && slice.empty())
275 return s;
276 return InternalInconsistencyStatus();
279 static void PutString(LevelDBTransaction* transaction,
280 const StringPiece& key,
281 const base::string16& value) {
282 std::string buffer;
283 EncodeString(value, &buffer);
284 transaction->Put(key, &buffer);
287 static void PutIDBKeyPath(LevelDBTransaction* transaction,
288 const StringPiece& key,
289 const IndexedDBKeyPath& value) {
290 std::string buffer;
291 EncodeIDBKeyPath(value, &buffer);
292 transaction->Put(key, &buffer);
295 static int CompareKeys(const StringPiece& a, const StringPiece& b) {
296 return Compare(a, b, false /*index_keys*/);
299 static int CompareIndexKeys(const StringPiece& a, const StringPiece& b) {
300 return Compare(a, b, true /*index_keys*/);
303 int IndexedDBBackingStore::Comparator::Compare(const StringPiece& a,
304 const StringPiece& b) const {
305 return content::Compare(a, b, false /*index_keys*/);
308 const char* IndexedDBBackingStore::Comparator::Name() const {
309 return "idb_cmp1";
312 // 0 - Initial version.
313 // 1 - Adds UserIntVersion to DatabaseMetaData.
314 // 2 - Adds DataVersion to to global metadata.
315 // 3 - Adds metadata needed for blob support.
316 static const int64 kLatestKnownSchemaVersion = 3;
317 WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
318 int64 db_schema_version = 0;
319 bool found = false;
320 leveldb::Status s =
321 GetInt(db, SchemaVersionKey::Encode(), &db_schema_version, &found);
322 if (!s.ok())
323 return false;
324 if (!found) {
325 *known = true;
326 return true;
328 if (db_schema_version > kLatestKnownSchemaVersion) {
329 *known = false;
330 return true;
333 const uint32 latest_known_data_version =
334 blink::kSerializedScriptValueVersion;
335 int64 db_data_version = 0;
336 s = GetInt(db, DataVersionKey::Encode(), &db_data_version, &found);
337 if (!s.ok())
338 return false;
339 if (!found) {
340 *known = true;
341 return true;
344 if (db_data_version > latest_known_data_version) {
345 *known = false;
346 return true;
349 *known = true;
350 return true;
353 // TODO(ericu): Move this down into the member section of this file. I'm
354 // leaving it here for this CL as it's easier to see the diffs in place.
355 WARN_UNUSED_RESULT leveldb::Status IndexedDBBackingStore::SetUpMetadata() {
356 const uint32 latest_known_data_version =
357 blink::kSerializedScriptValueVersion;
358 const std::string schema_version_key = SchemaVersionKey::Encode();
359 const std::string data_version_key = DataVersionKey::Encode();
361 scoped_refptr<LevelDBTransaction> transaction =
362 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
364 int64 db_schema_version = 0;
365 int64 db_data_version = 0;
366 bool found = false;
367 leveldb::Status s =
368 GetInt(transaction.get(), schema_version_key, &db_schema_version, &found);
369 if (!s.ok()) {
370 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
371 return s;
373 if (!found) {
374 // Initialize new backing store.
375 db_schema_version = kLatestKnownSchemaVersion;
376 PutInt(transaction.get(), schema_version_key, db_schema_version);
377 db_data_version = latest_known_data_version;
378 PutInt(transaction.get(), data_version_key, db_data_version);
379 // If a blob directory already exists for this database, blow it away. It's
380 // leftover from a partially-purged previous generation of data.
381 if (!base::DeleteFile(blob_path_, true)) {
382 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
383 return IOErrorStatus();
385 } else {
386 // Upgrade old backing store.
387 DCHECK_LE(db_schema_version, kLatestKnownSchemaVersion);
388 if (db_schema_version < 1) {
389 db_schema_version = 1;
390 PutInt(transaction.get(), schema_version_key, db_schema_version);
391 const std::string start_key =
392 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
393 const std::string stop_key =
394 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
395 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
396 for (s = it->Seek(start_key);
397 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
398 s = it->Next()) {
399 int64 database_id = 0;
400 found = false;
401 s = GetInt(transaction.get(), it->Key(), &database_id, &found);
402 if (!s.ok()) {
403 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
404 return s;
406 if (!found) {
407 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
408 return InternalInconsistencyStatus();
410 std::string int_version_key = DatabaseMetaDataKey::Encode(
411 database_id, DatabaseMetaDataKey::USER_INT_VERSION);
412 PutVarInt(transaction.get(),
413 int_version_key,
414 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
417 if (s.ok() && db_schema_version < 2) {
418 db_schema_version = 2;
419 PutInt(transaction.get(), schema_version_key, db_schema_version);
420 db_data_version = blink::kSerializedScriptValueVersion;
421 PutInt(transaction.get(), data_version_key, db_data_version);
423 if (db_schema_version < 3) {
424 db_schema_version = 3;
425 if (!base::DeleteFile(blob_path_, true)) {
426 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
427 return IOErrorStatus();
432 if (!s.ok()) {
433 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
434 return s;
437 // All new values will be written using this serialization version.
438 found = false;
439 s = GetInt(transaction.get(), data_version_key, &db_data_version, &found);
440 if (!s.ok()) {
441 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
442 return s;
444 if (!found) {
445 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
446 return InternalInconsistencyStatus();
448 if (db_data_version < latest_known_data_version) {
449 db_data_version = latest_known_data_version;
450 PutInt(transaction.get(), data_version_key, db_data_version);
453 DCHECK_EQ(db_schema_version, kLatestKnownSchemaVersion);
454 DCHECK_EQ(db_data_version, latest_known_data_version);
456 s = transaction->Commit();
457 if (!s.ok())
458 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
459 return s;
462 template <typename DBOrTransaction>
463 WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
464 DBOrTransaction* db,
465 int64 database_id,
466 int64* max_object_store_id) {
467 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
468 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
469 return GetMaxObjectStoreId(db, max_object_store_id_key, max_object_store_id);
472 template <typename DBOrTransaction>
473 WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
474 DBOrTransaction* db,
475 const std::string& max_object_store_id_key,
476 int64* max_object_store_id) {
477 *max_object_store_id = -1;
478 bool found = false;
479 leveldb::Status s =
480 GetInt(db, max_object_store_id_key, max_object_store_id, &found);
481 if (!s.ok())
482 return s;
483 if (!found)
484 *max_object_store_id = 0;
486 DCHECK_GE(*max_object_store_id, 0);
487 return s;
490 class DefaultLevelDBFactory : public LevelDBFactory {
491 public:
492 DefaultLevelDBFactory() {}
493 leveldb::Status OpenLevelDB(const base::FilePath& file_name,
494 const LevelDBComparator* comparator,
495 scoped_ptr<LevelDBDatabase>* db,
496 bool* is_disk_full) override {
497 return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full);
499 leveldb::Status DestroyLevelDB(const base::FilePath& file_name) override {
500 return LevelDBDatabase::Destroy(file_name);
503 private:
504 DISALLOW_COPY_AND_ASSIGN(DefaultLevelDBFactory);
507 static bool GetBlobKeyGeneratorCurrentNumber(
508 LevelDBTransaction* leveldb_transaction,
509 int64 database_id,
510 int64* blob_key_generator_current_number) {
511 const std::string key_gen_key = DatabaseMetaDataKey::Encode(
512 database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
514 // Default to initial number if not found.
515 int64 cur_number = DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber;
516 std::string data;
518 bool found = false;
519 bool ok = leveldb_transaction->Get(key_gen_key, &data, &found).ok();
520 if (!ok) {
521 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
522 return false;
524 if (found) {
525 StringPiece slice(data);
526 if (!DecodeVarInt(&slice, &cur_number) || !slice.empty() ||
527 !DatabaseMetaDataKey::IsValidBlobKey(cur_number)) {
528 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
529 return false;
532 *blob_key_generator_current_number = cur_number;
533 return true;
536 static bool UpdateBlobKeyGeneratorCurrentNumber(
537 LevelDBTransaction* leveldb_transaction,
538 int64 database_id,
539 int64 blob_key_generator_current_number) {
540 #ifndef NDEBUG
541 int64 old_number;
542 if (!GetBlobKeyGeneratorCurrentNumber(
543 leveldb_transaction, database_id, &old_number))
544 return false;
545 DCHECK_LT(old_number, blob_key_generator_current_number);
546 #endif
547 DCHECK(
548 DatabaseMetaDataKey::IsValidBlobKey(blob_key_generator_current_number));
549 const std::string key = DatabaseMetaDataKey::Encode(
550 database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
552 PutVarInt(leveldb_transaction, key, blob_key_generator_current_number);
553 return true;
556 // TODO(ericu): Error recovery. If we persistently can't read the
557 // blob journal, the safe thing to do is to clear it and leak the blobs,
558 // though that may be costly. Still, database/directory deletion should always
559 // clean things up, and we can write an fsck that will do a full correction if
560 // need be.
562 // Read and decode the specified blob journal via the supplied transaction.
563 // The key must be either the primary journal key or live journal key.
564 template <typename TransactionType>
565 static leveldb::Status GetBlobJournal(const StringPiece& key,
566 TransactionType* transaction,
567 BlobJournalType* journal) {
568 IDB_TRACE("IndexedDBBackingStore::GetBlobJournal");
569 std::string data;
570 bool found = false;
571 leveldb::Status s = transaction->Get(key, &data, &found);
572 if (!s.ok()) {
573 INTERNAL_READ_ERROR(READ_BLOB_JOURNAL);
574 return s;
576 journal->clear();
577 if (!found || data.empty())
578 return leveldb::Status::OK();
579 StringPiece slice(data);
580 if (!DecodeBlobJournal(&slice, journal)) {
581 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DECODE_BLOB_JOURNAL);
582 s = InternalInconsistencyStatus();
584 return s;
587 template <typename TransactionType>
588 static leveldb::Status GetPrimaryBlobJournal(TransactionType* transaction,
589 BlobJournalType* journal) {
590 return GetBlobJournal(BlobJournalKey::Encode(), transaction, journal);
593 template <typename TransactionType>
594 static leveldb::Status GetLiveBlobJournal(TransactionType* transaction,
595 BlobJournalType* journal) {
596 return GetBlobJournal(LiveBlobJournalKey::Encode(), transaction, journal);
599 // Clear the specified blob journal via the supplied transaction.
600 // The key must be either the primary journal key or live journal key.
601 template <typename TransactionType>
602 static void ClearBlobJournal(TransactionType* transaction,
603 const std::string& key) {
604 transaction->Remove(key);
607 // Overwrite the specified blob journal via the supplied transaction.
608 // The key must be either the primary journal key or live journal key.
609 template <typename TransactionType>
610 static void UpdateBlobJournal(TransactionType* transaction,
611 const std::string& key,
612 const BlobJournalType& journal) {
613 std::string data;
614 EncodeBlobJournal(journal, &data);
615 transaction->Put(key, &data);
618 template <typename TransactionType>
619 static void UpdatePrimaryBlobJournal(TransactionType* transaction,
620 const BlobJournalType& journal) {
621 UpdateBlobJournal(transaction, BlobJournalKey::Encode(), journal);
624 template <typename TransactionType>
625 static void UpdateLiveBlobJournal(TransactionType* transaction,
626 const BlobJournalType& journal) {
627 UpdateBlobJournal(transaction, LiveBlobJournalKey::Encode(), journal);
630 // Append blobs to the specified blob journal via the supplied transaction.
631 // The key must be either the primary journal key or live journal key.
632 template <typename TransactionType>
633 static leveldb::Status AppendBlobsToBlobJournal(
634 TransactionType* transaction,
635 const std::string& key,
636 const BlobJournalType& journal) {
637 if (journal.empty())
638 return leveldb::Status::OK();
639 BlobJournalType old_journal;
640 leveldb::Status s = GetBlobJournal(key, transaction, &old_journal);
641 if (!s.ok())
642 return s;
643 old_journal.insert(old_journal.end(), journal.begin(), journal.end());
644 UpdateBlobJournal(transaction, key, old_journal);
645 return leveldb::Status::OK();
648 template <typename TransactionType>
649 static leveldb::Status AppendBlobsToPrimaryBlobJournal(
650 TransactionType* transaction,
651 const BlobJournalType& journal) {
652 return AppendBlobsToBlobJournal(transaction, BlobJournalKey::Encode(),
653 journal);
656 template <typename TransactionType>
657 static leveldb::Status AppendBlobsToLiveBlobJournal(
658 TransactionType* transaction,
659 const BlobJournalType& journal) {
660 return AppendBlobsToBlobJournal(transaction, LiveBlobJournalKey::Encode(),
661 journal);
664 // Append a database to the specified blob journal via the supplied transaction.
665 // The key must be either the primary journal key or live journal key.
666 static leveldb::Status MergeDatabaseIntoBlobJournal(
667 LevelDBDirectTransaction* transaction,
668 const std::string& key,
669 int64 database_id) {
670 IDB_TRACE("IndexedDBBackingStore::MergeDatabaseIntoBlobJournal");
671 BlobJournalType journal;
672 leveldb::Status s = GetBlobJournal(key, transaction, &journal);
673 if (!s.ok())
674 return s;
675 journal.push_back(
676 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
677 UpdateBlobJournal(transaction, key, journal);
678 return leveldb::Status::OK();
681 static leveldb::Status MergeDatabaseIntoPrimaryBlobJournal(
682 LevelDBDirectTransaction* leveldb_transaction,
683 int64 database_id) {
684 return MergeDatabaseIntoBlobJournal(leveldb_transaction,
685 BlobJournalKey::Encode(), database_id);
688 static leveldb::Status MergeDatabaseIntoLiveBlobJournal(
689 LevelDBDirectTransaction* leveldb_transaction,
690 int64 database_id) {
691 return MergeDatabaseIntoBlobJournal(
692 leveldb_transaction, LiveBlobJournalKey::Encode(), database_id);
695 // Blob Data is encoded as a series of:
696 // { is_file [bool], key [int64 as varInt],
697 // type [string-with-length, may be empty],
698 // (for Blobs only) size [int64 as varInt]
699 // (for Files only) fileName [string-with-length]
700 // }
701 // There is no length field; just read until you run out of data.
702 static std::string EncodeBlobData(
703 const std::vector<IndexedDBBlobInfo*>& blob_info) {
704 std::string ret;
705 for (const auto* info : blob_info) {
706 EncodeBool(info->is_file(), &ret);
707 EncodeVarInt(info->key(), &ret);
708 EncodeStringWithLength(info->type(), &ret);
709 if (info->is_file())
710 EncodeStringWithLength(info->file_name(), &ret);
711 else
712 EncodeVarInt(info->size(), &ret);
714 return ret;
717 static bool DecodeBlobData(const std::string& data,
718 std::vector<IndexedDBBlobInfo>* output) {
719 std::vector<IndexedDBBlobInfo> ret;
720 output->clear();
721 StringPiece slice(data);
722 while (!slice.empty()) {
723 bool is_file;
724 int64 key;
725 base::string16 type;
726 int64 size;
727 base::string16 file_name;
729 if (!DecodeBool(&slice, &is_file))
730 return false;
731 if (!DecodeVarInt(&slice, &key) ||
732 !DatabaseMetaDataKey::IsValidBlobKey(key))
733 return false;
734 if (!DecodeStringWithLength(&slice, &type))
735 return false;
736 if (is_file) {
737 if (!DecodeStringWithLength(&slice, &file_name))
738 return false;
739 ret.push_back(IndexedDBBlobInfo(key, type, file_name));
740 } else {
741 if (!DecodeVarInt(&slice, &size) || size < 0)
742 return false;
743 ret.push_back(IndexedDBBlobInfo(type, static_cast<uint64>(size), key));
746 output->swap(ret);
748 return true;
751 IndexedDBBackingStore::IndexedDBBackingStore(
752 IndexedDBFactory* indexed_db_factory,
753 const GURL& origin_url,
754 const base::FilePath& blob_path,
755 net::URLRequestContext* request_context,
756 scoped_ptr<LevelDBDatabase> db,
757 scoped_ptr<LevelDBComparator> comparator,
758 base::SequencedTaskRunner* task_runner)
759 : indexed_db_factory_(indexed_db_factory),
760 origin_url_(origin_url),
761 blob_path_(blob_path),
762 origin_identifier_(ComputeOriginIdentifier(origin_url)),
763 request_context_(request_context),
764 task_runner_(task_runner),
765 db_(db.Pass()),
766 comparator_(comparator.Pass()),
767 active_blob_registry_(this),
768 committing_transaction_count_(0) {
771 IndexedDBBackingStore::~IndexedDBBackingStore() {
772 if (!blob_path_.empty() && !child_process_ids_granted_.empty()) {
773 ChildProcessSecurityPolicyImpl* policy =
774 ChildProcessSecurityPolicyImpl::GetInstance();
775 for (const auto& pid : child_process_ids_granted_)
776 policy->RevokeAllPermissionsForFile(pid, blob_path_);
778 STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
779 incognito_blob_map_.end());
780 // db_'s destructor uses comparator_. The order of destruction is important.
781 db_.reset();
782 comparator_.reset();
785 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
786 const std::string& primary_key,
787 int64 version)
788 : primary_key_(primary_key), version_(version) {
789 DCHECK(!primary_key.empty());
791 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
792 : primary_key_(), version_(-1) {}
793 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
795 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
796 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
798 // Values match entries in tools/metrics/histograms/histograms.xml
799 enum IndexedDBBackingStoreOpenResult {
800 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS,
801 INDEXED_DB_BACKING_STORE_OPEN_SUCCESS,
802 INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
803 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
804 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
805 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
806 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
807 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
808 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR_DEPRECATED,
809 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
810 INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
811 INDEXED_DB_BACKING_STORE_OPEN_DISK_FULL_DEPRECATED,
812 INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
813 INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
814 INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
815 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR,
816 INDEXED_DB_BACKING_STORE_OPEN_MAX,
819 // static
820 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
821 IndexedDBFactory* indexed_db_factory,
822 const GURL& origin_url,
823 const base::FilePath& path_base,
824 net::URLRequestContext* request_context,
825 blink::WebIDBDataLoss* data_loss,
826 std::string* data_loss_message,
827 bool* disk_full,
828 base::SequencedTaskRunner* task_runner,
829 bool clean_journal,
830 leveldb::Status* status) {
831 *data_loss = blink::WebIDBDataLossNone;
832 DefaultLevelDBFactory leveldb_factory;
833 return IndexedDBBackingStore::Open(indexed_db_factory,
834 origin_url,
835 path_base,
836 request_context,
837 data_loss,
838 data_loss_message,
839 disk_full,
840 &leveldb_factory,
841 task_runner,
842 clean_journal,
843 status);
846 static std::string OriginToCustomHistogramSuffix(const GURL& origin_url) {
847 if (origin_url.host() == "docs.google.com")
848 return ".Docs";
849 return std::string();
852 static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result,
853 const GURL& origin_url) {
854 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.BackingStore.OpenStatus",
855 result,
856 INDEXED_DB_BACKING_STORE_OPEN_MAX);
857 const std::string suffix = OriginToCustomHistogramSuffix(origin_url);
858 // Data from the WebCore.IndexedDB.BackingStore.OpenStatus histogram is used
859 // to generate a graph. So as not to alter the meaning of that graph,
860 // continue to collect all stats there (above) but also now collect docs stats
861 // separately (below).
862 if (!suffix.empty()) {
863 base::LinearHistogram::FactoryGet(
864 "WebCore.IndexedDB.BackingStore.OpenStatus" + suffix,
866 INDEXED_DB_BACKING_STORE_OPEN_MAX,
867 INDEXED_DB_BACKING_STORE_OPEN_MAX + 1,
868 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(result);
872 static bool IsPathTooLong(const base::FilePath& leveldb_dir) {
873 int limit = base::GetMaximumPathComponentLength(leveldb_dir.DirName());
874 if (limit == -1) {
875 DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
876 // In limited testing, ChromeOS returns 143, other OSes 255.
877 #if defined(OS_CHROMEOS)
878 limit = 143;
879 #else
880 limit = 255;
881 #endif
883 size_t component_length = leveldb_dir.BaseName().value().length();
884 if (component_length > static_cast<uint32_t>(limit)) {
885 DLOG(WARNING) << "Path component length (" << component_length
886 << ") exceeds maximum (" << limit
887 << ") allowed by this filesystem.";
888 const int min = 140;
889 const int max = 300;
890 const int num_buckets = 12;
891 UMA_HISTOGRAM_CUSTOM_COUNTS(
892 "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
893 component_length,
894 min,
895 max,
896 num_buckets);
897 return true;
899 return false;
902 leveldb::Status IndexedDBBackingStore::DestroyBackingStore(
903 const base::FilePath& path_base,
904 const GURL& origin_url) {
905 const base::FilePath file_path =
906 path_base.Append(ComputeFileName(origin_url));
907 DefaultLevelDBFactory leveldb_factory;
908 return leveldb_factory.DestroyLevelDB(file_path);
911 bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath& path_base,
912 const GURL& origin_url,
913 std::string* message) {
914 const base::FilePath info_path =
915 path_base.Append(ComputeCorruptionFileName(origin_url));
917 if (IsPathTooLong(info_path))
918 return false;
920 const int64 max_json_len = 4096;
921 int64 file_size(0);
922 if (!GetFileSize(info_path, &file_size) || file_size > max_json_len)
923 return false;
924 if (!file_size) {
925 NOTREACHED();
926 return false;
929 base::File file(info_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
930 bool success = false;
931 if (file.IsValid()) {
932 std::vector<char> bytes(file_size);
933 if (file_size == file.Read(0, &bytes[0], file_size)) {
934 std::string input_js(&bytes[0], file_size);
935 base::JSONReader reader;
936 scoped_ptr<base::Value> val(reader.ReadToValue(input_js));
937 if (val && val->GetType() == base::Value::TYPE_DICTIONARY) {
938 base::DictionaryValue* dict_val =
939 static_cast<base::DictionaryValue*>(val.get());
940 success = dict_val->GetString("message", message);
943 file.Close();
946 base::DeleteFile(info_path, false);
948 return success;
951 bool IndexedDBBackingStore::RecordCorruptionInfo(
952 const base::FilePath& path_base,
953 const GURL& origin_url,
954 const std::string& message) {
955 const base::FilePath info_path =
956 path_base.Append(ComputeCorruptionFileName(origin_url));
957 if (IsPathTooLong(info_path))
958 return false;
960 base::DictionaryValue root_dict;
961 root_dict.SetString("message", message);
962 std::string output_js;
963 base::JSONWriter::Write(root_dict, &output_js);
965 base::File file(info_path,
966 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
967 if (!file.IsValid())
968 return false;
969 int written = file.Write(0, output_js.c_str(), output_js.length());
970 return size_t(written) == output_js.length();
973 // static
974 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
975 IndexedDBFactory* indexed_db_factory,
976 const GURL& origin_url,
977 const base::FilePath& path_base,
978 net::URLRequestContext* request_context,
979 blink::WebIDBDataLoss* data_loss,
980 std::string* data_loss_message,
981 bool* is_disk_full,
982 LevelDBFactory* leveldb_factory,
983 base::SequencedTaskRunner* task_runner,
984 bool clean_journal,
985 leveldb::Status* status) {
986 IDB_TRACE("IndexedDBBackingStore::Open");
987 DCHECK(!path_base.empty());
988 *data_loss = blink::WebIDBDataLossNone;
989 *data_loss_message = "";
990 *is_disk_full = false;
992 *status = leveldb::Status::OK();
994 scoped_ptr<LevelDBComparator> comparator(new Comparator());
996 if (!base::IsStringASCII(path_base.AsUTF8Unsafe())) {
997 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
998 origin_url);
1000 if (!base::CreateDirectory(path_base)) {
1001 *status =
1002 leveldb::Status::IOError("Unable to create IndexedDB database path");
1003 LOG(ERROR) << status->ToString() << ": \"" << path_base.AsUTF8Unsafe()
1004 << "\"";
1005 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
1006 origin_url);
1007 return scoped_refptr<IndexedDBBackingStore>();
1010 const base::FilePath file_path =
1011 path_base.Append(ComputeFileName(origin_url));
1012 const base::FilePath blob_path =
1013 path_base.Append(ComputeBlobPath(origin_url));
1015 if (IsPathTooLong(file_path)) {
1016 *status = leveldb::Status::IOError("File path too long");
1017 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
1018 origin_url);
1019 return scoped_refptr<IndexedDBBackingStore>();
1022 scoped_ptr<LevelDBDatabase> db;
1023 *status = leveldb_factory->OpenLevelDB(
1024 file_path, comparator.get(), &db, is_disk_full);
1026 DCHECK(!db == !status->ok());
1027 if (!status->ok()) {
1028 if (leveldb_env::IndicatesDiskFull(*status)) {
1029 *is_disk_full = true;
1030 } else if (status->IsCorruption()) {
1031 *data_loss = blink::WebIDBDataLossTotal;
1032 *data_loss_message = leveldb_env::GetCorruptionMessage(*status);
1036 bool is_schema_known = false;
1037 if (db) {
1038 std::string corruption_message;
1039 if (ReadCorruptionInfo(path_base, origin_url, &corruption_message)) {
1040 LOG(ERROR) << "IndexedDB recovering from a corrupted (and deleted) "
1041 "database.";
1042 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
1043 origin_url);
1044 db.reset();
1045 *data_loss = blink::WebIDBDataLossTotal;
1046 *data_loss_message =
1047 "IndexedDB (database was corrupt): " + corruption_message;
1048 } else if (!IsSchemaKnown(db.get(), &is_schema_known)) {
1049 LOG(ERROR) << "IndexedDB had IO error checking schema, treating it as "
1050 "failure to open";
1051 HistogramOpenStatus(
1052 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
1053 origin_url);
1054 db.reset();
1055 *data_loss = blink::WebIDBDataLossTotal;
1056 *data_loss_message = "I/O error checking schema";
1057 } else if (!is_schema_known) {
1058 LOG(ERROR) << "IndexedDB backing store had unknown schema, treating it "
1059 "as failure to open";
1060 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
1061 origin_url);
1062 db.reset();
1063 *data_loss = blink::WebIDBDataLossTotal;
1064 *data_loss_message = "Unknown schema";
1068 DCHECK(status->ok() || !is_schema_known || status->IsIOError() ||
1069 status->IsCorruption());
1071 if (db) {
1072 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_SUCCESS, origin_url);
1073 } else if (status->IsIOError()) {
1074 LOG(ERROR) << "Unable to open backing store, not trying to recover - "
1075 << status->ToString();
1076 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY, origin_url);
1077 return scoped_refptr<IndexedDBBackingStore>();
1078 } else {
1079 DCHECK(!is_schema_known || status->IsCorruption());
1080 LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup";
1081 *status = leveldb_factory->DestroyLevelDB(file_path);
1082 if (!status->ok()) {
1083 LOG(ERROR) << "IndexedDB backing store cleanup failed";
1084 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
1085 origin_url);
1086 return scoped_refptr<IndexedDBBackingStore>();
1089 LOG(ERROR) << "IndexedDB backing store cleanup succeeded, reopening";
1090 leveldb_factory->OpenLevelDB(file_path, comparator.get(), &db, NULL);
1091 if (!db) {
1092 LOG(ERROR) << "IndexedDB backing store reopen after recovery failed";
1093 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
1094 origin_url);
1095 return scoped_refptr<IndexedDBBackingStore>();
1097 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
1098 origin_url);
1101 scoped_refptr<IndexedDBBackingStore> backing_store =
1102 Create(indexed_db_factory,
1103 origin_url,
1104 blob_path,
1105 request_context,
1106 db.Pass(),
1107 comparator.Pass(),
1108 task_runner,
1109 status);
1111 if (clean_journal && backing_store.get() &&
1112 !backing_store->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
1113 HistogramOpenStatus(
1114 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR, origin_url);
1115 return scoped_refptr<IndexedDBBackingStore>();
1117 return backing_store;
1120 // static
1121 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
1122 const GURL& origin_url,
1123 base::SequencedTaskRunner* task_runner,
1124 leveldb::Status* status) {
1125 DefaultLevelDBFactory leveldb_factory;
1126 return IndexedDBBackingStore::OpenInMemory(
1127 origin_url, &leveldb_factory, task_runner, status);
1130 // static
1131 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
1132 const GURL& origin_url,
1133 LevelDBFactory* leveldb_factory,
1134 base::SequencedTaskRunner* task_runner,
1135 leveldb::Status* status) {
1136 IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
1138 scoped_ptr<LevelDBComparator> comparator(new Comparator());
1139 scoped_ptr<LevelDBDatabase> db =
1140 LevelDBDatabase::OpenInMemory(comparator.get());
1141 if (!db) {
1142 LOG(ERROR) << "LevelDBDatabase::OpenInMemory failed.";
1143 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
1144 origin_url);
1145 return scoped_refptr<IndexedDBBackingStore>();
1147 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS, origin_url);
1149 return Create(NULL /* indexed_db_factory */,
1150 origin_url,
1151 base::FilePath(),
1152 NULL /* request_context */,
1153 db.Pass(),
1154 comparator.Pass(),
1155 task_runner,
1156 status);
1159 // static
1160 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
1161 IndexedDBFactory* indexed_db_factory,
1162 const GURL& origin_url,
1163 const base::FilePath& blob_path,
1164 net::URLRequestContext* request_context,
1165 scoped_ptr<LevelDBDatabase> db,
1166 scoped_ptr<LevelDBComparator> comparator,
1167 base::SequencedTaskRunner* task_runner,
1168 leveldb::Status* status) {
1169 // TODO(jsbell): Handle comparator name changes.
1170 scoped_refptr<IndexedDBBackingStore> backing_store(
1171 new IndexedDBBackingStore(indexed_db_factory,
1172 origin_url,
1173 blob_path,
1174 request_context,
1175 db.Pass(),
1176 comparator.Pass(),
1177 task_runner));
1178 *status = backing_store->SetUpMetadata();
1179 if (!status->ok())
1180 return scoped_refptr<IndexedDBBackingStore>();
1182 return backing_store;
1185 void IndexedDBBackingStore::GrantChildProcessPermissions(int child_process_id) {
1186 if (!child_process_ids_granted_.count(child_process_id)) {
1187 child_process_ids_granted_.insert(child_process_id);
1188 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
1189 child_process_id, blob_path_);
1193 std::vector<base::string16> IndexedDBBackingStore::GetDatabaseNames(
1194 leveldb::Status* s) {
1195 *s = leveldb::Status::OK();
1196 std::vector<base::string16> found_names;
1197 const std::string start_key =
1198 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
1199 const std::string stop_key =
1200 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
1202 DCHECK(found_names.empty());
1204 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1205 for (*s = it->Seek(start_key);
1206 s->ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1207 *s = it->Next()) {
1208 // Decode database name (in iterator key).
1209 StringPiece slice(it->Key());
1210 DatabaseNameKey database_name_key;
1211 if (!DatabaseNameKey::Decode(&slice, &database_name_key) ||
1212 !slice.empty()) {
1213 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
1214 continue;
1217 // Decode database id (in iterator value).
1218 int64 database_id = 0;
1219 StringPiece value_slice(it->Value());
1220 if (!DecodeInt(&value_slice, &database_id) || !value_slice.empty()) {
1221 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
1222 continue;
1225 // Look up version by id.
1226 bool found = false;
1227 int64 database_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1228 *s = GetVarInt(db_.get(),
1229 DatabaseMetaDataKey::Encode(
1230 database_id, DatabaseMetaDataKey::USER_INT_VERSION),
1231 &database_version,
1232 &found);
1233 if (!s->ok() || !found) {
1234 INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES);
1235 continue;
1238 // Ignore stale metadata from failed initial opens.
1239 if (database_version != IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
1240 found_names.push_back(database_name_key.database_name());
1243 if (!s->ok())
1244 INTERNAL_READ_ERROR(GET_DATABASE_NAMES);
1246 return found_names;
1249 leveldb::Status IndexedDBBackingStore::GetIDBDatabaseMetaData(
1250 const base::string16& name,
1251 IndexedDBDatabaseMetadata* metadata,
1252 bool* found) {
1253 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1254 *found = false;
1256 leveldb::Status s = GetInt(db_.get(), key, &metadata->id, found);
1257 if (!s.ok()) {
1258 INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
1259 return s;
1261 if (!*found)
1262 return leveldb::Status::OK();
1264 s = GetString(db_.get(),
1265 DatabaseMetaDataKey::Encode(metadata->id,
1266 DatabaseMetaDataKey::USER_VERSION),
1267 &metadata->version,
1268 found);
1269 if (!s.ok()) {
1270 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1271 return s;
1273 if (!*found) {
1274 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1275 return InternalInconsistencyStatus();
1278 s = GetVarInt(db_.get(),
1279 DatabaseMetaDataKey::Encode(
1280 metadata->id, DatabaseMetaDataKey::USER_INT_VERSION),
1281 &metadata->int_version,
1282 found);
1283 if (!s.ok()) {
1284 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1285 return s;
1287 if (!*found) {
1288 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1289 return InternalInconsistencyStatus();
1292 if (metadata->int_version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
1293 metadata->int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1295 s = GetMaxObjectStoreId(
1296 db_.get(), metadata->id, &metadata->max_object_store_id);
1297 if (!s.ok()) {
1298 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1301 // We don't cache this, we just check it if it's there.
1302 int64 blob_key_generator_current_number =
1303 DatabaseMetaDataKey::kInvalidBlobKey;
1305 s = GetVarInt(
1306 db_.get(),
1307 DatabaseMetaDataKey::Encode(
1308 metadata->id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
1309 &blob_key_generator_current_number,
1310 found);
1311 if (!s.ok()) {
1312 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1313 return s;
1315 if (!*found) {
1316 // This database predates blob support.
1317 *found = true;
1318 } else if (!DatabaseMetaDataKey::IsValidBlobKey(
1319 blob_key_generator_current_number)) {
1320 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1321 return InternalInconsistencyStatus();
1324 return s;
1327 WARN_UNUSED_RESULT static leveldb::Status GetNewDatabaseId(
1328 LevelDBTransaction* transaction,
1329 int64* new_id) {
1330 *new_id = -1;
1331 int64 max_database_id = -1;
1332 bool found = false;
1333 leveldb::Status s =
1334 GetInt(transaction, MaxDatabaseIdKey::Encode(), &max_database_id, &found);
1335 if (!s.ok()) {
1336 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID);
1337 return s;
1339 if (!found)
1340 max_database_id = 0;
1342 DCHECK_GE(max_database_id, 0);
1344 int64 database_id = max_database_id + 1;
1345 PutInt(transaction, MaxDatabaseIdKey::Encode(), database_id);
1346 *new_id = database_id;
1347 return leveldb::Status::OK();
1350 leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData(
1351 const base::string16& name,
1352 const base::string16& version,
1353 int64 int_version,
1354 int64* row_id) {
1355 // TODO(jsbell): Don't persist metadata if open fails. http://crbug.com/395472
1356 scoped_refptr<LevelDBTransaction> transaction =
1357 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
1359 leveldb::Status s = GetNewDatabaseId(transaction.get(), row_id);
1360 if (!s.ok())
1361 return s;
1362 DCHECK_GE(*row_id, 0);
1364 if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1365 int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1367 PutInt(transaction.get(),
1368 DatabaseNameKey::Encode(origin_identifier_, name),
1369 *row_id);
1370 PutString(
1371 transaction.get(),
1372 DatabaseMetaDataKey::Encode(*row_id, DatabaseMetaDataKey::USER_VERSION),
1373 version);
1374 PutVarInt(transaction.get(),
1375 DatabaseMetaDataKey::Encode(*row_id,
1376 DatabaseMetaDataKey::USER_INT_VERSION),
1377 int_version);
1378 PutVarInt(
1379 transaction.get(),
1380 DatabaseMetaDataKey::Encode(
1381 *row_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
1382 DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber);
1384 s = transaction->Commit();
1385 if (!s.ok())
1386 INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA);
1387 return s;
1390 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
1391 IndexedDBBackingStore::Transaction* transaction,
1392 int64 row_id,
1393 int64 int_version) {
1394 if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1395 int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1396 DCHECK_GE(int_version, 0) << "int_version was " << int_version;
1397 PutVarInt(transaction->transaction(),
1398 DatabaseMetaDataKey::Encode(row_id,
1399 DatabaseMetaDataKey::USER_INT_VERSION),
1400 int_version);
1401 return true;
1404 // If you're deleting a range that contains user keys that have blob info, this
1405 // won't clean up the blobs.
1406 static leveldb::Status DeleteRangeBasic(LevelDBTransaction* transaction,
1407 const std::string& begin,
1408 const std::string& end,
1409 bool upper_open) {
1410 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
1411 leveldb::Status s;
1412 for (s = it->Seek(begin); s.ok() && it->IsValid() &&
1413 (upper_open ? CompareKeys(it->Key(), end) < 0
1414 : CompareKeys(it->Key(), end) <= 0);
1415 s = it->Next())
1416 transaction->Remove(it->Key());
1417 return s;
1420 static leveldb::Status DeleteBlobsInRange(
1421 IndexedDBBackingStore::Transaction* transaction,
1422 int64 database_id,
1423 int64 object_store_id,
1424 const std::string& start_key,
1425 const std::string& end_key,
1426 bool upper_open) {
1427 scoped_ptr<LevelDBIterator> it = transaction->transaction()->CreateIterator();
1428 leveldb::Status s = it->Seek(start_key);
1429 for (; s.ok() && it->IsValid() &&
1430 (upper_open ? CompareKeys(it->Key(), end_key) < 0
1431 : CompareKeys(it->Key(), end_key) <= 0);
1432 s = it->Next()) {
1433 StringPiece key_piece(it->Key());
1434 std::string user_key =
1435 BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece);
1436 if (!user_key.size()) {
1437 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1438 return InternalInconsistencyStatus();
1440 transaction->PutBlobInfo(
1441 database_id, object_store_id, user_key, NULL, NULL);
1443 return s;
1446 static leveldb::Status DeleteBlobsInObjectStore(
1447 IndexedDBBackingStore::Transaction* transaction,
1448 int64 database_id,
1449 int64 object_store_id) {
1450 std::string start_key, stop_key;
1451 start_key =
1452 BlobEntryKey::EncodeMinKeyForObjectStore(database_id, object_store_id);
1453 stop_key =
1454 BlobEntryKey::EncodeStopKeyForObjectStore(database_id, object_store_id);
1455 return DeleteBlobsInRange(
1456 transaction, database_id, object_store_id, start_key, stop_key, true);
1459 leveldb::Status IndexedDBBackingStore::DeleteDatabase(
1460 const base::string16& name) {
1461 IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
1462 scoped_ptr<LevelDBDirectTransaction> transaction =
1463 LevelDBDirectTransaction::Create(db_.get());
1465 leveldb::Status s;
1467 IndexedDBDatabaseMetadata metadata;
1468 bool success = false;
1469 s = GetIDBDatabaseMetaData(name, &metadata, &success);
1470 if (!s.ok())
1471 return s;
1472 if (!success)
1473 return leveldb::Status::OK();
1475 const std::string start_key = DatabaseMetaDataKey::Encode(
1476 metadata.id, DatabaseMetaDataKey::ORIGIN_NAME);
1477 const std::string stop_key = DatabaseMetaDataKey::Encode(
1478 metadata.id + 1, DatabaseMetaDataKey::ORIGIN_NAME);
1480 IDB_TRACE("IndexedDBBackingStore::DeleteDatabase.DeleteEntries");
1481 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1482 for (s = it->Seek(start_key);
1483 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1484 s = it->Next())
1485 transaction->Remove(it->Key());
1487 if (!s.ok()) {
1488 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1489 return s;
1492 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1493 transaction->Remove(key);
1495 bool need_cleanup = false;
1496 if (active_blob_registry()->MarkDeletedCheckIfUsed(
1497 metadata.id, DatabaseMetaDataKey::kAllBlobsKey)) {
1498 s = MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id);
1499 if (!s.ok())
1500 return s;
1501 } else {
1502 s = MergeDatabaseIntoPrimaryBlobJournal(transaction.get(), metadata.id);
1503 if (!s.ok())
1504 return s;
1505 need_cleanup = true;
1508 s = transaction->Commit();
1509 if (!s.ok()) {
1510 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1511 return s;
1514 // If another transaction is running, this will defer processing
1515 // the journal until completion.
1516 if (need_cleanup)
1517 CleanPrimaryJournalIgnoreReturn();
1519 db_->Compact(start_key, stop_key);
1520 return s;
1523 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it,
1524 const std::string& stop_key,
1525 int64 object_store_id,
1526 int64 meta_data_type) {
1527 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
1528 return false;
1530 StringPiece slice(it->Key());
1531 ObjectStoreMetaDataKey meta_data_key;
1532 bool ok =
1533 ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
1534 DCHECK(ok);
1535 if (meta_data_key.ObjectStoreId() != object_store_id)
1536 return false;
1537 if (meta_data_key.MetaDataType() != meta_data_type)
1538 return false;
1539 return ok;
1542 // TODO(jsbell): This should do some error handling rather than
1543 // plowing ahead when bad data is encountered.
1544 leveldb::Status IndexedDBBackingStore::GetObjectStores(
1545 int64 database_id,
1546 IndexedDBDatabaseMetadata::ObjectStoreMap* object_stores) {
1547 IDB_TRACE("IndexedDBBackingStore::GetObjectStores");
1548 if (!KeyPrefix::IsValidDatabaseId(database_id))
1549 return InvalidDBKeyStatus();
1550 const std::string start_key =
1551 ObjectStoreMetaDataKey::Encode(database_id, 1, 0);
1552 const std::string stop_key =
1553 ObjectStoreMetaDataKey::EncodeMaxKey(database_id);
1555 DCHECK(object_stores->empty());
1557 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1558 leveldb::Status s = it->Seek(start_key);
1559 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
1560 StringPiece slice(it->Key());
1561 ObjectStoreMetaDataKey meta_data_key;
1562 bool ok =
1563 ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
1564 DCHECK(ok);
1565 if (!ok || meta_data_key.MetaDataType() != ObjectStoreMetaDataKey::NAME) {
1566 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1567 // Possible stale metadata, but don't fail the load.
1568 s = it->Next();
1569 if (!s.ok())
1570 break;
1571 continue;
1574 int64 object_store_id = meta_data_key.ObjectStoreId();
1576 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
1577 // simplify.
1578 base::string16 object_store_name;
1580 StringPiece slice(it->Value());
1581 if (!DecodeString(&slice, &object_store_name) || !slice.empty())
1582 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1585 s = it->Next();
1586 if (!s.ok())
1587 break;
1588 if (!CheckObjectStoreAndMetaDataType(it.get(),
1589 stop_key,
1590 object_store_id,
1591 ObjectStoreMetaDataKey::KEY_PATH)) {
1592 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1593 break;
1595 IndexedDBKeyPath key_path;
1597 StringPiece slice(it->Value());
1598 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
1599 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1602 s = it->Next();
1603 if (!s.ok())
1604 break;
1605 if (!CheckObjectStoreAndMetaDataType(
1606 it.get(),
1607 stop_key,
1608 object_store_id,
1609 ObjectStoreMetaDataKey::AUTO_INCREMENT)) {
1610 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1611 break;
1613 bool auto_increment;
1615 StringPiece slice(it->Value());
1616 if (!DecodeBool(&slice, &auto_increment) || !slice.empty())
1617 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1620 s = it->Next(); // Is evictable.
1621 if (!s.ok())
1622 break;
1623 if (!CheckObjectStoreAndMetaDataType(it.get(),
1624 stop_key,
1625 object_store_id,
1626 ObjectStoreMetaDataKey::EVICTABLE)) {
1627 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1628 break;
1631 s = it->Next(); // Last version.
1632 if (!s.ok())
1633 break;
1634 if (!CheckObjectStoreAndMetaDataType(
1635 it.get(),
1636 stop_key,
1637 object_store_id,
1638 ObjectStoreMetaDataKey::LAST_VERSION)) {
1639 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1640 break;
1643 s = it->Next(); // Maximum index id allocated.
1644 if (!s.ok())
1645 break;
1646 if (!CheckObjectStoreAndMetaDataType(
1647 it.get(),
1648 stop_key,
1649 object_store_id,
1650 ObjectStoreMetaDataKey::MAX_INDEX_ID)) {
1651 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1652 break;
1654 int64 max_index_id;
1656 StringPiece slice(it->Value());
1657 if (!DecodeInt(&slice, &max_index_id) || !slice.empty())
1658 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1661 s = it->Next(); // [optional] has key path (is not null)
1662 if (!s.ok())
1663 break;
1664 if (CheckObjectStoreAndMetaDataType(it.get(),
1665 stop_key,
1666 object_store_id,
1667 ObjectStoreMetaDataKey::HAS_KEY_PATH)) {
1668 bool has_key_path;
1670 StringPiece slice(it->Value());
1671 if (!DecodeBool(&slice, &has_key_path))
1672 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1674 // This check accounts for two layers of legacy coding:
1675 // (1) Initially, has_key_path was added to distinguish null vs. string.
1676 // (2) Later, null vs. string vs. array was stored in the key_path itself.
1677 // So this check is only relevant for string-type key_paths.
1678 if (!has_key_path &&
1679 (key_path.type() == blink::WebIDBKeyPathTypeString &&
1680 !key_path.string().empty())) {
1681 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1682 break;
1684 if (!has_key_path)
1685 key_path = IndexedDBKeyPath();
1686 s = it->Next();
1687 if (!s.ok())
1688 break;
1691 int64 key_generator_current_number = -1;
1692 if (CheckObjectStoreAndMetaDataType(
1693 it.get(),
1694 stop_key,
1695 object_store_id,
1696 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER)) {
1697 StringPiece slice(it->Value());
1698 if (!DecodeInt(&slice, &key_generator_current_number) || !slice.empty())
1699 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1701 // TODO(jsbell): Return key_generator_current_number, cache in
1702 // object store, and write lazily to backing store. For now,
1703 // just assert that if it was written it was valid.
1704 DCHECK_GE(key_generator_current_number, kKeyGeneratorInitialNumber);
1705 s = it->Next();
1706 if (!s.ok())
1707 break;
1710 IndexedDBObjectStoreMetadata metadata(object_store_name,
1711 object_store_id,
1712 key_path,
1713 auto_increment,
1714 max_index_id);
1715 s = GetIndexes(database_id, object_store_id, &metadata.indexes);
1716 if (!s.ok())
1717 break;
1718 (*object_stores)[object_store_id] = metadata;
1721 if (!s.ok())
1722 INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES);
1724 return s;
1727 WARN_UNUSED_RESULT static leveldb::Status SetMaxObjectStoreId(
1728 LevelDBTransaction* transaction,
1729 int64 database_id,
1730 int64 object_store_id) {
1731 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
1732 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
1733 int64 max_object_store_id = -1;
1734 leveldb::Status s = GetMaxObjectStoreId(
1735 transaction, max_object_store_id_key, &max_object_store_id);
1736 if (!s.ok()) {
1737 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1738 return s;
1741 if (object_store_id <= max_object_store_id) {
1742 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1743 return InternalInconsistencyStatus();
1745 PutInt(transaction, max_object_store_id_key, object_store_id);
1746 return s;
1749 void IndexedDBBackingStore::Compact() { db_->CompactAll(); }
1751 leveldb::Status IndexedDBBackingStore::CreateObjectStore(
1752 IndexedDBBackingStore::Transaction* transaction,
1753 int64 database_id,
1754 int64 object_store_id,
1755 const base::string16& name,
1756 const IndexedDBKeyPath& key_path,
1757 bool auto_increment) {
1758 IDB_TRACE("IndexedDBBackingStore::CreateObjectStore");
1759 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1760 return InvalidDBKeyStatus();
1761 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1762 leveldb::Status s =
1763 SetMaxObjectStoreId(leveldb_transaction, database_id, object_store_id);
1764 if (!s.ok())
1765 return s;
1767 const std::string name_key = ObjectStoreMetaDataKey::Encode(
1768 database_id, object_store_id, ObjectStoreMetaDataKey::NAME);
1769 const std::string key_path_key = ObjectStoreMetaDataKey::Encode(
1770 database_id, object_store_id, ObjectStoreMetaDataKey::KEY_PATH);
1771 const std::string auto_increment_key = ObjectStoreMetaDataKey::Encode(
1772 database_id, object_store_id, ObjectStoreMetaDataKey::AUTO_INCREMENT);
1773 const std::string evictable_key = ObjectStoreMetaDataKey::Encode(
1774 database_id, object_store_id, ObjectStoreMetaDataKey::EVICTABLE);
1775 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1776 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1777 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
1778 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
1779 const std::string has_key_path_key = ObjectStoreMetaDataKey::Encode(
1780 database_id, object_store_id, ObjectStoreMetaDataKey::HAS_KEY_PATH);
1781 const std::string key_generator_current_number_key =
1782 ObjectStoreMetaDataKey::Encode(
1783 database_id,
1784 object_store_id,
1785 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
1786 const std::string names_key = ObjectStoreNamesKey::Encode(database_id, name);
1788 PutString(leveldb_transaction, name_key, name);
1789 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
1790 PutInt(leveldb_transaction, auto_increment_key, auto_increment);
1791 PutInt(leveldb_transaction, evictable_key, false);
1792 PutInt(leveldb_transaction, last_version_key, 1);
1793 PutInt(leveldb_transaction, max_index_id_key, kMinimumIndexId);
1794 PutBool(leveldb_transaction, has_key_path_key, !key_path.IsNull());
1795 PutInt(leveldb_transaction,
1796 key_generator_current_number_key,
1797 kKeyGeneratorInitialNumber);
1798 PutInt(leveldb_transaction, names_key, object_store_id);
1799 return s;
1802 leveldb::Status IndexedDBBackingStore::DeleteObjectStore(
1803 IndexedDBBackingStore::Transaction* transaction,
1804 int64 database_id,
1805 int64 object_store_id) {
1806 IDB_TRACE("IndexedDBBackingStore::DeleteObjectStore");
1807 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1808 return InvalidDBKeyStatus();
1809 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1811 base::string16 object_store_name;
1812 bool found = false;
1813 leveldb::Status s =
1814 GetString(leveldb_transaction,
1815 ObjectStoreMetaDataKey::Encode(
1816 database_id, object_store_id, ObjectStoreMetaDataKey::NAME),
1817 &object_store_name,
1818 &found);
1819 if (!s.ok()) {
1820 INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1821 return s;
1823 if (!found) {
1824 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1825 return InternalInconsistencyStatus();
1828 s = DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
1829 if (!s.ok()) {
1830 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1831 return s;
1834 s = DeleteRangeBasic(
1835 leveldb_transaction,
1836 ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0),
1837 ObjectStoreMetaDataKey::EncodeMaxKey(database_id, object_store_id),
1838 true);
1840 if (s.ok()) {
1841 leveldb_transaction->Remove(
1842 ObjectStoreNamesKey::Encode(database_id, object_store_name));
1844 s = DeleteRangeBasic(
1845 leveldb_transaction,
1846 IndexFreeListKey::Encode(database_id, object_store_id, 0),
1847 IndexFreeListKey::EncodeMaxKey(database_id, object_store_id),
1848 true);
1851 if (s.ok()) {
1852 s = DeleteRangeBasic(
1853 leveldb_transaction,
1854 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0),
1855 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id),
1856 true);
1859 if (!s.ok()) {
1860 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1861 return s;
1864 return ClearObjectStore(transaction, database_id, object_store_id);
1867 leveldb::Status IndexedDBBackingStore::GetRecord(
1868 IndexedDBBackingStore::Transaction* transaction,
1869 int64 database_id,
1870 int64 object_store_id,
1871 const IndexedDBKey& key,
1872 IndexedDBValue* record) {
1873 IDB_TRACE("IndexedDBBackingStore::GetRecord");
1874 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1875 return InvalidDBKeyStatus();
1876 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1878 const std::string leveldb_key =
1879 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1880 std::string data;
1882 record->clear();
1884 bool found = false;
1885 leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
1886 if (!s.ok()) {
1887 INTERNAL_READ_ERROR(GET_RECORD);
1888 return s;
1890 if (!found)
1891 return s;
1892 if (data.empty()) {
1893 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1894 return leveldb::Status::NotFound("Record contained no data");
1897 int64 version;
1898 StringPiece slice(data);
1899 if (!DecodeVarInt(&slice, &version)) {
1900 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1901 return InternalInconsistencyStatus();
1904 record->bits = slice.as_string();
1905 return transaction->GetBlobInfoForRecord(database_id, leveldb_key, record);
1908 WARN_UNUSED_RESULT static leveldb::Status GetNewVersionNumber(
1909 LevelDBTransaction* transaction,
1910 int64 database_id,
1911 int64 object_store_id,
1912 int64* new_version_number) {
1913 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1914 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1916 *new_version_number = -1;
1917 int64 last_version = -1;
1918 bool found = false;
1919 leveldb::Status s =
1920 GetInt(transaction, last_version_key, &last_version, &found);
1921 if (!s.ok()) {
1922 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER);
1923 return s;
1925 if (!found)
1926 last_version = 0;
1928 DCHECK_GE(last_version, 0);
1930 int64 version = last_version + 1;
1931 PutInt(transaction, last_version_key, version);
1933 // TODO(jsbell): Think about how we want to handle the overflow scenario.
1934 DCHECK(version > last_version);
1936 *new_version_number = version;
1937 return s;
1940 leveldb::Status IndexedDBBackingStore::PutRecord(
1941 IndexedDBBackingStore::Transaction* transaction,
1942 int64 database_id,
1943 int64 object_store_id,
1944 const IndexedDBKey& key,
1945 IndexedDBValue* value,
1946 ScopedVector<storage::BlobDataHandle>* handles,
1947 RecordIdentifier* record_identifier) {
1948 IDB_TRACE("IndexedDBBackingStore::PutRecord");
1949 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1950 return InvalidDBKeyStatus();
1951 DCHECK(key.IsValid());
1953 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1954 int64 version = -1;
1955 leveldb::Status s = GetNewVersionNumber(
1956 leveldb_transaction, database_id, object_store_id, &version);
1957 if (!s.ok())
1958 return s;
1959 DCHECK_GE(version, 0);
1960 const std::string object_store_data_key =
1961 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1963 std::string v;
1964 EncodeVarInt(version, &v);
1965 v.append(value->bits);
1967 leveldb_transaction->Put(object_store_data_key, &v);
1968 s = transaction->PutBlobInfoIfNeeded(database_id,
1969 object_store_id,
1970 object_store_data_key,
1971 &value->blob_info,
1972 handles);
1973 if (!s.ok())
1974 return s;
1975 DCHECK(!handles->size());
1977 const std::string exists_entry_key =
1978 ExistsEntryKey::Encode(database_id, object_store_id, key);
1979 std::string version_encoded;
1980 EncodeInt(version, &version_encoded);
1981 leveldb_transaction->Put(exists_entry_key, &version_encoded);
1983 std::string key_encoded;
1984 EncodeIDBKey(key, &key_encoded);
1985 record_identifier->Reset(key_encoded, version);
1986 return s;
1989 leveldb::Status IndexedDBBackingStore::ClearObjectStore(
1990 IndexedDBBackingStore::Transaction* transaction,
1991 int64 database_id,
1992 int64 object_store_id) {
1993 IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
1994 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1995 return InvalidDBKeyStatus();
1996 const std::string start_key =
1997 KeyPrefix(database_id, object_store_id).Encode();
1998 const std::string stop_key =
1999 KeyPrefix(database_id, object_store_id + 1).Encode();
2001 leveldb::Status s =
2002 DeleteRangeBasic(transaction->transaction(), start_key, stop_key, true);
2003 if (!s.ok()) {
2004 INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE);
2005 return s;
2007 return DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
2010 leveldb::Status IndexedDBBackingStore::DeleteRecord(
2011 IndexedDBBackingStore::Transaction* transaction,
2012 int64 database_id,
2013 int64 object_store_id,
2014 const RecordIdentifier& record_identifier) {
2015 IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
2016 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2017 return InvalidDBKeyStatus();
2018 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2020 const std::string object_store_data_key = ObjectStoreDataKey::Encode(
2021 database_id, object_store_id, record_identifier.primary_key());
2022 leveldb_transaction->Remove(object_store_data_key);
2023 leveldb::Status s = transaction->PutBlobInfoIfNeeded(
2024 database_id, object_store_id, object_store_data_key, NULL, NULL);
2025 if (!s.ok())
2026 return s;
2028 const std::string exists_entry_key = ExistsEntryKey::Encode(
2029 database_id, object_store_id, record_identifier.primary_key());
2030 leveldb_transaction->Remove(exists_entry_key);
2031 return leveldb::Status::OK();
2034 leveldb::Status IndexedDBBackingStore::DeleteRange(
2035 IndexedDBBackingStore::Transaction* transaction,
2036 int64 database_id,
2037 int64 object_store_id,
2038 const IndexedDBKeyRange& key_range) {
2039 leveldb::Status s;
2040 scoped_ptr<IndexedDBBackingStore::Cursor> start_cursor =
2041 OpenObjectStoreCursor(transaction,
2042 database_id,
2043 object_store_id,
2044 key_range,
2045 blink::WebIDBCursorDirectionNext,
2046 &s);
2047 if (!s.ok())
2048 return s;
2049 if (!start_cursor)
2050 return leveldb::Status::OK(); // Empty range == delete success.
2052 scoped_ptr<IndexedDBBackingStore::Cursor> end_cursor =
2053 OpenObjectStoreCursor(transaction,
2054 database_id,
2055 object_store_id,
2056 key_range,
2057 blink::WebIDBCursorDirectionPrev,
2058 &s);
2060 if (!s.ok())
2061 return s;
2062 if (!end_cursor)
2063 return leveldb::Status::OK(); // Empty range == delete success.
2065 BlobEntryKey start_blob_key, end_blob_key;
2067 std::string start_key = ObjectStoreDataKey::Encode(
2068 database_id, object_store_id, start_cursor->key());
2069 base::StringPiece start_key_piece(start_key);
2070 if (!BlobEntryKey::FromObjectStoreDataKey(&start_key_piece, &start_blob_key))
2071 return InternalInconsistencyStatus();
2072 std::string stop_key = ObjectStoreDataKey::Encode(
2073 database_id, object_store_id, end_cursor->key());
2074 base::StringPiece stop_key_piece(stop_key);
2075 if (!BlobEntryKey::FromObjectStoreDataKey(&stop_key_piece, &end_blob_key))
2076 return InternalInconsistencyStatus();
2078 s = DeleteBlobsInRange(transaction,
2079 database_id,
2080 object_store_id,
2081 start_blob_key.Encode(),
2082 end_blob_key.Encode(),
2083 false);
2084 if (!s.ok())
2085 return s;
2086 s = DeleteRangeBasic(transaction->transaction(), start_key, stop_key, false);
2087 if (!s.ok())
2088 return s;
2089 start_key =
2090 ExistsEntryKey::Encode(database_id, object_store_id, start_cursor->key());
2091 stop_key =
2092 ExistsEntryKey::Encode(database_id, object_store_id, end_cursor->key());
2093 return DeleteRangeBasic(
2094 transaction->transaction(), start_key, stop_key, false);
2097 leveldb::Status IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
2098 IndexedDBBackingStore::Transaction* transaction,
2099 int64 database_id,
2100 int64 object_store_id,
2101 int64* key_generator_current_number) {
2102 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2103 return InvalidDBKeyStatus();
2104 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2106 const std::string key_generator_current_number_key =
2107 ObjectStoreMetaDataKey::Encode(
2108 database_id,
2109 object_store_id,
2110 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
2112 *key_generator_current_number = -1;
2113 std::string data;
2115 bool found = false;
2116 leveldb::Status s =
2117 leveldb_transaction->Get(key_generator_current_number_key, &data, &found);
2118 if (!s.ok()) {
2119 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2120 return s;
2122 if (found && !data.empty()) {
2123 StringPiece slice(data);
2124 if (!DecodeInt(&slice, key_generator_current_number) || !slice.empty()) {
2125 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2126 return InternalInconsistencyStatus();
2128 return s;
2131 // Previously, the key generator state was not stored explicitly
2132 // but derived from the maximum numeric key present in existing
2133 // data. This violates the spec as the data may be cleared but the
2134 // key generator state must be preserved.
2135 // TODO(jsbell): Fix this for all stores on database open?
2136 const std::string start_key =
2137 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
2138 const std::string stop_key =
2139 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
2141 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
2142 int64 max_numeric_key = 0;
2144 for (s = it->Seek(start_key);
2145 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
2146 s = it->Next()) {
2147 StringPiece slice(it->Key());
2148 ObjectStoreDataKey data_key;
2149 if (!ObjectStoreDataKey::Decode(&slice, &data_key) || !slice.empty()) {
2150 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2151 return InternalInconsistencyStatus();
2153 scoped_ptr<IndexedDBKey> user_key = data_key.user_key();
2154 if (user_key->type() == blink::WebIDBKeyTypeNumber) {
2155 int64 n = static_cast<int64>(user_key->number());
2156 if (n > max_numeric_key)
2157 max_numeric_key = n;
2161 if (s.ok())
2162 *key_generator_current_number = max_numeric_key + 1;
2163 else
2164 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2166 return s;
2169 leveldb::Status IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
2170 IndexedDBBackingStore::Transaction* transaction,
2171 int64 database_id,
2172 int64 object_store_id,
2173 int64 new_number,
2174 bool check_current) {
2175 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2176 return InvalidDBKeyStatus();
2178 if (check_current) {
2179 int64 current_number;
2180 leveldb::Status s = GetKeyGeneratorCurrentNumber(
2181 transaction, database_id, object_store_id, &current_number);
2182 if (!s.ok())
2183 return s;
2184 if (new_number <= current_number)
2185 return s;
2188 const std::string key_generator_current_number_key =
2189 ObjectStoreMetaDataKey::Encode(
2190 database_id,
2191 object_store_id,
2192 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
2193 PutInt(
2194 transaction->transaction(), key_generator_current_number_key, new_number);
2195 return leveldb::Status::OK();
2198 leveldb::Status IndexedDBBackingStore::KeyExistsInObjectStore(
2199 IndexedDBBackingStore::Transaction* transaction,
2200 int64 database_id,
2201 int64 object_store_id,
2202 const IndexedDBKey& key,
2203 RecordIdentifier* found_record_identifier,
2204 bool* found) {
2205 IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
2206 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2207 return InvalidDBKeyStatus();
2208 *found = false;
2209 const std::string leveldb_key =
2210 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
2211 std::string data;
2213 leveldb::Status s =
2214 transaction->transaction()->Get(leveldb_key, &data, found);
2215 if (!s.ok()) {
2216 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
2217 return s;
2219 if (!*found)
2220 return leveldb::Status::OK();
2221 if (!data.size()) {
2222 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
2223 return InternalInconsistencyStatus();
2226 int64 version;
2227 StringPiece slice(data);
2228 if (!DecodeVarInt(&slice, &version))
2229 return InternalInconsistencyStatus();
2231 std::string encoded_key;
2232 EncodeIDBKey(key, &encoded_key);
2233 found_record_identifier->Reset(encoded_key, version);
2234 return s;
2237 class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
2238 : public IndexedDBBackingStore::Transaction::ChainedBlobWriter {
2239 public:
2240 typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
2241 WriteDescriptorVec;
2242 ChainedBlobWriterImpl(
2243 int64 database_id,
2244 IndexedDBBackingStore* backing_store,
2245 WriteDescriptorVec* blobs,
2246 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback)
2247 : waiting_for_callback_(false),
2248 database_id_(database_id),
2249 backing_store_(backing_store),
2250 callback_(callback) {
2251 blobs_.swap(*blobs);
2252 iter_ = blobs_.begin();
2253 backing_store->task_runner()->PostTask(
2254 FROM_HERE, base::Bind(&ChainedBlobWriterImpl::WriteNextFile, this));
2257 void set_delegate(scoped_ptr<FileWriterDelegate> delegate) override {
2258 delegate_.reset(delegate.release());
2261 void ReportWriteCompletion(bool succeeded, int64 bytes_written) override {
2262 DCHECK(waiting_for_callback_);
2263 DCHECK(!succeeded || bytes_written >= 0);
2264 waiting_for_callback_ = false;
2265 if (delegate_.get()) // Only present for Blob, not File.
2266 content::BrowserThread::DeleteSoon(
2267 content::BrowserThread::IO, FROM_HERE, delegate_.release());
2268 if (aborted_self_ref_.get()) {
2269 aborted_self_ref_ = NULL;
2270 return;
2272 if (iter_->size() != -1 && iter_->size() != bytes_written)
2273 succeeded = false;
2274 if (succeeded) {
2275 ++iter_;
2276 WriteNextFile();
2277 } else {
2278 callback_->Run(false);
2282 void Abort() override {
2283 if (!waiting_for_callback_)
2284 return;
2285 aborted_self_ref_ = this;
2288 private:
2289 ~ChainedBlobWriterImpl() override { DCHECK(!waiting_for_callback_); }
2291 void WriteNextFile() {
2292 DCHECK(!waiting_for_callback_);
2293 DCHECK(!aborted_self_ref_.get());
2294 if (iter_ == blobs_.end()) {
2295 callback_->Run(true);
2296 return;
2297 } else {
2298 waiting_for_callback_ = true;
2299 if (!backing_store_->WriteBlobFile(database_id_, *iter_, this)) {
2300 waiting_for_callback_ = false;
2301 callback_->Run(false);
2302 return;
2307 bool waiting_for_callback_;
2308 scoped_refptr<ChainedBlobWriterImpl> aborted_self_ref_;
2309 WriteDescriptorVec blobs_;
2310 WriteDescriptorVec::const_iterator iter_;
2311 int64 database_id_;
2312 IndexedDBBackingStore* backing_store_;
2313 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback_;
2314 scoped_ptr<FileWriterDelegate> delegate_;
2316 DISALLOW_COPY_AND_ASSIGN(ChainedBlobWriterImpl);
2319 class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
2320 public base::RefCountedThreadSafe<LocalWriteClosure> {
2321 public:
2322 LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter*
2323 chained_blob_writer,
2324 base::SequencedTaskRunner* task_runner)
2325 : chained_blob_writer_(chained_blob_writer),
2326 task_runner_(task_runner),
2327 bytes_written_(0) {}
2329 void Run(base::File::Error rv,
2330 int64 bytes,
2331 FileWriterDelegate::WriteProgressStatus write_status) {
2332 DCHECK_GE(bytes, 0);
2333 bytes_written_ += bytes;
2334 if (write_status == FileWriterDelegate::SUCCESS_IO_PENDING)
2335 return; // We don't care about progress events.
2336 if (rv == base::File::FILE_OK) {
2337 DCHECK_EQ(write_status, FileWriterDelegate::SUCCESS_COMPLETED);
2338 } else {
2339 DCHECK(write_status == FileWriterDelegate::ERROR_WRITE_STARTED ||
2340 write_status == FileWriterDelegate::ERROR_WRITE_NOT_STARTED);
2343 bool success = write_status == FileWriterDelegate::SUCCESS_COMPLETED;
2344 if (success && !bytes_written_) {
2345 // LocalFileStreamWriter only creates a file if data is actually written.
2346 // If none was then create one now.
2347 task_runner_->PostTask(
2348 FROM_HERE, base::Bind(&LocalWriteClosure::CreateEmptyFile, this));
2349 } else if (success && !last_modified_.is_null()) {
2350 task_runner_->PostTask(
2351 FROM_HERE, base::Bind(&LocalWriteClosure::UpdateTimeStamp, this));
2352 } else {
2353 task_runner_->PostTask(
2354 FROM_HERE,
2355 base::Bind(&IndexedDBBackingStore::Transaction::ChainedBlobWriter::
2356 ReportWriteCompletion,
2357 chained_blob_writer_,
2358 success,
2359 bytes_written_));
2363 void WriteBlobToFileOnIOThread(const FilePath& file_path,
2364 const GURL& blob_url,
2365 const base::Time& last_modified,
2366 net::URLRequestContext* request_context) {
2367 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
2368 scoped_ptr<storage::FileStreamWriter> writer(
2369 storage::FileStreamWriter::CreateForLocalFile(
2370 task_runner_.get(),
2371 file_path,
2373 storage::FileStreamWriter::CREATE_NEW_FILE));
2374 scoped_ptr<FileWriterDelegate> delegate(new FileWriterDelegate(
2375 writer.Pass(), storage::FlushPolicy::FLUSH_ON_COMPLETION));
2377 DCHECK(blob_url.is_valid());
2378 scoped_ptr<net::URLRequest> blob_request(request_context->CreateRequest(
2379 blob_url, net::DEFAULT_PRIORITY, delegate.get()));
2381 this->file_path_ = file_path;
2382 this->last_modified_ = last_modified;
2384 delegate->Start(blob_request.Pass(),
2385 base::Bind(&LocalWriteClosure::Run, this));
2386 chained_blob_writer_->set_delegate(delegate.Pass());
2389 private:
2390 virtual ~LocalWriteClosure() {
2391 // Make sure the last reference to a ChainedBlobWriter is released (and
2392 // deleted) on the IDB thread since it owns a transaction which has thread
2393 // affinity.
2394 IndexedDBBackingStore::Transaction::ChainedBlobWriter* raw_tmp =
2395 chained_blob_writer_.get();
2396 raw_tmp->AddRef();
2397 chained_blob_writer_ = NULL;
2398 task_runner_->ReleaseSoon(FROM_HERE, raw_tmp);
2400 friend class base::RefCountedThreadSafe<LocalWriteClosure>;
2402 // If necessary, update the timestamps on the file as a final
2403 // step before reporting success.
2404 void UpdateTimeStamp() {
2405 DCHECK(task_runner_->RunsTasksOnCurrentThread());
2406 if (!base::TouchFile(file_path_, last_modified_, last_modified_)) {
2407 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2409 chained_blob_writer_->ReportWriteCompletion(true, bytes_written_);
2412 // Create an empty file.
2413 void CreateEmptyFile() {
2414 DCHECK(task_runner_->RunsTasksOnCurrentThread());
2415 base::File file(file_path_,
2416 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
2417 bool success = file.created();
2418 if (success && !last_modified_.is_null() &&
2419 !file.SetTimes(last_modified_, last_modified_)) {
2420 // TODO(cmumford): Complain quietly; timestamp's probably not vital.
2422 file.Close();
2423 chained_blob_writer_->ReportWriteCompletion(success, bytes_written_);
2426 scoped_refptr<IndexedDBBackingStore::Transaction::ChainedBlobWriter>
2427 chained_blob_writer_;
2428 scoped_refptr<base::SequencedTaskRunner> task_runner_;
2429 int64 bytes_written_;
2431 base::FilePath file_path_;
2432 base::Time last_modified_;
2434 DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure);
2437 bool IndexedDBBackingStore::WriteBlobFile(
2438 int64 database_id,
2439 const Transaction::WriteDescriptor& descriptor,
2440 Transaction::ChainedBlobWriter* chained_blob_writer) {
2442 if (!MakeIDBBlobDirectory(blob_path_, database_id, descriptor.key()))
2443 return false;
2445 FilePath path = GetBlobFileName(database_id, descriptor.key());
2447 if (descriptor.is_file() && !descriptor.file_path().empty()) {
2448 if (!base::CopyFile(descriptor.file_path(), path))
2449 return false;
2451 base::File::Info info;
2452 if (base::GetFileInfo(descriptor.file_path(), &info)) {
2453 if (descriptor.size() != -1) {
2454 if (descriptor.size() != info.size)
2455 return false;
2456 // The round-trip can be lossy; round to nearest millisecond.
2457 int64 delta = (descriptor.last_modified() -
2458 info.last_modified).InMilliseconds();
2459 if (std::abs(delta) > 1)
2460 return false;
2462 if (!base::TouchFile(path, info.last_accessed, info.last_modified)) {
2463 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2465 } else {
2466 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2469 task_runner_->PostTask(
2470 FROM_HERE,
2471 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion,
2472 chained_blob_writer,
2473 true,
2474 info.size));
2475 } else {
2476 DCHECK(descriptor.url().is_valid());
2477 scoped_refptr<LocalWriteClosure> write_closure(
2478 new LocalWriteClosure(chained_blob_writer, task_runner_.get()));
2479 content::BrowserThread::PostTask(
2480 content::BrowserThread::IO,
2481 FROM_HERE,
2482 base::Bind(&LocalWriteClosure::WriteBlobToFileOnIOThread,
2483 write_closure.get(),
2484 path,
2485 descriptor.url(),
2486 descriptor.last_modified(),
2487 request_context_));
2489 return true;
2492 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
2493 int64 blob_key) {
2494 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2495 bool all_blobs = blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2496 DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2497 scoped_refptr<LevelDBTransaction> transaction =
2498 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2500 BlobJournalType live_blob_journal, primary_journal;
2501 if (!GetLiveBlobJournal(transaction.get(), &live_blob_journal).ok())
2502 return;
2503 DCHECK(!live_blob_journal.empty());
2504 if (!GetPrimaryBlobJournal(transaction.get(), &primary_journal).ok())
2505 return;
2507 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2508 // remove all entries with database_id from the live_blob journal and add only
2509 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2510 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2511 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2512 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2513 // matching (database_id, blob_key) tuple, we should move it to the primary
2514 // journal.
2515 BlobJournalType new_live_blob_journal;
2516 for (BlobJournalType::iterator journal_iter = live_blob_journal.begin();
2517 journal_iter != live_blob_journal.end();
2518 ++journal_iter) {
2519 int64 current_database_id = journal_iter->first;
2520 int64 current_blob_key = journal_iter->second;
2521 bool current_all_blobs =
2522 current_blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2523 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id) ||
2524 current_all_blobs);
2525 if (current_database_id == database_id &&
2526 (all_blobs || current_all_blobs || blob_key == current_blob_key)) {
2527 if (!all_blobs) {
2528 primary_journal.push_back(
2529 std::make_pair(database_id, current_blob_key));
2530 if (current_all_blobs)
2531 new_live_blob_journal.push_back(*journal_iter);
2532 new_live_blob_journal.insert(new_live_blob_journal.end(),
2533 ++journal_iter,
2534 live_blob_journal.end()); // All the rest.
2535 break;
2537 } else {
2538 new_live_blob_journal.push_back(*journal_iter);
2541 if (all_blobs) {
2542 primary_journal.push_back(
2543 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
2545 UpdatePrimaryBlobJournal(transaction.get(), primary_journal);
2546 UpdateLiveBlobJournal(transaction.get(), new_live_blob_journal);
2547 transaction->Commit();
2548 // We could just do the deletions/cleaning here, but if there are a lot of
2549 // blobs about to be garbage collected, it'd be better to wait and do them all
2550 // at once.
2551 StartJournalCleaningTimer();
2554 // The this reference is a raw pointer that's declared Unretained inside the
2555 // timer code, so this won't confuse IndexedDBFactory's check for
2556 // HasLastBackingStoreReference. It's safe because if the backing store is
2557 // deleted, the timer will automatically be canceled on destruction.
2558 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2559 journal_cleaning_timer_.Start(
2560 FROM_HERE,
2561 base::TimeDelta::FromSeconds(5),
2562 this,
2563 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn);
2566 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
2567 FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id,
2568 int64 key) const {
2569 return GetBlobFileNameForKey(blob_path_, database_id, key);
2572 static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
2573 const std::string& stop_key,
2574 int64 index_id,
2575 unsigned char meta_data_type) {
2576 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
2577 return false;
2579 StringPiece slice(it->Key());
2580 IndexMetaDataKey meta_data_key;
2581 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2582 DCHECK(ok);
2583 if (meta_data_key.IndexId() != index_id)
2584 return false;
2585 if (meta_data_key.meta_data_type() != meta_data_type)
2586 return false;
2587 return true;
2590 // TODO(jsbell): This should do some error handling rather than plowing ahead
2591 // when bad data is encountered.
2592 leveldb::Status IndexedDBBackingStore::GetIndexes(
2593 int64 database_id,
2594 int64 object_store_id,
2595 IndexedDBObjectStoreMetadata::IndexMap* indexes) {
2596 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2597 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2598 return InvalidDBKeyStatus();
2599 const std::string start_key =
2600 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0);
2601 const std::string stop_key =
2602 IndexMetaDataKey::Encode(database_id, object_store_id + 1, 0, 0);
2604 DCHECK(indexes->empty());
2606 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
2607 leveldb::Status s = it->Seek(start_key);
2608 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
2609 StringPiece slice(it->Key());
2610 IndexMetaDataKey meta_data_key;
2611 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2612 DCHECK(ok);
2613 if (meta_data_key.meta_data_type() != IndexMetaDataKey::NAME) {
2614 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2615 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2616 // the load.
2617 s = it->Next();
2618 if (!s.ok())
2619 break;
2620 continue;
2623 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2624 // simplify.
2625 int64 index_id = meta_data_key.IndexId();
2626 base::string16 index_name;
2628 StringPiece slice(it->Value());
2629 if (!DecodeString(&slice, &index_name) || !slice.empty())
2630 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2633 s = it->Next(); // unique flag
2634 if (!s.ok())
2635 break;
2636 if (!CheckIndexAndMetaDataKey(
2637 it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
2638 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2639 break;
2641 bool index_unique;
2643 StringPiece slice(it->Value());
2644 if (!DecodeBool(&slice, &index_unique) || !slice.empty())
2645 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2648 s = it->Next(); // key_path
2649 if (!s.ok())
2650 break;
2651 if (!CheckIndexAndMetaDataKey(
2652 it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
2653 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2654 break;
2656 IndexedDBKeyPath key_path;
2658 StringPiece slice(it->Value());
2659 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
2660 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2663 s = it->Next(); // [optional] multi_entry flag
2664 if (!s.ok())
2665 break;
2666 bool index_multi_entry = false;
2667 if (CheckIndexAndMetaDataKey(
2668 it.get(), stop_key, index_id, IndexMetaDataKey::MULTI_ENTRY)) {
2669 StringPiece slice(it->Value());
2670 if (!DecodeBool(&slice, &index_multi_entry) || !slice.empty())
2671 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2673 s = it->Next();
2674 if (!s.ok())
2675 break;
2678 (*indexes)[index_id] = IndexedDBIndexMetadata(
2679 index_name, index_id, key_path, index_unique, index_multi_entry);
2682 if (!s.ok())
2683 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES);
2685 return s;
2688 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) const {
2689 FilePath path = GetBlobFileName(database_id, key);
2690 return base::DeleteFile(path, false);
2693 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) const {
2694 FilePath path = GetBlobDirectoryName(blob_path_, database_id);
2695 return base::DeleteFile(path, true);
2698 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournalEntries(
2699 const BlobJournalType& journal) const {
2700 IDB_TRACE("IndexedDBBackingStore::CleanUpBlobJournalEntries");
2701 if (journal.empty())
2702 return leveldb::Status::OK();
2703 for (const auto& entry : journal) {
2704 int64 database_id = entry.first;
2705 int64 blob_key = entry.second;
2706 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2707 if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
2708 if (!RemoveBlobDirectory(database_id))
2709 return IOErrorStatus();
2710 } else {
2711 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2712 if (!RemoveBlobFile(database_id, blob_key))
2713 return IOErrorStatus();
2716 return leveldb::Status::OK();
2719 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal(
2720 const std::string& level_db_key) const {
2721 IDB_TRACE("IndexedDBBackingStore::CleanUpBlobJournal");
2722 DCHECK(!committing_transaction_count_);
2723 leveldb::Status s;
2724 scoped_refptr<LevelDBTransaction> journal_transaction =
2725 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2726 BlobJournalType journal;
2728 s = GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
2729 if (!s.ok())
2730 return s;
2731 if (journal.empty())
2732 return leveldb::Status::OK();
2733 s = CleanUpBlobJournalEntries(journal);
2734 if (!s.ok())
2735 return s;
2736 ClearBlobJournal(journal_transaction.get(), level_db_key);
2737 return journal_transaction->Commit();
2740 leveldb::Status IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
2741 int64 database_id,
2742 const std::string& object_store_data_key,
2743 IndexedDBValue* value) {
2744 BlobChangeRecord* change_record = NULL;
2745 BlobChangeMap::const_iterator blob_iter =
2746 blob_change_map_.find(object_store_data_key);
2747 if (blob_iter != blob_change_map_.end()) {
2748 change_record = blob_iter->second;
2749 } else {
2750 blob_iter = incognito_blob_map_.find(object_store_data_key);
2751 if (blob_iter != incognito_blob_map_.end())
2752 change_record = blob_iter->second;
2754 if (change_record) {
2755 // Either we haven't written the blob to disk yet or we're in incognito
2756 // mode, so we have to send back the one they sent us. This change record
2757 // includes the original UUID.
2758 value->blob_info = change_record->blob_info();
2759 return leveldb::Status::OK();
2762 BlobEntryKey blob_entry_key;
2763 StringPiece leveldb_key_piece(object_store_data_key);
2764 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
2765 &blob_entry_key)) {
2766 NOTREACHED();
2767 return InternalInconsistencyStatus();
2769 std::string encoded_key = blob_entry_key.Encode();
2770 bool found;
2771 std::string encoded_value;
2772 leveldb::Status s = transaction()->Get(encoded_key, &encoded_value, &found);
2773 if (!s.ok())
2774 return s;
2775 if (found) {
2776 if (!DecodeBlobData(encoded_value, &value->blob_info)) {
2777 INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD);
2778 return InternalInconsistencyStatus();
2780 for (auto& entry : value->blob_info) {
2781 entry.set_file_path(
2782 backing_store_->GetBlobFileName(database_id, entry.key()));
2783 entry.set_mark_used_callback(
2784 backing_store_->active_blob_registry()->GetAddBlobRefCallback(
2785 database_id, entry.key()));
2786 entry.set_release_callback(
2787 backing_store_->active_blob_registry()->GetFinalReleaseCallback(
2788 database_id, entry.key()));
2789 if (entry.is_file() && !entry.file_path().empty()) {
2790 base::File::Info info;
2791 if (base::GetFileInfo(entry.file_path(), &info)) {
2792 // This should always work, but it isn't fatal if it doesn't; it just
2793 // means a potential slow synchronous call from the renderer later.
2794 entry.set_last_modified(info.last_modified);
2795 entry.set_size(info.size);
2800 return leveldb::Status::OK();
2803 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2804 // While a transaction is busy it is not safe to clean the journal.
2805 if (committing_transaction_count_ > 0)
2806 StartJournalCleaningTimer();
2807 else
2808 CleanUpBlobJournal(BlobJournalKey::Encode());
2811 WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId(
2812 LevelDBTransaction* transaction,
2813 int64 database_id,
2814 int64 object_store_id,
2815 int64 index_id) {
2816 int64 max_index_id = -1;
2817 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
2818 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
2819 bool found = false;
2820 leveldb::Status s =
2821 GetInt(transaction, max_index_id_key, &max_index_id, &found);
2822 if (!s.ok()) {
2823 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2824 return s;
2826 if (!found)
2827 max_index_id = kMinimumIndexId;
2829 if (index_id <= max_index_id) {
2830 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2831 return InternalInconsistencyStatus();
2834 PutInt(transaction, max_index_id_key, index_id);
2835 return s;
2838 leveldb::Status IndexedDBBackingStore::CreateIndex(
2839 IndexedDBBackingStore::Transaction* transaction,
2840 int64 database_id,
2841 int64 object_store_id,
2842 int64 index_id,
2843 const base::string16& name,
2844 const IndexedDBKeyPath& key_path,
2845 bool is_unique,
2846 bool is_multi_entry) {
2847 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2848 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2849 return InvalidDBKeyStatus();
2850 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2851 leveldb::Status s = SetMaxIndexId(
2852 leveldb_transaction, database_id, object_store_id, index_id);
2854 if (!s.ok())
2855 return s;
2857 const std::string name_key = IndexMetaDataKey::Encode(
2858 database_id, object_store_id, index_id, IndexMetaDataKey::NAME);
2859 const std::string unique_key = IndexMetaDataKey::Encode(
2860 database_id, object_store_id, index_id, IndexMetaDataKey::UNIQUE);
2861 const std::string key_path_key = IndexMetaDataKey::Encode(
2862 database_id, object_store_id, index_id, IndexMetaDataKey::KEY_PATH);
2863 const std::string multi_entry_key = IndexMetaDataKey::Encode(
2864 database_id, object_store_id, index_id, IndexMetaDataKey::MULTI_ENTRY);
2866 PutString(leveldb_transaction, name_key, name);
2867 PutBool(leveldb_transaction, unique_key, is_unique);
2868 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
2869 PutBool(leveldb_transaction, multi_entry_key, is_multi_entry);
2870 return s;
2873 leveldb::Status IndexedDBBackingStore::DeleteIndex(
2874 IndexedDBBackingStore::Transaction* transaction,
2875 int64 database_id,
2876 int64 object_store_id,
2877 int64 index_id) {
2878 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2879 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2880 return InvalidDBKeyStatus();
2881 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2883 const std::string index_meta_data_start =
2884 IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0);
2885 const std::string index_meta_data_end =
2886 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2887 leveldb::Status s = DeleteRangeBasic(
2888 leveldb_transaction, index_meta_data_start, index_meta_data_end, true);
2890 if (s.ok()) {
2891 const std::string index_data_start =
2892 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
2893 const std::string index_data_end =
2894 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2895 s = DeleteRangeBasic(
2896 leveldb_transaction, index_data_start, index_data_end, true);
2899 if (!s.ok())
2900 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX);
2902 return s;
2905 leveldb::Status IndexedDBBackingStore::PutIndexDataForRecord(
2906 IndexedDBBackingStore::Transaction* transaction,
2907 int64 database_id,
2908 int64 object_store_id,
2909 int64 index_id,
2910 const IndexedDBKey& key,
2911 const RecordIdentifier& record_identifier) {
2912 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2913 DCHECK(key.IsValid());
2914 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2915 return InvalidDBKeyStatus();
2917 std::string encoded_key;
2918 EncodeIDBKey(key, &encoded_key);
2920 const std::string index_data_key =
2921 IndexDataKey::Encode(database_id,
2922 object_store_id,
2923 index_id,
2924 encoded_key,
2925 record_identifier.primary_key(),
2928 std::string data;
2929 EncodeVarInt(record_identifier.version(), &data);
2930 data.append(record_identifier.primary_key());
2932 transaction->transaction()->Put(index_data_key, &data);
2933 return leveldb::Status::OK();
2936 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
2937 const std::string& target,
2938 std::string* found_key,
2939 leveldb::Status* s) {
2940 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
2941 *s = it->Seek(target);
2942 if (!s->ok())
2943 return false;
2945 if (!it->IsValid()) {
2946 *s = it->SeekToLast();
2947 if (!s->ok() || !it->IsValid())
2948 return false;
2951 while (CompareIndexKeys(it->Key(), target) > 0) {
2952 *s = it->Prev();
2953 if (!s->ok() || !it->IsValid())
2954 return false;
2957 do {
2958 *found_key = it->Key().as_string();
2960 // There can be several index keys that compare equal. We want the last one.
2961 *s = it->Next();
2962 } while (s->ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
2964 return true;
2967 static leveldb::Status VersionExists(LevelDBTransaction* transaction,
2968 int64 database_id,
2969 int64 object_store_id,
2970 int64 version,
2971 const std::string& encoded_primary_key,
2972 bool* exists) {
2973 const std::string key =
2974 ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
2975 std::string data;
2977 leveldb::Status s = transaction->Get(key, &data, exists);
2978 if (!s.ok()) {
2979 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS);
2980 return s;
2982 if (!*exists)
2983 return s;
2985 StringPiece slice(data);
2986 int64 decoded;
2987 if (!DecodeInt(&slice, &decoded) || !slice.empty())
2988 return InternalInconsistencyStatus();
2989 *exists = (decoded == version);
2990 return s;
2993 leveldb::Status IndexedDBBackingStore::FindKeyInIndex(
2994 IndexedDBBackingStore::Transaction* transaction,
2995 int64 database_id,
2996 int64 object_store_id,
2997 int64 index_id,
2998 const IndexedDBKey& key,
2999 std::string* found_encoded_primary_key,
3000 bool* found) {
3001 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
3002 DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id));
3004 DCHECK(found_encoded_primary_key->empty());
3005 *found = false;
3007 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3008 const std::string leveldb_key =
3009 IndexDataKey::Encode(database_id, object_store_id, index_id, key);
3010 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
3011 leveldb::Status s = it->Seek(leveldb_key);
3012 if (!s.ok()) {
3013 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
3014 return s;
3017 for (;;) {
3018 if (!it->IsValid())
3019 return leveldb::Status::OK();
3020 if (CompareIndexKeys(it->Key(), leveldb_key) > 0)
3021 return leveldb::Status::OK();
3023 StringPiece slice(it->Value());
3025 int64 version;
3026 if (!DecodeVarInt(&slice, &version)) {
3027 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
3028 return InternalInconsistencyStatus();
3030 *found_encoded_primary_key = slice.as_string();
3032 bool exists = false;
3033 s = VersionExists(leveldb_transaction,
3034 database_id,
3035 object_store_id,
3036 version,
3037 *found_encoded_primary_key,
3038 &exists);
3039 if (!s.ok())
3040 return s;
3041 if (!exists) {
3042 // Delete stale index data entry and continue.
3043 leveldb_transaction->Remove(it->Key());
3044 s = it->Next();
3045 continue;
3047 *found = true;
3048 return s;
3052 leveldb::Status IndexedDBBackingStore::GetPrimaryKeyViaIndex(
3053 IndexedDBBackingStore::Transaction* transaction,
3054 int64 database_id,
3055 int64 object_store_id,
3056 int64 index_id,
3057 const IndexedDBKey& key,
3058 scoped_ptr<IndexedDBKey>* primary_key) {
3059 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
3060 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3061 return InvalidDBKeyStatus();
3063 bool found = false;
3064 std::string found_encoded_primary_key;
3065 leveldb::Status s = FindKeyInIndex(transaction,
3066 database_id,
3067 object_store_id,
3068 index_id,
3069 key,
3070 &found_encoded_primary_key,
3071 &found);
3072 if (!s.ok()) {
3073 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
3074 return s;
3076 if (!found)
3077 return s;
3078 if (!found_encoded_primary_key.size()) {
3079 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
3080 return InvalidDBKeyStatus();
3083 StringPiece slice(found_encoded_primary_key);
3084 if (DecodeIDBKey(&slice, primary_key) && slice.empty())
3085 return s;
3086 else
3087 return InvalidDBKeyStatus();
3090 leveldb::Status IndexedDBBackingStore::KeyExistsInIndex(
3091 IndexedDBBackingStore::Transaction* transaction,
3092 int64 database_id,
3093 int64 object_store_id,
3094 int64 index_id,
3095 const IndexedDBKey& index_key,
3096 scoped_ptr<IndexedDBKey>* found_primary_key,
3097 bool* exists) {
3098 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
3099 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3100 return InvalidDBKeyStatus();
3102 *exists = false;
3103 std::string found_encoded_primary_key;
3104 leveldb::Status s = FindKeyInIndex(transaction,
3105 database_id,
3106 object_store_id,
3107 index_id,
3108 index_key,
3109 &found_encoded_primary_key,
3110 exists);
3111 if (!s.ok()) {
3112 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
3113 return s;
3115 if (!*exists)
3116 return leveldb::Status::OK();
3117 if (found_encoded_primary_key.empty()) {
3118 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
3119 return InvalidDBKeyStatus();
3122 StringPiece slice(found_encoded_primary_key);
3123 if (DecodeIDBKey(&slice, found_primary_key) && slice.empty())
3124 return s;
3125 else
3126 return InvalidDBKeyStatus();
3129 IndexedDBBackingStore::Cursor::Cursor(
3130 const IndexedDBBackingStore::Cursor* other)
3131 : backing_store_(other->backing_store_),
3132 transaction_(other->transaction_),
3133 database_id_(other->database_id_),
3134 cursor_options_(other->cursor_options_),
3135 current_key_(new IndexedDBKey(*other->current_key_)) {
3136 if (other->iterator_) {
3137 iterator_ = transaction_->transaction()->CreateIterator();
3139 if (other->iterator_->IsValid()) {
3140 leveldb::Status s = iterator_->Seek(other->iterator_->Key());
3141 // TODO(cmumford): Handle this error (crbug.com/363397)
3142 DCHECK(iterator_->IsValid());
3147 IndexedDBBackingStore::Cursor::Cursor(
3148 scoped_refptr<IndexedDBBackingStore> backing_store,
3149 IndexedDBBackingStore::Transaction* transaction,
3150 int64 database_id,
3151 const CursorOptions& cursor_options)
3152 : backing_store_(backing_store.get()),
3153 transaction_(transaction),
3154 database_id_(database_id),
3155 cursor_options_(cursor_options) {
3157 IndexedDBBackingStore::Cursor::~Cursor() {}
3159 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) {
3160 iterator_ = transaction_->transaction()->CreateIterator();
3161 if (cursor_options_.forward)
3162 *s = iterator_->Seek(cursor_options_.low_key);
3163 else
3164 *s = iterator_->Seek(cursor_options_.high_key);
3165 if (!s->ok())
3166 return false;
3168 return Continue(0, READY, s);
3171 bool IndexedDBBackingStore::Cursor::Advance(uint32 count, leveldb::Status* s) {
3172 *s = leveldb::Status::OK();
3173 while (count--) {
3174 if (!Continue(s))
3175 return false;
3177 return true;
3180 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
3181 const IndexedDBKey* primary_key,
3182 IteratorState next_state,
3183 leveldb::Status* s) {
3184 DCHECK(!key || next_state == SEEK);
3186 if (cursor_options_.forward)
3187 return ContinueNext(key, primary_key, next_state, s);
3188 else
3189 return ContinuePrevious(key, primary_key, next_state, s);
3192 bool IndexedDBBackingStore::Cursor::ContinueNext(
3193 const IndexedDBKey* key,
3194 const IndexedDBKey* primary_key,
3195 IteratorState next_state,
3196 leveldb::Status* s) {
3197 DCHECK(cursor_options_.forward);
3198 DCHECK(!key || key->IsValid());
3199 DCHECK(!primary_key || primary_key->IsValid());
3200 *s = leveldb::Status::OK();
3202 // TODO(alecflett): avoid a copy here?
3203 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3205 // If seeking to a particular key (or key and primary key), skip the cursor
3206 // forward rather than iterating it.
3207 if (next_state == SEEK && key) {
3208 std::string leveldb_key =
3209 primary_key ? EncodeKey(*key, *primary_key) : EncodeKey(*key);
3210 *s = iterator_->Seek(leveldb_key);
3211 if (!s->ok())
3212 return false;
3213 // Cursor is at the next value already; don't advance it again below.
3214 next_state = READY;
3217 for (;;) {
3218 // Only advance the cursor if it was not set to position already, either
3219 // because it is newly opened (and positioned at start of range) or
3220 // skipped forward by continue with a specific key.
3221 if (next_state == SEEK) {
3222 *s = iterator_->Next();
3223 if (!s->ok())
3224 return false;
3225 } else {
3226 next_state = SEEK;
3229 // Fail if we've run out of data or gone past the cursor's bounds.
3230 if (!iterator_->IsValid() || IsPastBounds())
3231 return false;
3233 // TODO(jsbell): Document why this might be false. When do we ever not
3234 // seek into the range before starting cursor iteration?
3235 if (!HaveEnteredRange())
3236 continue;
3238 // The row may not load because there's a stale entry in the index. If no
3239 // error then not fatal.
3240 if (!LoadCurrentRow(s)) {
3241 if (!s->ok())
3242 return false;
3243 continue;
3246 // Cursor is now positioned at a non-stale record in range.
3248 // "Unique" cursors should continue seeking until a new key value is seen.
3249 if (cursor_options_.unique && previous_key.IsValid() &&
3250 current_key_->Equals(previous_key)) {
3251 continue;
3254 break;
3257 return true;
3260 bool IndexedDBBackingStore::Cursor::ContinuePrevious(
3261 const IndexedDBKey* key,
3262 const IndexedDBKey* primary_key,
3263 IteratorState next_state,
3264 leveldb::Status* s) {
3265 DCHECK(!cursor_options_.forward);
3266 DCHECK(!key || key->IsValid());
3267 DCHECK(!primary_key || primary_key->IsValid());
3268 *s = leveldb::Status::OK();
3270 // TODO(alecflett): avoid a copy here?
3271 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3273 // When iterating with PrevNoDuplicate, spec requires that the value we
3274 // yield for each key is the *first* duplicate in forwards order. We do this
3275 // by remembering the duplicate key (implicitly, the first record seen with
3276 // a new key), keeping track of the earliest duplicate seen, and continuing
3277 // until yet another new key is seen, at which point the earliest duplicate
3278 // is the correct cursor position.
3279 IndexedDBKey duplicate_key;
3280 std::string earliest_duplicate;
3282 // TODO(jsbell): Optimize continuing to a specific key (or key and primary
3283 // key) for reverse cursors as well. See Seek() optimization at the start of
3284 // ContinueNext() for an example.
3286 for (;;) {
3287 if (next_state == SEEK) {
3288 *s = iterator_->Prev();
3289 if (!s->ok())
3290 return false;
3291 } else {
3292 next_state = SEEK; // for subsequent iterations
3295 // If we've run out of data or gone past the cursor's bounds.
3296 if (!iterator_->IsValid() || IsPastBounds()) {
3297 if (duplicate_key.IsValid())
3298 break;
3299 return false;
3302 // TODO(jsbell): Document why this might be false. When do we ever not
3303 // seek into the range before starting cursor iteration?
3304 if (!HaveEnteredRange())
3305 continue;
3307 // The row may not load because there's a stale entry in the index. If no
3308 // error then not fatal.
3309 if (!LoadCurrentRow(s)) {
3310 if (!s->ok())
3311 return false;
3312 continue;
3315 // If seeking to a key (or key and primary key), continue until found.
3316 // TODO(jsbell): If Seek() optimization is added above, remove this.
3317 if (key) {
3318 if (primary_key && key->Equals(*current_key_) &&
3319 primary_key->IsLessThan(this->primary_key()))
3320 continue;
3321 if (key->IsLessThan(*current_key_))
3322 continue;
3325 // Cursor is now positioned at a non-stale record in range.
3327 if (cursor_options_.unique) {
3328 // If entry is a duplicate of the previous, keep going. Although the
3329 // cursor should be positioned at the first duplicate already, new
3330 // duplicates may have been inserted since the cursor was last iterated,
3331 // and should be skipped to maintain "unique" iteration.
3332 if (previous_key.IsValid() && current_key_->Equals(previous_key))
3333 continue;
3335 // If we've found a new key, remember it and keep going.
3336 if (!duplicate_key.IsValid()) {
3337 duplicate_key = *current_key_;
3338 earliest_duplicate = iterator_->Key().as_string();
3339 continue;
3342 // If we're still seeing duplicates, keep going.
3343 if (duplicate_key.Equals(*current_key_)) {
3344 earliest_duplicate = iterator_->Key().as_string();
3345 continue;
3349 break;
3352 if (cursor_options_.unique) {
3353 DCHECK(duplicate_key.IsValid());
3354 DCHECK(!earliest_duplicate.empty());
3356 *s = iterator_->Seek(earliest_duplicate);
3357 if (!s->ok())
3358 return false;
3359 if (!LoadCurrentRow(s)) {
3360 DCHECK(!s->ok());
3361 return false;
3365 return true;
3368 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3369 if (cursor_options_.forward) {
3370 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3371 if (cursor_options_.low_open) {
3372 return compare > 0;
3374 return compare >= 0;
3376 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3377 if (cursor_options_.high_open) {
3378 return compare < 0;
3380 return compare <= 0;
3383 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
3384 if (cursor_options_.forward) {
3385 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3386 if (cursor_options_.high_open) {
3387 return compare >= 0;
3389 return compare > 0;
3391 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3392 if (cursor_options_.low_open) {
3393 return compare <= 0;
3395 return compare < 0;
3398 const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const {
3399 return *current_key_;
3402 const IndexedDBBackingStore::RecordIdentifier&
3403 IndexedDBBackingStore::Cursor::record_identifier() const {
3404 return record_identifier_;
3407 class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3408 public:
3409 ObjectStoreKeyCursorImpl(
3410 scoped_refptr<IndexedDBBackingStore> backing_store,
3411 IndexedDBBackingStore::Transaction* transaction,
3412 int64 database_id,
3413 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3414 : IndexedDBBackingStore::Cursor(backing_store,
3415 transaction,
3416 database_id,
3417 cursor_options) {}
3419 Cursor* Clone() override { return new ObjectStoreKeyCursorImpl(this); }
3421 // IndexedDBBackingStore::Cursor
3422 IndexedDBValue* value() override {
3423 NOTREACHED();
3424 return NULL;
3426 bool LoadCurrentRow(leveldb::Status* s) override;
3428 protected:
3429 std::string EncodeKey(const IndexedDBKey& key) override {
3430 return ObjectStoreDataKey::Encode(
3431 cursor_options_.database_id, cursor_options_.object_store_id, key);
3433 std::string EncodeKey(const IndexedDBKey& key,
3434 const IndexedDBKey& primary_key) override {
3435 NOTREACHED();
3436 return std::string();
3439 private:
3440 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other)
3441 : IndexedDBBackingStore::Cursor(other) {}
3443 DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl);
3446 bool ObjectStoreKeyCursorImpl::LoadCurrentRow(leveldb::Status* s) {
3447 StringPiece slice(iterator_->Key());
3448 ObjectStoreDataKey object_store_data_key;
3449 if (!ObjectStoreDataKey::Decode(&slice, &object_store_data_key)) {
3450 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3451 *s = InvalidDBKeyStatus();
3452 return false;
3455 current_key_ = object_store_data_key.user_key();
3457 int64 version;
3458 slice = StringPiece(iterator_->Value());
3459 if (!DecodeVarInt(&slice, &version)) {
3460 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3461 *s = InternalInconsistencyStatus();
3462 return false;
3465 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3466 std::string encoded_key;
3467 EncodeIDBKey(*current_key_, &encoded_key);
3468 record_identifier_.Reset(encoded_key, version);
3470 return true;
3473 class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
3474 public:
3475 ObjectStoreCursorImpl(
3476 scoped_refptr<IndexedDBBackingStore> backing_store,
3477 IndexedDBBackingStore::Transaction* transaction,
3478 int64 database_id,
3479 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3480 : IndexedDBBackingStore::Cursor(backing_store,
3481 transaction,
3482 database_id,
3483 cursor_options) {}
3485 Cursor* Clone() override { return new ObjectStoreCursorImpl(this); }
3487 // IndexedDBBackingStore::Cursor
3488 IndexedDBValue* value() override { return &current_value_; }
3489 bool LoadCurrentRow(leveldb::Status* s) override;
3491 protected:
3492 std::string EncodeKey(const IndexedDBKey& key) override {
3493 return ObjectStoreDataKey::Encode(
3494 cursor_options_.database_id, cursor_options_.object_store_id, key);
3496 std::string EncodeKey(const IndexedDBKey& key,
3497 const IndexedDBKey& primary_key) override {
3498 NOTREACHED();
3499 return std::string();
3502 private:
3503 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other)
3504 : IndexedDBBackingStore::Cursor(other),
3505 current_value_(other->current_value_) {}
3507 IndexedDBValue current_value_;
3509 DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl);
3512 bool ObjectStoreCursorImpl::LoadCurrentRow(leveldb::Status* s) {
3513 StringPiece key_slice(iterator_->Key());
3514 ObjectStoreDataKey object_store_data_key;
3515 if (!ObjectStoreDataKey::Decode(&key_slice, &object_store_data_key)) {
3516 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3517 *s = InvalidDBKeyStatus();
3518 return false;
3521 current_key_ = object_store_data_key.user_key();
3523 int64 version;
3524 StringPiece value_slice = StringPiece(iterator_->Value());
3525 if (!DecodeVarInt(&value_slice, &version)) {
3526 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3527 *s = InternalInconsistencyStatus();
3528 return false;
3531 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3532 std::string encoded_key;
3533 EncodeIDBKey(*current_key_, &encoded_key);
3534 record_identifier_.Reset(encoded_key, version);
3536 *s = transaction_->GetBlobInfoForRecord(
3537 database_id_, iterator_->Key().as_string(), &current_value_);
3538 if (!s->ok())
3539 return false;
3541 current_value_.bits = value_slice.as_string();
3542 return true;
3545 class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3546 public:
3547 IndexKeyCursorImpl(
3548 scoped_refptr<IndexedDBBackingStore> backing_store,
3549 IndexedDBBackingStore::Transaction* transaction,
3550 int64 database_id,
3551 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3552 : IndexedDBBackingStore::Cursor(backing_store,
3553 transaction,
3554 database_id,
3555 cursor_options) {}
3557 Cursor* Clone() override { return new IndexKeyCursorImpl(this); }
3559 // IndexedDBBackingStore::Cursor
3560 IndexedDBValue* value() override {
3561 NOTREACHED();
3562 return NULL;
3564 const IndexedDBKey& primary_key() const override { return *primary_key_; }
3565 const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3566 const override {
3567 NOTREACHED();
3568 return record_identifier_;
3570 bool LoadCurrentRow(leveldb::Status* s) override;
3572 protected:
3573 std::string EncodeKey(const IndexedDBKey& key) override {
3574 return IndexDataKey::Encode(cursor_options_.database_id,
3575 cursor_options_.object_store_id,
3576 cursor_options_.index_id,
3577 key);
3579 std::string EncodeKey(const IndexedDBKey& key,
3580 const IndexedDBKey& primary_key) override {
3581 return IndexDataKey::Encode(cursor_options_.database_id,
3582 cursor_options_.object_store_id,
3583 cursor_options_.index_id,
3584 key,
3585 primary_key);
3588 private:
3589 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl* other)
3590 : IndexedDBBackingStore::Cursor(other),
3591 primary_key_(new IndexedDBKey(*other->primary_key_)) {}
3593 scoped_ptr<IndexedDBKey> primary_key_;
3595 DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl);
3598 bool IndexKeyCursorImpl::LoadCurrentRow(leveldb::Status* s) {
3599 StringPiece slice(iterator_->Key());
3600 IndexDataKey index_data_key;
3601 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3602 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3603 *s = InvalidDBKeyStatus();
3604 return false;
3607 current_key_ = index_data_key.user_key();
3608 DCHECK(current_key_);
3610 slice = StringPiece(iterator_->Value());
3611 int64 index_data_version;
3612 if (!DecodeVarInt(&slice, &index_data_version)) {
3613 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3614 *s = InternalInconsistencyStatus();
3615 return false;
3618 if (!DecodeIDBKey(&slice, &primary_key_) || !slice.empty()) {
3619 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3620 *s = InternalInconsistencyStatus();
3621 return false;
3624 std::string primary_leveldb_key =
3625 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3626 index_data_key.ObjectStoreId(),
3627 *primary_key_);
3629 std::string result;
3630 bool found = false;
3631 *s = transaction_->transaction()->Get(primary_leveldb_key, &result, &found);
3632 if (!s->ok()) {
3633 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3634 return false;
3636 if (!found) {
3637 transaction_->transaction()->Remove(iterator_->Key());
3638 return false;
3640 if (!result.size()) {
3641 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3642 return false;
3645 int64 object_store_data_version;
3646 slice = StringPiece(result);
3647 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3648 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3649 *s = InternalInconsistencyStatus();
3650 return false;
3653 if (object_store_data_version != index_data_version) {
3654 transaction_->transaction()->Remove(iterator_->Key());
3655 return false;
3658 return true;
3661 class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
3662 public:
3663 IndexCursorImpl(
3664 scoped_refptr<IndexedDBBackingStore> backing_store,
3665 IndexedDBBackingStore::Transaction* transaction,
3666 int64 database_id,
3667 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3668 : IndexedDBBackingStore::Cursor(backing_store,
3669 transaction,
3670 database_id,
3671 cursor_options) {}
3673 Cursor* Clone() override { return new IndexCursorImpl(this); }
3675 // IndexedDBBackingStore::Cursor
3676 IndexedDBValue* value() override { return &current_value_; }
3677 const IndexedDBKey& primary_key() const override { return *primary_key_; }
3678 const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3679 const override {
3680 NOTREACHED();
3681 return record_identifier_;
3683 bool LoadCurrentRow(leveldb::Status* s) override;
3685 protected:
3686 std::string EncodeKey(const IndexedDBKey& key) override {
3687 return IndexDataKey::Encode(cursor_options_.database_id,
3688 cursor_options_.object_store_id,
3689 cursor_options_.index_id,
3690 key);
3692 std::string EncodeKey(const IndexedDBKey& key,
3693 const IndexedDBKey& primary_key) override {
3694 return IndexDataKey::Encode(cursor_options_.database_id,
3695 cursor_options_.object_store_id,
3696 cursor_options_.index_id,
3697 key,
3698 primary_key);
3701 private:
3702 explicit IndexCursorImpl(const IndexCursorImpl* other)
3703 : IndexedDBBackingStore::Cursor(other),
3704 primary_key_(new IndexedDBKey(*other->primary_key_)),
3705 current_value_(other->current_value_),
3706 primary_leveldb_key_(other->primary_leveldb_key_) {}
3708 scoped_ptr<IndexedDBKey> primary_key_;
3709 IndexedDBValue current_value_;
3710 std::string primary_leveldb_key_;
3712 DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl);
3715 bool IndexCursorImpl::LoadCurrentRow(leveldb::Status* s) {
3716 StringPiece slice(iterator_->Key());
3717 IndexDataKey index_data_key;
3718 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3719 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3720 *s = InvalidDBKeyStatus();
3721 return false;
3724 current_key_ = index_data_key.user_key();
3725 DCHECK(current_key_);
3727 slice = StringPiece(iterator_->Value());
3728 int64 index_data_version;
3729 if (!DecodeVarInt(&slice, &index_data_version)) {
3730 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3731 *s = InternalInconsistencyStatus();
3732 return false;
3734 if (!DecodeIDBKey(&slice, &primary_key_)) {
3735 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3736 *s = InvalidDBKeyStatus();
3737 return false;
3740 DCHECK_EQ(index_data_key.DatabaseId(), database_id_);
3741 primary_leveldb_key_ =
3742 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3743 index_data_key.ObjectStoreId(),
3744 *primary_key_);
3746 std::string result;
3747 bool found = false;
3748 *s = transaction_->transaction()->Get(primary_leveldb_key_, &result, &found);
3749 if (!s->ok()) {
3750 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3751 return false;
3753 if (!found) {
3754 transaction_->transaction()->Remove(iterator_->Key());
3755 return false;
3757 if (!result.size()) {
3758 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3759 return false;
3762 int64 object_store_data_version;
3763 slice = StringPiece(result);
3764 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3765 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3766 *s = InternalInconsistencyStatus();
3767 return false;
3770 if (object_store_data_version != index_data_version) {
3771 transaction_->transaction()->Remove(iterator_->Key());
3772 return false;
3775 current_value_.bits = slice.as_string();
3776 *s = transaction_->GetBlobInfoForRecord(database_id_, primary_leveldb_key_,
3777 &current_value_);
3778 return s->ok();
3781 bool ObjectStoreCursorOptions(
3782 LevelDBTransaction* transaction,
3783 int64 database_id,
3784 int64 object_store_id,
3785 const IndexedDBKeyRange& range,
3786 blink::WebIDBCursorDirection direction,
3787 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3788 cursor_options->database_id = database_id;
3789 cursor_options->object_store_id = object_store_id;
3791 bool lower_bound = range.lower().IsValid();
3792 bool upper_bound = range.upper().IsValid();
3793 cursor_options->forward =
3794 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3795 direction == blink::WebIDBCursorDirectionNext);
3796 cursor_options->unique =
3797 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3798 direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
3800 if (!lower_bound) {
3801 cursor_options->low_key =
3802 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
3803 cursor_options->low_open = true; // Not included.
3804 } else {
3805 cursor_options->low_key =
3806 ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower());
3807 cursor_options->low_open = range.lower_open();
3810 leveldb::Status s;
3812 if (!upper_bound) {
3813 cursor_options->high_key =
3814 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
3816 if (cursor_options->forward) {
3817 cursor_options->high_open = true; // Not included.
3818 } else {
3819 // We need a key that exists.
3820 // TODO(cmumford): Handle this error (crbug.com/363397)
3821 if (!FindGreatestKeyLessThanOrEqual(transaction,
3822 cursor_options->high_key,
3823 &cursor_options->high_key,
3824 &s))
3825 return false;
3826 cursor_options->high_open = false;
3828 } else {
3829 cursor_options->high_key =
3830 ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper());
3831 cursor_options->high_open = range.upper_open();
3833 if (!cursor_options->forward) {
3834 // For reverse cursors, we need a key that exists.
3835 std::string found_high_key;
3836 // TODO(cmumford): Handle this error (crbug.com/363397)
3837 if (!FindGreatestKeyLessThanOrEqual(
3838 transaction, cursor_options->high_key, &found_high_key, &s))
3839 return false;
3841 // If the target key should not be included, but we end up with a smaller
3842 // key, we should include that.
3843 if (cursor_options->high_open &&
3844 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3845 cursor_options->high_open = false;
3847 cursor_options->high_key = found_high_key;
3851 return true;
3854 bool IndexCursorOptions(
3855 LevelDBTransaction* transaction,
3856 int64 database_id,
3857 int64 object_store_id,
3858 int64 index_id,
3859 const IndexedDBKeyRange& range,
3860 blink::WebIDBCursorDirection direction,
3861 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3862 DCHECK(transaction);
3863 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3864 return false;
3866 cursor_options->database_id = database_id;
3867 cursor_options->object_store_id = object_store_id;
3868 cursor_options->index_id = index_id;
3870 bool lower_bound = range.lower().IsValid();
3871 bool upper_bound = range.upper().IsValid();
3872 cursor_options->forward =
3873 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3874 direction == blink::WebIDBCursorDirectionNext);
3875 cursor_options->unique =
3876 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3877 direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
3879 if (!lower_bound) {
3880 cursor_options->low_key =
3881 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
3882 cursor_options->low_open = false; // Included.
3883 } else {
3884 cursor_options->low_key = IndexDataKey::Encode(
3885 database_id, object_store_id, index_id, range.lower());
3886 cursor_options->low_open = range.lower_open();
3889 leveldb::Status s;
3891 if (!upper_bound) {
3892 cursor_options->high_key =
3893 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
3894 cursor_options->high_open = false; // Included.
3896 if (!cursor_options->forward) { // We need a key that exists.
3897 if (!FindGreatestKeyLessThanOrEqual(transaction,
3898 cursor_options->high_key,
3899 &cursor_options->high_key,
3900 &s))
3901 return false;
3902 cursor_options->high_open = false;
3904 } else {
3905 cursor_options->high_key = IndexDataKey::Encode(
3906 database_id, object_store_id, index_id, range.upper());
3907 cursor_options->high_open = range.upper_open();
3909 std::string found_high_key;
3910 // Seek to the *last* key in the set of non-unique keys
3911 // TODO(cmumford): Handle this error (crbug.com/363397)
3912 if (!FindGreatestKeyLessThanOrEqual(
3913 transaction, cursor_options->high_key, &found_high_key, &s))
3914 return false;
3916 // If the target key should not be included, but we end up with a smaller
3917 // key, we should include that.
3918 if (cursor_options->high_open &&
3919 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3920 cursor_options->high_open = false;
3922 cursor_options->high_key = found_high_key;
3925 return true;
3928 scoped_ptr<IndexedDBBackingStore::Cursor>
3929 IndexedDBBackingStore::OpenObjectStoreCursor(
3930 IndexedDBBackingStore::Transaction* transaction,
3931 int64 database_id,
3932 int64 object_store_id,
3933 const IndexedDBKeyRange& range,
3934 blink::WebIDBCursorDirection direction,
3935 leveldb::Status* s) {
3936 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3937 *s = leveldb::Status::OK();
3938 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3939 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3940 if (!ObjectStoreCursorOptions(leveldb_transaction,
3941 database_id,
3942 object_store_id,
3943 range,
3944 direction,
3945 &cursor_options))
3946 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3947 scoped_ptr<ObjectStoreCursorImpl> cursor(new ObjectStoreCursorImpl(
3948 this, transaction, database_id, cursor_options));
3949 if (!cursor->FirstSeek(s))
3950 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3952 return cursor.Pass();
3955 scoped_ptr<IndexedDBBackingStore::Cursor>
3956 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3957 IndexedDBBackingStore::Transaction* transaction,
3958 int64 database_id,
3959 int64 object_store_id,
3960 const IndexedDBKeyRange& range,
3961 blink::WebIDBCursorDirection direction,
3962 leveldb::Status* s) {
3963 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3964 *s = leveldb::Status::OK();
3965 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3966 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3967 if (!ObjectStoreCursorOptions(leveldb_transaction,
3968 database_id,
3969 object_store_id,
3970 range,
3971 direction,
3972 &cursor_options))
3973 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3974 scoped_ptr<ObjectStoreKeyCursorImpl> cursor(new ObjectStoreKeyCursorImpl(
3975 this, transaction, database_id, cursor_options));
3976 if (!cursor->FirstSeek(s))
3977 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3979 return cursor.Pass();
3982 scoped_ptr<IndexedDBBackingStore::Cursor>
3983 IndexedDBBackingStore::OpenIndexKeyCursor(
3984 IndexedDBBackingStore::Transaction* transaction,
3985 int64 database_id,
3986 int64 object_store_id,
3987 int64 index_id,
3988 const IndexedDBKeyRange& range,
3989 blink::WebIDBCursorDirection direction,
3990 leveldb::Status* s) {
3991 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3992 *s = leveldb::Status::OK();
3993 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3994 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3995 if (!IndexCursorOptions(leveldb_transaction,
3996 database_id,
3997 object_store_id,
3998 index_id,
3999 range,
4000 direction,
4001 &cursor_options))
4002 return scoped_ptr<IndexedDBBackingStore::Cursor>();
4003 scoped_ptr<IndexKeyCursorImpl> cursor(
4004 new IndexKeyCursorImpl(this, transaction, database_id, cursor_options));
4005 if (!cursor->FirstSeek(s))
4006 return scoped_ptr<IndexedDBBackingStore::Cursor>();
4008 return cursor.Pass();
4011 scoped_ptr<IndexedDBBackingStore::Cursor>
4012 IndexedDBBackingStore::OpenIndexCursor(
4013 IndexedDBBackingStore::Transaction* transaction,
4014 int64 database_id,
4015 int64 object_store_id,
4016 int64 index_id,
4017 const IndexedDBKeyRange& range,
4018 blink::WebIDBCursorDirection direction,
4019 leveldb::Status* s) {
4020 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
4021 LevelDBTransaction* leveldb_transaction = transaction->transaction();
4022 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
4023 if (!IndexCursorOptions(leveldb_transaction,
4024 database_id,
4025 object_store_id,
4026 index_id,
4027 range,
4028 direction,
4029 &cursor_options))
4030 return scoped_ptr<IndexedDBBackingStore::Cursor>();
4031 scoped_ptr<IndexCursorImpl> cursor(
4032 new IndexCursorImpl(this, transaction, database_id, cursor_options));
4033 if (!cursor->FirstSeek(s))
4034 return scoped_ptr<IndexedDBBackingStore::Cursor>();
4036 return cursor.Pass();
4039 IndexedDBBackingStore::Transaction::Transaction(
4040 IndexedDBBackingStore* backing_store)
4041 : backing_store_(backing_store), database_id_(-1), committing_(false) {
4044 IndexedDBBackingStore::Transaction::~Transaction() {
4045 STLDeleteContainerPairSecondPointers(
4046 blob_change_map_.begin(), blob_change_map_.end());
4047 STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
4048 incognito_blob_map_.end());
4049 DCHECK(!committing_);
4052 void IndexedDBBackingStore::Transaction::Begin() {
4053 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
4054 DCHECK(!transaction_.get());
4055 transaction_ = IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4056 backing_store_->db_.get());
4058 // If incognito, this snapshots blobs just as the above transaction_
4059 // constructor snapshots the leveldb.
4060 for (const auto& iter : backing_store_->incognito_blob_map_)
4061 incognito_blob_map_[iter.first] = iter.second->Clone().release();
4064 static GURL GetURLFromUUID(const string& uuid) {
4065 return GURL("blob:uuid/" + uuid);
4068 leveldb::Status IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
4069 BlobEntryKeyValuePairVec* new_blob_entries,
4070 WriteDescriptorVec* new_files_to_write) {
4071 if (backing_store_->is_incognito())
4072 return leveldb::Status::OK();
4074 DCHECK(new_blob_entries->empty());
4075 DCHECK(new_files_to_write->empty());
4076 DCHECK(blobs_to_write_.empty());
4078 if (blob_change_map_.empty())
4079 return leveldb::Status::OK();
4081 // Create LevelDBTransaction for the name generator seed and add-journal.
4082 scoped_refptr<LevelDBTransaction> pre_transaction =
4083 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4084 backing_store_->db_.get());
4086 for (auto& iter : blob_change_map_) {
4087 std::vector<IndexedDBBlobInfo*> new_blob_keys;
4088 for (auto& entry : iter.second->mutable_blob_info()) {
4089 int64 next_blob_key = -1;
4090 bool result = GetBlobKeyGeneratorCurrentNumber(
4091 pre_transaction.get(), database_id_, &next_blob_key);
4092 if (!result || next_blob_key < 0)
4093 return InternalInconsistencyStatus();
4094 blobs_to_write_.push_back(std::make_pair(database_id_, next_blob_key));
4095 if (entry.is_file() && !entry.file_path().empty()) {
4096 new_files_to_write->push_back(
4097 WriteDescriptor(entry.file_path(), next_blob_key, entry.size(),
4098 entry.last_modified()));
4099 } else {
4100 new_files_to_write->push_back(
4101 WriteDescriptor(GetURLFromUUID(entry.uuid()), next_blob_key,
4102 entry.size(), entry.last_modified()));
4104 entry.set_key(next_blob_key);
4105 new_blob_keys.push_back(&entry);
4106 result = UpdateBlobKeyGeneratorCurrentNumber(
4107 pre_transaction.get(), database_id_, next_blob_key + 1);
4108 if (!result)
4109 return InternalInconsistencyStatus();
4111 BlobEntryKey blob_entry_key;
4112 StringPiece key_piece(iter.second->key());
4113 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
4114 NOTREACHED();
4115 return InternalInconsistencyStatus();
4117 new_blob_entries->push_back(
4118 std::make_pair(blob_entry_key, EncodeBlobData(new_blob_keys)));
4121 AppendBlobsToPrimaryBlobJournal(pre_transaction.get(), blobs_to_write_);
4122 leveldb::Status s = pre_transaction->Commit();
4123 if (!s.ok())
4124 return InternalInconsistencyStatus();
4126 return leveldb::Status::OK();
4129 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
4130 if (backing_store_->is_incognito())
4131 return true;
4133 // Look up all old files to remove as part of the transaction, store their
4134 // names in blobs_to_remove_, and remove their old blob data entries.
4135 for (const auto& iter : blob_change_map_) {
4136 BlobEntryKey blob_entry_key;
4137 StringPiece key_piece(iter.second->key());
4138 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
4139 NOTREACHED();
4140 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4141 transaction_ = NULL;
4142 return false;
4144 if (database_id_ < 0)
4145 database_id_ = blob_entry_key.database_id();
4146 else
4147 DCHECK_EQ(database_id_, blob_entry_key.database_id());
4148 std::string blob_entry_key_bytes = blob_entry_key.Encode();
4149 bool found;
4150 std::string blob_entry_value_bytes;
4151 leveldb::Status s = transaction_->Get(
4152 blob_entry_key_bytes, &blob_entry_value_bytes, &found);
4153 if (s.ok() && found) {
4154 std::vector<IndexedDBBlobInfo> blob_info;
4155 if (!DecodeBlobData(blob_entry_value_bytes, &blob_info)) {
4156 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4157 transaction_ = NULL;
4158 return false;
4160 for (const auto& blob : blob_info) {
4161 blobs_to_remove_.push_back(std::make_pair(database_id_, blob.key()));
4162 transaction_->Remove(blob_entry_key_bytes);
4166 return true;
4169 void IndexedDBBackingStore::Transaction::PartitionBlobsToRemove(
4170 BlobJournalType* dead_blobs,
4171 BlobJournalType* live_blobs) const {
4172 IndexedDBActiveBlobRegistry* registry =
4173 backing_store_->active_blob_registry();
4174 for (const auto& iter : blobs_to_remove_) {
4175 if (registry->MarkDeletedCheckIfUsed(iter.first, iter.second))
4176 live_blobs->push_back(iter);
4177 else
4178 dead_blobs->push_back(iter);
4182 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne(
4183 scoped_refptr<BlobWriteCallback> callback) {
4184 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
4185 DCHECK(transaction_.get());
4186 DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
4188 leveldb::Status s;
4190 BlobEntryKeyValuePairVec new_blob_entries;
4191 WriteDescriptorVec new_files_to_write;
4192 s = HandleBlobPreTransaction(&new_blob_entries, &new_files_to_write);
4193 if (!s.ok()) {
4194 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4195 transaction_ = NULL;
4196 return s;
4199 DCHECK(!new_files_to_write.size() ||
4200 KeyPrefix::IsValidDatabaseId(database_id_));
4201 if (!CollectBlobFilesToRemove()) {
4202 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4203 transaction_ = NULL;
4204 return InternalInconsistencyStatus();
4207 committing_ = true;
4208 ++backing_store_->committing_transaction_count_;
4210 if (!new_files_to_write.empty()) {
4211 // This kicks off the writes of the new blobs, if any.
4212 // This call will zero out new_blob_entries and new_files_to_write.
4213 WriteNewBlobs(&new_blob_entries, &new_files_to_write, callback);
4214 } else {
4215 callback->Run(true);
4218 return leveldb::Status::OK();
4221 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
4222 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
4223 leveldb::Status s;
4225 DCHECK(committing_);
4226 committing_ = false;
4227 DCHECK_GT(backing_store_->committing_transaction_count_, 0UL);
4228 --backing_store_->committing_transaction_count_;
4230 BlobJournalType primary_journal, live_journal, saved_primary_journal,
4231 dead_blobs;
4232 if (!blob_change_map_.empty()) {
4233 IDB_TRACE("IndexedDBBackingStore::Transaction.BlobJournal");
4234 // Read the persisted states of the primary/live blob journals,
4235 // so that they can be updated correctly by the transaction.
4236 scoped_refptr<LevelDBTransaction> journal_transaction =
4237 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4238 backing_store_->db_.get());
4239 s = GetPrimaryBlobJournal(journal_transaction.get(), &primary_journal);
4240 if (!s.ok())
4241 return s;
4242 s = GetLiveBlobJournal(journal_transaction.get(), &live_journal);
4243 if (!s.ok())
4244 return s;
4246 // Remove newly added blobs from the journal - they will be accounted
4247 // for in blob entry tables in the transaction.
4248 std::sort(primary_journal.begin(), primary_journal.end());
4249 std::sort(blobs_to_write_.begin(), blobs_to_write_.end());
4250 BlobJournalType new_journal = base::STLSetDifference<BlobJournalType>(
4251 primary_journal, blobs_to_write_);
4252 primary_journal.swap(new_journal);
4254 // Append newly deleted blobs to appropriate primary/live journals.
4255 saved_primary_journal = primary_journal;
4256 BlobJournalType live_blobs;
4257 if (!blobs_to_remove_.empty()) {
4258 DCHECK(!backing_store_->is_incognito());
4259 PartitionBlobsToRemove(&dead_blobs, &live_blobs);
4261 primary_journal.insert(primary_journal.end(), dead_blobs.begin(),
4262 dead_blobs.end());
4263 live_journal.insert(live_journal.end(), live_blobs.begin(),
4264 live_blobs.end());
4265 UpdatePrimaryBlobJournal(transaction_.get(), primary_journal);
4266 UpdateLiveBlobJournal(transaction_.get(), live_journal);
4269 // Actually commit. If this succeeds, the journals will appropriately
4270 // reflect pending blob work - dead files that should be deleted
4271 // immediately, and live files to monitor.
4272 s = transaction_->Commit();
4273 transaction_ = NULL;
4275 if (!s.ok()) {
4276 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
4277 return s;
4280 if (backing_store_->is_incognito()) {
4281 if (!blob_change_map_.empty()) {
4282 BlobChangeMap& target_map = backing_store_->incognito_blob_map_;
4283 for (auto& iter : blob_change_map_) {
4284 BlobChangeMap::iterator target_record = target_map.find(iter.first);
4285 if (target_record != target_map.end()) {
4286 delete target_record->second;
4287 target_map.erase(target_record);
4289 if (iter.second) {
4290 target_map[iter.first] = iter.second;
4291 iter.second = NULL;
4295 return leveldb::Status::OK();
4298 // Actually delete dead blob files, then remove those entries
4299 // from the persisted primary journal.
4300 if (dead_blobs.empty())
4301 return leveldb::Status::OK();
4303 DCHECK(!blob_change_map_.empty());
4305 s = backing_store_->CleanUpBlobJournalEntries(dead_blobs);
4306 if (!s.ok()) {
4307 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4308 return s;
4311 scoped_refptr<LevelDBTransaction> update_journal_transaction =
4312 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4313 backing_store_->db_.get());
4314 UpdatePrimaryBlobJournal(update_journal_transaction.get(),
4315 saved_primary_journal);
4316 s = update_journal_transaction->Commit();
4317 return s;
4321 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
4322 : public IndexedDBBackingStore::BlobWriteCallback {
4323 public:
4324 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction,
4325 scoped_refptr<BlobWriteCallback> callback)
4326 : transaction_(transaction), callback_(callback) {}
4327 void Run(bool succeeded) override {
4328 IDB_ASYNC_TRACE_END("IndexedDBBackingStore::Transaction::WriteNewBlobs",
4329 transaction_);
4330 callback_->Run(succeeded);
4331 if (succeeded) // Else it's already been deleted during rollback.
4332 transaction_->chained_blob_writer_ = NULL;
4335 private:
4336 ~BlobWriteCallbackWrapper() override {}
4337 friend class base::RefCounted<IndexedDBBackingStore::BlobWriteCallback>;
4339 IndexedDBBackingStore::Transaction* transaction_;
4340 scoped_refptr<BlobWriteCallback> callback_;
4342 DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper);
4345 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
4346 BlobEntryKeyValuePairVec* new_blob_entries,
4347 WriteDescriptorVec* new_files_to_write,
4348 scoped_refptr<BlobWriteCallback> callback) {
4349 IDB_ASYNC_TRACE_BEGIN("IndexedDBBackingStore::Transaction::WriteNewBlobs",
4350 this);
4351 DCHECK_GT(new_files_to_write->size(), 0UL);
4352 DCHECK_GT(database_id_, 0);
4353 for (auto& blob_entry_iter : *new_blob_entries) {
4354 // Add the new blob-table entry for each blob to the main transaction, or
4355 // remove any entry that may exist if there's no new one.
4356 if (blob_entry_iter.second.empty())
4357 transaction_->Remove(blob_entry_iter.first.Encode());
4358 else
4359 transaction_->Put(blob_entry_iter.first.Encode(),
4360 &blob_entry_iter.second);
4362 // Creating the writer will start it going asynchronously.
4363 chained_blob_writer_ =
4364 new ChainedBlobWriterImpl(database_id_,
4365 backing_store_,
4366 new_files_to_write,
4367 new BlobWriteCallbackWrapper(this, callback));
4370 void IndexedDBBackingStore::Transaction::Rollback() {
4371 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
4372 if (committing_) {
4373 committing_ = false;
4374 DCHECK_GT(backing_store_->committing_transaction_count_, 0UL);
4375 --backing_store_->committing_transaction_count_;
4378 if (chained_blob_writer_.get()) {
4379 chained_blob_writer_->Abort();
4380 chained_blob_writer_ = NULL;
4382 if (transaction_.get() == NULL)
4383 return;
4384 transaction_->Rollback();
4385 transaction_ = NULL;
4388 IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
4389 const std::string& key,
4390 int64 object_store_id)
4391 : key_(key), object_store_id_(object_store_id) {
4394 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
4397 void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
4398 std::vector<IndexedDBBlobInfo>* blob_info) {
4399 blob_info_.clear();
4400 if (blob_info)
4401 blob_info_.swap(*blob_info);
4404 void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
4405 ScopedVector<storage::BlobDataHandle>* handles) {
4406 handles_.clear();
4407 if (handles)
4408 handles_.swap(*handles);
4411 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord>
4412 IndexedDBBackingStore::BlobChangeRecord::Clone() const {
4413 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord> record(
4414 new BlobChangeRecord(key_, object_store_id_));
4415 record->blob_info_ = blob_info_;
4417 for (const auto* handle : handles_)
4418 record->handles_.push_back(new storage::BlobDataHandle(*handle));
4419 return record.Pass();
4422 leveldb::Status IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
4423 int64 database_id,
4424 int64 object_store_id,
4425 const std::string& object_store_data_key,
4426 std::vector<IndexedDBBlobInfo>* blob_info,
4427 ScopedVector<storage::BlobDataHandle>* handles) {
4428 if (!blob_info || blob_info->empty()) {
4429 blob_change_map_.erase(object_store_data_key);
4430 incognito_blob_map_.erase(object_store_data_key);
4432 BlobEntryKey blob_entry_key;
4433 StringPiece leveldb_key_piece(object_store_data_key);
4434 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
4435 &blob_entry_key)) {
4436 NOTREACHED();
4437 return InternalInconsistencyStatus();
4439 std::string value;
4440 bool found = false;
4441 leveldb::Status s =
4442 transaction()->Get(blob_entry_key.Encode(), &value, &found);
4443 if (!s.ok())
4444 return s;
4445 if (!found)
4446 return leveldb::Status::OK();
4448 PutBlobInfo(
4449 database_id, object_store_id, object_store_data_key, blob_info, handles);
4450 return leveldb::Status::OK();
4453 // This is storing an info, even if empty, even if the previous key had no blob
4454 // info that we know of. It duplicates a bunch of information stored in the
4455 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
4456 // changes to exists or index keys here.
4457 void IndexedDBBackingStore::Transaction::PutBlobInfo(
4458 int64 database_id,
4459 int64 object_store_id,
4460 const std::string& object_store_data_key,
4461 std::vector<IndexedDBBlobInfo>* blob_info,
4462 ScopedVector<storage::BlobDataHandle>* handles) {
4463 DCHECK_GT(object_store_data_key.size(), 0UL);
4464 if (database_id_ < 0)
4465 database_id_ = database_id;
4466 DCHECK_EQ(database_id_, database_id);
4468 BlobChangeMap::iterator it = blob_change_map_.find(object_store_data_key);
4469 BlobChangeRecord* record = NULL;
4470 if (it == blob_change_map_.end()) {
4471 record = new BlobChangeRecord(object_store_data_key, object_store_id);
4472 blob_change_map_[object_store_data_key] = record;
4473 } else {
4474 record = it->second;
4476 DCHECK_EQ(record->object_store_id(), object_store_id);
4477 record->SetBlobInfo(blob_info);
4478 record->SetHandles(handles);
4479 DCHECK(!handles || !handles->size());
4482 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4483 const GURL& url,
4484 int64_t key,
4485 int64_t size,
4486 base::Time last_modified)
4487 : is_file_(false),
4488 url_(url),
4489 key_(key),
4490 size_(size),
4491 last_modified_(last_modified) {
4494 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4495 const FilePath& file_path,
4496 int64_t key,
4497 int64_t size,
4498 base::Time last_modified)
4499 : is_file_(true),
4500 file_path_(file_path),
4501 key_(key),
4502 size_(size),
4503 last_modified_(last_modified) {
4506 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4507 const WriteDescriptor& other) = default;
4508 IndexedDBBackingStore::Transaction::WriteDescriptor::~WriteDescriptor() =
4509 default;
4510 IndexedDBBackingStore::Transaction::WriteDescriptor&
4511 IndexedDBBackingStore::Transaction::WriteDescriptor::
4512 operator=(const WriteDescriptor& other) = default;
4514 } // namespace content