Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_backing_store.cc
blob5000e507fbbc971af60c28c17c1c8f134aca4a19
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/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 std::string data;
569 bool found = false;
570 leveldb::Status s = transaction->Get(key, &data, &found);
571 if (!s.ok()) {
572 INTERNAL_READ_ERROR(READ_BLOB_JOURNAL);
573 return s;
575 journal->clear();
576 if (!found || data.empty())
577 return leveldb::Status::OK();
578 StringPiece slice(data);
579 if (!DecodeBlobJournal(&slice, journal)) {
580 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DECODE_BLOB_JOURNAL);
581 s = InternalInconsistencyStatus();
583 return s;
586 template <typename TransactionType>
587 static leveldb::Status GetPrimaryBlobJournal(TransactionType* transaction,
588 BlobJournalType* journal) {
589 return GetBlobJournal(BlobJournalKey::Encode(), transaction, journal);
592 template <typename TransactionType>
593 static leveldb::Status GetLiveBlobJournal(TransactionType* transaction,
594 BlobJournalType* journal) {
595 return GetBlobJournal(LiveBlobJournalKey::Encode(), transaction, journal);
598 // Clear the specified blob journal via the supplied transaction.
599 // The key must be either the primary journal key or live journal key.
600 template <typename TransactionType>
601 static void ClearBlobJournal(TransactionType* transaction,
602 const std::string& key) {
603 transaction->Remove(key);
606 // Overwrite the specified blob journal via the supplied transaction.
607 // The key must be either the primary journal key or live journal key.
608 template <typename TransactionType>
609 static void UpdateBlobJournal(TransactionType* transaction,
610 const std::string& key,
611 const BlobJournalType& journal) {
612 std::string data;
613 EncodeBlobJournal(journal, &data);
614 transaction->Put(key, &data);
617 template <typename TransactionType>
618 static void UpdatePrimaryBlobJournal(TransactionType* transaction,
619 const BlobJournalType& journal) {
620 UpdateBlobJournal(transaction, BlobJournalKey::Encode(), journal);
623 template <typename TransactionType>
624 static void UpdateLiveBlobJournal(TransactionType* transaction,
625 const BlobJournalType& journal) {
626 UpdateBlobJournal(transaction, LiveBlobJournalKey::Encode(), journal);
629 // Append blobs to the specified blob journal via the supplied transaction.
630 // The key must be either the primary journal key or live journal key.
631 template <typename TransactionType>
632 static leveldb::Status AppendBlobsToBlobJournal(
633 TransactionType* transaction,
634 const std::string& key,
635 const BlobJournalType& journal) {
636 if (journal.empty())
637 return leveldb::Status::OK();
638 BlobJournalType old_journal;
639 leveldb::Status s = GetBlobJournal(key, transaction, &old_journal);
640 if (!s.ok())
641 return s;
642 old_journal.insert(old_journal.end(), journal.begin(), journal.end());
643 UpdateBlobJournal(transaction, key, old_journal);
644 return leveldb::Status::OK();
647 template <typename TransactionType>
648 static leveldb::Status AppendBlobsToPrimaryBlobJournal(
649 TransactionType* transaction,
650 const BlobJournalType& journal) {
651 return AppendBlobsToBlobJournal(transaction, BlobJournalKey::Encode(),
652 journal);
655 template <typename TransactionType>
656 static leveldb::Status AppendBlobsToLiveBlobJournal(
657 TransactionType* transaction,
658 const BlobJournalType& journal) {
659 return AppendBlobsToBlobJournal(transaction, LiveBlobJournalKey::Encode(),
660 journal);
663 // Append a database to the specified blob journal via the supplied transaction.
664 // The key must be either the primary journal key or live journal key.
665 static leveldb::Status MergeDatabaseIntoBlobJournal(
666 LevelDBDirectTransaction* transaction,
667 const std::string& key,
668 int64 database_id) {
669 BlobJournalType journal;
670 leveldb::Status s = GetBlobJournal(key, transaction, &journal);
671 if (!s.ok())
672 return s;
673 journal.push_back(
674 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
675 UpdateBlobJournal(transaction, key, journal);
676 return leveldb::Status::OK();
679 static leveldb::Status MergeDatabaseIntoPrimaryBlobJournal(
680 LevelDBDirectTransaction* leveldb_transaction,
681 int64 database_id) {
682 return MergeDatabaseIntoBlobJournal(leveldb_transaction,
683 BlobJournalKey::Encode(), database_id);
686 static leveldb::Status MergeDatabaseIntoLiveBlobJournal(
687 LevelDBDirectTransaction* leveldb_transaction,
688 int64 database_id) {
689 return MergeDatabaseIntoBlobJournal(
690 leveldb_transaction, LiveBlobJournalKey::Encode(), database_id);
693 // Blob Data is encoded as a series of:
694 // { is_file [bool], key [int64 as varInt],
695 // type [string-with-length, may be empty],
696 // (for Blobs only) size [int64 as varInt]
697 // (for Files only) fileName [string-with-length]
698 // }
699 // There is no length field; just read until you run out of data.
700 static std::string EncodeBlobData(
701 const std::vector<IndexedDBBlobInfo*>& blob_info) {
702 std::string ret;
703 for (const auto* info : blob_info) {
704 EncodeBool(info->is_file(), &ret);
705 EncodeVarInt(info->key(), &ret);
706 EncodeStringWithLength(info->type(), &ret);
707 if (info->is_file())
708 EncodeStringWithLength(info->file_name(), &ret);
709 else
710 EncodeVarInt(info->size(), &ret);
712 return ret;
715 static bool DecodeBlobData(const std::string& data,
716 std::vector<IndexedDBBlobInfo>* output) {
717 std::vector<IndexedDBBlobInfo> ret;
718 output->clear();
719 StringPiece slice(data);
720 while (!slice.empty()) {
721 bool is_file;
722 int64 key;
723 base::string16 type;
724 int64 size;
725 base::string16 file_name;
727 if (!DecodeBool(&slice, &is_file))
728 return false;
729 if (!DecodeVarInt(&slice, &key) ||
730 !DatabaseMetaDataKey::IsValidBlobKey(key))
731 return false;
732 if (!DecodeStringWithLength(&slice, &type))
733 return false;
734 if (is_file) {
735 if (!DecodeStringWithLength(&slice, &file_name))
736 return false;
737 ret.push_back(IndexedDBBlobInfo(key, type, file_name));
738 } else {
739 if (!DecodeVarInt(&slice, &size) || size < 0)
740 return false;
741 ret.push_back(IndexedDBBlobInfo(type, static_cast<uint64>(size), key));
744 output->swap(ret);
746 return true;
749 IndexedDBBackingStore::IndexedDBBackingStore(
750 IndexedDBFactory* indexed_db_factory,
751 const GURL& origin_url,
752 const base::FilePath& blob_path,
753 net::URLRequestContext* request_context,
754 scoped_ptr<LevelDBDatabase> db,
755 scoped_ptr<LevelDBComparator> comparator,
756 base::SequencedTaskRunner* task_runner)
757 : indexed_db_factory_(indexed_db_factory),
758 origin_url_(origin_url),
759 blob_path_(blob_path),
760 origin_identifier_(ComputeOriginIdentifier(origin_url)),
761 request_context_(request_context),
762 task_runner_(task_runner),
763 db_(db.Pass()),
764 comparator_(comparator.Pass()),
765 active_blob_registry_(this),
766 committing_transaction_count_(0) {
769 IndexedDBBackingStore::~IndexedDBBackingStore() {
770 if (!blob_path_.empty() && !child_process_ids_granted_.empty()) {
771 ChildProcessSecurityPolicyImpl* policy =
772 ChildProcessSecurityPolicyImpl::GetInstance();
773 for (const auto& pid : child_process_ids_granted_)
774 policy->RevokeAllPermissionsForFile(pid, blob_path_);
776 STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
777 incognito_blob_map_.end());
778 // db_'s destructor uses comparator_. The order of destruction is important.
779 db_.reset();
780 comparator_.reset();
783 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
784 const std::string& primary_key,
785 int64 version)
786 : primary_key_(primary_key), version_(version) {
787 DCHECK(!primary_key.empty());
789 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
790 : primary_key_(), version_(-1) {}
791 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
793 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
794 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
796 // Values match entries in tools/metrics/histograms/histograms.xml
797 enum IndexedDBBackingStoreOpenResult {
798 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS,
799 INDEXED_DB_BACKING_STORE_OPEN_SUCCESS,
800 INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
801 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
802 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
803 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
804 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
805 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
806 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR_DEPRECATED,
807 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
808 INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
809 INDEXED_DB_BACKING_STORE_OPEN_DISK_FULL_DEPRECATED,
810 INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
811 INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
812 INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
813 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR,
814 INDEXED_DB_BACKING_STORE_OPEN_MAX,
817 // static
818 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
819 IndexedDBFactory* indexed_db_factory,
820 const GURL& origin_url,
821 const base::FilePath& path_base,
822 net::URLRequestContext* request_context,
823 blink::WebIDBDataLoss* data_loss,
824 std::string* data_loss_message,
825 bool* disk_full,
826 base::SequencedTaskRunner* task_runner,
827 bool clean_journal,
828 leveldb::Status* status) {
829 *data_loss = blink::WebIDBDataLossNone;
830 DefaultLevelDBFactory leveldb_factory;
831 return IndexedDBBackingStore::Open(indexed_db_factory,
832 origin_url,
833 path_base,
834 request_context,
835 data_loss,
836 data_loss_message,
837 disk_full,
838 &leveldb_factory,
839 task_runner,
840 clean_journal,
841 status);
844 static std::string OriginToCustomHistogramSuffix(const GURL& origin_url) {
845 if (origin_url.host() == "docs.google.com")
846 return ".Docs";
847 return std::string();
850 static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result,
851 const GURL& origin_url) {
852 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.BackingStore.OpenStatus",
853 result,
854 INDEXED_DB_BACKING_STORE_OPEN_MAX);
855 const std::string suffix = OriginToCustomHistogramSuffix(origin_url);
856 // Data from the WebCore.IndexedDB.BackingStore.OpenStatus histogram is used
857 // to generate a graph. So as not to alter the meaning of that graph,
858 // continue to collect all stats there (above) but also now collect docs stats
859 // separately (below).
860 if (!suffix.empty()) {
861 base::LinearHistogram::FactoryGet(
862 "WebCore.IndexedDB.BackingStore.OpenStatus" + suffix,
864 INDEXED_DB_BACKING_STORE_OPEN_MAX,
865 INDEXED_DB_BACKING_STORE_OPEN_MAX + 1,
866 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(result);
870 static bool IsPathTooLong(const base::FilePath& leveldb_dir) {
871 int limit = base::GetMaximumPathComponentLength(leveldb_dir.DirName());
872 if (limit == -1) {
873 DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
874 // In limited testing, ChromeOS returns 143, other OSes 255.
875 #if defined(OS_CHROMEOS)
876 limit = 143;
877 #else
878 limit = 255;
879 #endif
881 size_t component_length = leveldb_dir.BaseName().value().length();
882 if (component_length > static_cast<uint32_t>(limit)) {
883 DLOG(WARNING) << "Path component length (" << component_length
884 << ") exceeds maximum (" << limit
885 << ") allowed by this filesystem.";
886 const int min = 140;
887 const int max = 300;
888 const int num_buckets = 12;
889 UMA_HISTOGRAM_CUSTOM_COUNTS(
890 "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
891 component_length,
892 min,
893 max,
894 num_buckets);
895 return true;
897 return false;
900 leveldb::Status IndexedDBBackingStore::DestroyBackingStore(
901 const base::FilePath& path_base,
902 const GURL& origin_url) {
903 const base::FilePath file_path =
904 path_base.Append(ComputeFileName(origin_url));
905 DefaultLevelDBFactory leveldb_factory;
906 return leveldb_factory.DestroyLevelDB(file_path);
909 bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath& path_base,
910 const GURL& origin_url,
911 std::string* message) {
912 const base::FilePath info_path =
913 path_base.Append(ComputeCorruptionFileName(origin_url));
915 if (IsPathTooLong(info_path))
916 return false;
918 const int64 max_json_len = 4096;
919 int64 file_size(0);
920 if (!GetFileSize(info_path, &file_size) || file_size > max_json_len)
921 return false;
922 if (!file_size) {
923 NOTREACHED();
924 return false;
927 base::File file(info_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
928 bool success = false;
929 if (file.IsValid()) {
930 std::vector<char> bytes(file_size);
931 if (file_size == file.Read(0, &bytes[0], file_size)) {
932 std::string input_js(&bytes[0], file_size);
933 base::JSONReader reader;
934 scoped_ptr<base::Value> val(reader.ReadToValue(input_js));
935 if (val && val->GetType() == base::Value::TYPE_DICTIONARY) {
936 base::DictionaryValue* dict_val =
937 static_cast<base::DictionaryValue*>(val.get());
938 success = dict_val->GetString("message", message);
941 file.Close();
944 base::DeleteFile(info_path, false);
946 return success;
949 bool IndexedDBBackingStore::RecordCorruptionInfo(
950 const base::FilePath& path_base,
951 const GURL& origin_url,
952 const std::string& message) {
953 const base::FilePath info_path =
954 path_base.Append(ComputeCorruptionFileName(origin_url));
955 if (IsPathTooLong(info_path))
956 return false;
958 base::DictionaryValue root_dict;
959 root_dict.SetString("message", message);
960 std::string output_js;
961 base::JSONWriter::Write(&root_dict, &output_js);
963 base::File file(info_path,
964 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
965 if (!file.IsValid())
966 return false;
967 int written = file.Write(0, output_js.c_str(), output_js.length());
968 return size_t(written) == output_js.length();
971 // static
972 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
973 IndexedDBFactory* indexed_db_factory,
974 const GURL& origin_url,
975 const base::FilePath& path_base,
976 net::URLRequestContext* request_context,
977 blink::WebIDBDataLoss* data_loss,
978 std::string* data_loss_message,
979 bool* is_disk_full,
980 LevelDBFactory* leveldb_factory,
981 base::SequencedTaskRunner* task_runner,
982 bool clean_journal,
983 leveldb::Status* status) {
984 IDB_TRACE("IndexedDBBackingStore::Open");
985 DCHECK(!path_base.empty());
986 *data_loss = blink::WebIDBDataLossNone;
987 *data_loss_message = "";
988 *is_disk_full = false;
990 *status = leveldb::Status::OK();
992 scoped_ptr<LevelDBComparator> comparator(new Comparator());
994 if (!base::IsStringASCII(path_base.AsUTF8Unsafe())) {
995 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
996 origin_url);
998 if (!base::CreateDirectory(path_base)) {
999 *status =
1000 leveldb::Status::IOError("Unable to create IndexedDB database path");
1001 LOG(ERROR) << status->ToString() << ": \"" << path_base.AsUTF8Unsafe()
1002 << "\"";
1003 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
1004 origin_url);
1005 return scoped_refptr<IndexedDBBackingStore>();
1008 const base::FilePath file_path =
1009 path_base.Append(ComputeFileName(origin_url));
1010 const base::FilePath blob_path =
1011 path_base.Append(ComputeBlobPath(origin_url));
1013 if (IsPathTooLong(file_path)) {
1014 *status = leveldb::Status::IOError("File path too long");
1015 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
1016 origin_url);
1017 return scoped_refptr<IndexedDBBackingStore>();
1020 scoped_ptr<LevelDBDatabase> db;
1021 *status = leveldb_factory->OpenLevelDB(
1022 file_path, comparator.get(), &db, is_disk_full);
1024 DCHECK(!db == !status->ok());
1025 if (!status->ok()) {
1026 if (leveldb_env::IndicatesDiskFull(*status)) {
1027 *is_disk_full = true;
1028 } else if (status->IsCorruption()) {
1029 *data_loss = blink::WebIDBDataLossTotal;
1030 *data_loss_message = leveldb_env::GetCorruptionMessage(*status);
1034 bool is_schema_known = false;
1035 if (db) {
1036 std::string corruption_message;
1037 if (ReadCorruptionInfo(path_base, origin_url, &corruption_message)) {
1038 LOG(ERROR) << "IndexedDB recovering from a corrupted (and deleted) "
1039 "database.";
1040 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
1041 origin_url);
1042 db.reset();
1043 *data_loss = blink::WebIDBDataLossTotal;
1044 *data_loss_message =
1045 "IndexedDB (database was corrupt): " + corruption_message;
1046 } else if (!IsSchemaKnown(db.get(), &is_schema_known)) {
1047 LOG(ERROR) << "IndexedDB had IO error checking schema, treating it as "
1048 "failure to open";
1049 HistogramOpenStatus(
1050 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
1051 origin_url);
1052 db.reset();
1053 *data_loss = blink::WebIDBDataLossTotal;
1054 *data_loss_message = "I/O error checking schema";
1055 } else if (!is_schema_known) {
1056 LOG(ERROR) << "IndexedDB backing store had unknown schema, treating it "
1057 "as failure to open";
1058 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
1059 origin_url);
1060 db.reset();
1061 *data_loss = blink::WebIDBDataLossTotal;
1062 *data_loss_message = "Unknown schema";
1066 DCHECK(status->ok() || !is_schema_known || status->IsIOError() ||
1067 status->IsCorruption());
1069 if (db) {
1070 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_SUCCESS, origin_url);
1071 } else if (status->IsIOError()) {
1072 LOG(ERROR) << "Unable to open backing store, not trying to recover - "
1073 << status->ToString();
1074 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY, origin_url);
1075 return scoped_refptr<IndexedDBBackingStore>();
1076 } else {
1077 DCHECK(!is_schema_known || status->IsCorruption());
1078 LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup";
1079 *status = leveldb_factory->DestroyLevelDB(file_path);
1080 if (!status->ok()) {
1081 LOG(ERROR) << "IndexedDB backing store cleanup failed";
1082 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
1083 origin_url);
1084 return scoped_refptr<IndexedDBBackingStore>();
1087 LOG(ERROR) << "IndexedDB backing store cleanup succeeded, reopening";
1088 leveldb_factory->OpenLevelDB(file_path, comparator.get(), &db, NULL);
1089 if (!db) {
1090 LOG(ERROR) << "IndexedDB backing store reopen after recovery failed";
1091 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
1092 origin_url);
1093 return scoped_refptr<IndexedDBBackingStore>();
1095 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
1096 origin_url);
1099 scoped_refptr<IndexedDBBackingStore> backing_store =
1100 Create(indexed_db_factory,
1101 origin_url,
1102 blob_path,
1103 request_context,
1104 db.Pass(),
1105 comparator.Pass(),
1106 task_runner,
1107 status);
1109 if (clean_journal && backing_store.get() &&
1110 !backing_store->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
1111 HistogramOpenStatus(
1112 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR, origin_url);
1113 return scoped_refptr<IndexedDBBackingStore>();
1115 return backing_store;
1118 // static
1119 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
1120 const GURL& origin_url,
1121 base::SequencedTaskRunner* task_runner,
1122 leveldb::Status* status) {
1123 DefaultLevelDBFactory leveldb_factory;
1124 return IndexedDBBackingStore::OpenInMemory(
1125 origin_url, &leveldb_factory, task_runner, status);
1128 // static
1129 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
1130 const GURL& origin_url,
1131 LevelDBFactory* leveldb_factory,
1132 base::SequencedTaskRunner* task_runner,
1133 leveldb::Status* status) {
1134 IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
1136 scoped_ptr<LevelDBComparator> comparator(new Comparator());
1137 scoped_ptr<LevelDBDatabase> db =
1138 LevelDBDatabase::OpenInMemory(comparator.get());
1139 if (!db) {
1140 LOG(ERROR) << "LevelDBDatabase::OpenInMemory failed.";
1141 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
1142 origin_url);
1143 return scoped_refptr<IndexedDBBackingStore>();
1145 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS, origin_url);
1147 return Create(NULL /* indexed_db_factory */,
1148 origin_url,
1149 base::FilePath(),
1150 NULL /* request_context */,
1151 db.Pass(),
1152 comparator.Pass(),
1153 task_runner,
1154 status);
1157 // static
1158 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
1159 IndexedDBFactory* indexed_db_factory,
1160 const GURL& origin_url,
1161 const base::FilePath& blob_path,
1162 net::URLRequestContext* request_context,
1163 scoped_ptr<LevelDBDatabase> db,
1164 scoped_ptr<LevelDBComparator> comparator,
1165 base::SequencedTaskRunner* task_runner,
1166 leveldb::Status* status) {
1167 // TODO(jsbell): Handle comparator name changes.
1168 scoped_refptr<IndexedDBBackingStore> backing_store(
1169 new IndexedDBBackingStore(indexed_db_factory,
1170 origin_url,
1171 blob_path,
1172 request_context,
1173 db.Pass(),
1174 comparator.Pass(),
1175 task_runner));
1176 *status = backing_store->SetUpMetadata();
1177 if (!status->ok())
1178 return scoped_refptr<IndexedDBBackingStore>();
1180 return backing_store;
1183 void IndexedDBBackingStore::GrantChildProcessPermissions(int child_process_id) {
1184 if (!child_process_ids_granted_.count(child_process_id)) {
1185 child_process_ids_granted_.insert(child_process_id);
1186 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
1187 child_process_id, blob_path_);
1191 std::vector<base::string16> IndexedDBBackingStore::GetDatabaseNames(
1192 leveldb::Status* s) {
1193 *s = leveldb::Status::OK();
1194 std::vector<base::string16> found_names;
1195 const std::string start_key =
1196 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
1197 const std::string stop_key =
1198 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
1200 DCHECK(found_names.empty());
1202 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1203 for (*s = it->Seek(start_key);
1204 s->ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1205 *s = it->Next()) {
1206 // Decode database name (in iterator key).
1207 StringPiece slice(it->Key());
1208 DatabaseNameKey database_name_key;
1209 if (!DatabaseNameKey::Decode(&slice, &database_name_key) ||
1210 !slice.empty()) {
1211 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
1212 continue;
1215 // Decode database id (in iterator value).
1216 int64 database_id = 0;
1217 StringPiece value_slice(it->Value());
1218 if (!DecodeInt(&value_slice, &database_id) || !value_slice.empty()) {
1219 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
1220 continue;
1223 // Look up version by id.
1224 bool found = false;
1225 int64 database_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1226 *s = GetVarInt(db_.get(),
1227 DatabaseMetaDataKey::Encode(
1228 database_id, DatabaseMetaDataKey::USER_INT_VERSION),
1229 &database_version,
1230 &found);
1231 if (!s->ok() || !found) {
1232 INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES);
1233 continue;
1236 // Ignore stale metadata from failed initial opens.
1237 if (database_version != IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
1238 found_names.push_back(database_name_key.database_name());
1241 if (!s->ok())
1242 INTERNAL_READ_ERROR(GET_DATABASE_NAMES);
1244 return found_names;
1247 leveldb::Status IndexedDBBackingStore::GetIDBDatabaseMetaData(
1248 const base::string16& name,
1249 IndexedDBDatabaseMetadata* metadata,
1250 bool* found) {
1251 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1252 *found = false;
1254 leveldb::Status s = GetInt(db_.get(), key, &metadata->id, found);
1255 if (!s.ok()) {
1256 INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
1257 return s;
1259 if (!*found)
1260 return leveldb::Status::OK();
1262 s = GetString(db_.get(),
1263 DatabaseMetaDataKey::Encode(metadata->id,
1264 DatabaseMetaDataKey::USER_VERSION),
1265 &metadata->version,
1266 found);
1267 if (!s.ok()) {
1268 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1269 return s;
1271 if (!*found) {
1272 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1273 return InternalInconsistencyStatus();
1276 s = GetVarInt(db_.get(),
1277 DatabaseMetaDataKey::Encode(
1278 metadata->id, DatabaseMetaDataKey::USER_INT_VERSION),
1279 &metadata->int_version,
1280 found);
1281 if (!s.ok()) {
1282 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1283 return s;
1285 if (!*found) {
1286 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1287 return InternalInconsistencyStatus();
1290 if (metadata->int_version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
1291 metadata->int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1293 s = GetMaxObjectStoreId(
1294 db_.get(), metadata->id, &metadata->max_object_store_id);
1295 if (!s.ok()) {
1296 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1299 // We don't cache this, we just check it if it's there.
1300 int64 blob_key_generator_current_number =
1301 DatabaseMetaDataKey::kInvalidBlobKey;
1303 s = GetVarInt(
1304 db_.get(),
1305 DatabaseMetaDataKey::Encode(
1306 metadata->id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
1307 &blob_key_generator_current_number,
1308 found);
1309 if (!s.ok()) {
1310 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1311 return s;
1313 if (!*found) {
1314 // This database predates blob support.
1315 *found = true;
1316 } else if (!DatabaseMetaDataKey::IsValidBlobKey(
1317 blob_key_generator_current_number)) {
1318 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1319 return InternalInconsistencyStatus();
1322 return s;
1325 WARN_UNUSED_RESULT static leveldb::Status GetNewDatabaseId(
1326 LevelDBTransaction* transaction,
1327 int64* new_id) {
1328 *new_id = -1;
1329 int64 max_database_id = -1;
1330 bool found = false;
1331 leveldb::Status s =
1332 GetInt(transaction, MaxDatabaseIdKey::Encode(), &max_database_id, &found);
1333 if (!s.ok()) {
1334 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID);
1335 return s;
1337 if (!found)
1338 max_database_id = 0;
1340 DCHECK_GE(max_database_id, 0);
1342 int64 database_id = max_database_id + 1;
1343 PutInt(transaction, MaxDatabaseIdKey::Encode(), database_id);
1344 *new_id = database_id;
1345 return leveldb::Status::OK();
1348 leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData(
1349 const base::string16& name,
1350 const base::string16& version,
1351 int64 int_version,
1352 int64* row_id) {
1353 // TODO(jsbell): Don't persist metadata if open fails. http://crbug.com/395472
1354 scoped_refptr<LevelDBTransaction> transaction =
1355 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
1357 leveldb::Status s = GetNewDatabaseId(transaction.get(), row_id);
1358 if (!s.ok())
1359 return s;
1360 DCHECK_GE(*row_id, 0);
1362 if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1363 int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1365 PutInt(transaction.get(),
1366 DatabaseNameKey::Encode(origin_identifier_, name),
1367 *row_id);
1368 PutString(
1369 transaction.get(),
1370 DatabaseMetaDataKey::Encode(*row_id, DatabaseMetaDataKey::USER_VERSION),
1371 version);
1372 PutVarInt(transaction.get(),
1373 DatabaseMetaDataKey::Encode(*row_id,
1374 DatabaseMetaDataKey::USER_INT_VERSION),
1375 int_version);
1376 PutVarInt(
1377 transaction.get(),
1378 DatabaseMetaDataKey::Encode(
1379 *row_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
1380 DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber);
1382 s = transaction->Commit();
1383 if (!s.ok())
1384 INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA);
1385 return s;
1388 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
1389 IndexedDBBackingStore::Transaction* transaction,
1390 int64 row_id,
1391 int64 int_version) {
1392 if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1393 int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1394 DCHECK_GE(int_version, 0) << "int_version was " << int_version;
1395 PutVarInt(transaction->transaction(),
1396 DatabaseMetaDataKey::Encode(row_id,
1397 DatabaseMetaDataKey::USER_INT_VERSION),
1398 int_version);
1399 return true;
1402 // If you're deleting a range that contains user keys that have blob info, this
1403 // won't clean up the blobs.
1404 static leveldb::Status DeleteRangeBasic(LevelDBTransaction* transaction,
1405 const std::string& begin,
1406 const std::string& end,
1407 bool upper_open) {
1408 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
1409 leveldb::Status s;
1410 for (s = it->Seek(begin); s.ok() && it->IsValid() &&
1411 (upper_open ? CompareKeys(it->Key(), end) < 0
1412 : CompareKeys(it->Key(), end) <= 0);
1413 s = it->Next())
1414 transaction->Remove(it->Key());
1415 return s;
1418 static leveldb::Status DeleteBlobsInRange(
1419 IndexedDBBackingStore::Transaction* transaction,
1420 int64 database_id,
1421 int64 object_store_id,
1422 const std::string& start_key,
1423 const std::string& end_key,
1424 bool upper_open) {
1425 scoped_ptr<LevelDBIterator> it = transaction->transaction()->CreateIterator();
1426 leveldb::Status s = it->Seek(start_key);
1427 for (; s.ok() && it->IsValid() &&
1428 (upper_open ? CompareKeys(it->Key(), end_key) < 0
1429 : CompareKeys(it->Key(), end_key) <= 0);
1430 s = it->Next()) {
1431 StringPiece key_piece(it->Key());
1432 std::string user_key =
1433 BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece);
1434 if (!user_key.size()) {
1435 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1436 return InternalInconsistencyStatus();
1438 transaction->PutBlobInfo(
1439 database_id, object_store_id, user_key, NULL, NULL);
1441 return s;
1444 static leveldb::Status DeleteBlobsInObjectStore(
1445 IndexedDBBackingStore::Transaction* transaction,
1446 int64 database_id,
1447 int64 object_store_id) {
1448 std::string start_key, stop_key;
1449 start_key =
1450 BlobEntryKey::EncodeMinKeyForObjectStore(database_id, object_store_id);
1451 stop_key =
1452 BlobEntryKey::EncodeStopKeyForObjectStore(database_id, object_store_id);
1453 return DeleteBlobsInRange(
1454 transaction, database_id, object_store_id, start_key, stop_key, true);
1457 leveldb::Status IndexedDBBackingStore::DeleteDatabase(
1458 const base::string16& name) {
1459 IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
1460 scoped_ptr<LevelDBDirectTransaction> transaction =
1461 LevelDBDirectTransaction::Create(db_.get());
1463 leveldb::Status s;
1465 IndexedDBDatabaseMetadata metadata;
1466 bool success = false;
1467 s = GetIDBDatabaseMetaData(name, &metadata, &success);
1468 if (!s.ok())
1469 return s;
1470 if (!success)
1471 return leveldb::Status::OK();
1473 const std::string start_key = DatabaseMetaDataKey::Encode(
1474 metadata.id, DatabaseMetaDataKey::ORIGIN_NAME);
1475 const std::string stop_key = DatabaseMetaDataKey::Encode(
1476 metadata.id + 1, DatabaseMetaDataKey::ORIGIN_NAME);
1477 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1478 for (s = it->Seek(start_key);
1479 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1480 s = it->Next())
1481 transaction->Remove(it->Key());
1482 if (!s.ok()) {
1483 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1484 return s;
1487 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1488 transaction->Remove(key);
1490 bool need_cleanup = false;
1491 if (active_blob_registry()->MarkDeletedCheckIfUsed(
1492 metadata.id, DatabaseMetaDataKey::kAllBlobsKey)) {
1493 s = MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id);
1494 if (!s.ok())
1495 return s;
1496 } else {
1497 s = MergeDatabaseIntoPrimaryBlobJournal(transaction.get(), metadata.id);
1498 if (!s.ok())
1499 return s;
1500 need_cleanup = true;
1503 s = transaction->Commit();
1504 if (!s.ok()) {
1505 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1506 return s;
1509 // If another transaction is running, this will defer processing
1510 // the journal until completion.
1511 if (need_cleanup)
1512 CleanPrimaryJournalIgnoreReturn();
1514 db_->Compact(start_key, stop_key);
1515 return s;
1518 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it,
1519 const std::string& stop_key,
1520 int64 object_store_id,
1521 int64 meta_data_type) {
1522 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
1523 return false;
1525 StringPiece slice(it->Key());
1526 ObjectStoreMetaDataKey meta_data_key;
1527 bool ok =
1528 ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
1529 DCHECK(ok);
1530 if (meta_data_key.ObjectStoreId() != object_store_id)
1531 return false;
1532 if (meta_data_key.MetaDataType() != meta_data_type)
1533 return false;
1534 return ok;
1537 // TODO(jsbell): This should do some error handling rather than
1538 // plowing ahead when bad data is encountered.
1539 leveldb::Status IndexedDBBackingStore::GetObjectStores(
1540 int64 database_id,
1541 IndexedDBDatabaseMetadata::ObjectStoreMap* object_stores) {
1542 IDB_TRACE("IndexedDBBackingStore::GetObjectStores");
1543 if (!KeyPrefix::IsValidDatabaseId(database_id))
1544 return InvalidDBKeyStatus();
1545 const std::string start_key =
1546 ObjectStoreMetaDataKey::Encode(database_id, 1, 0);
1547 const std::string stop_key =
1548 ObjectStoreMetaDataKey::EncodeMaxKey(database_id);
1550 DCHECK(object_stores->empty());
1552 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1553 leveldb::Status s = it->Seek(start_key);
1554 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
1555 StringPiece slice(it->Key());
1556 ObjectStoreMetaDataKey meta_data_key;
1557 bool ok =
1558 ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
1559 DCHECK(ok);
1560 if (!ok || meta_data_key.MetaDataType() != ObjectStoreMetaDataKey::NAME) {
1561 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1562 // Possible stale metadata, but don't fail the load.
1563 s = it->Next();
1564 if (!s.ok())
1565 break;
1566 continue;
1569 int64 object_store_id = meta_data_key.ObjectStoreId();
1571 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
1572 // simplify.
1573 base::string16 object_store_name;
1575 StringPiece slice(it->Value());
1576 if (!DecodeString(&slice, &object_store_name) || !slice.empty())
1577 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1580 s = it->Next();
1581 if (!s.ok())
1582 break;
1583 if (!CheckObjectStoreAndMetaDataType(it.get(),
1584 stop_key,
1585 object_store_id,
1586 ObjectStoreMetaDataKey::KEY_PATH)) {
1587 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1588 break;
1590 IndexedDBKeyPath key_path;
1592 StringPiece slice(it->Value());
1593 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
1594 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1597 s = it->Next();
1598 if (!s.ok())
1599 break;
1600 if (!CheckObjectStoreAndMetaDataType(
1601 it.get(),
1602 stop_key,
1603 object_store_id,
1604 ObjectStoreMetaDataKey::AUTO_INCREMENT)) {
1605 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1606 break;
1608 bool auto_increment;
1610 StringPiece slice(it->Value());
1611 if (!DecodeBool(&slice, &auto_increment) || !slice.empty())
1612 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1615 s = it->Next(); // Is evictable.
1616 if (!s.ok())
1617 break;
1618 if (!CheckObjectStoreAndMetaDataType(it.get(),
1619 stop_key,
1620 object_store_id,
1621 ObjectStoreMetaDataKey::EVICTABLE)) {
1622 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1623 break;
1626 s = it->Next(); // Last version.
1627 if (!s.ok())
1628 break;
1629 if (!CheckObjectStoreAndMetaDataType(
1630 it.get(),
1631 stop_key,
1632 object_store_id,
1633 ObjectStoreMetaDataKey::LAST_VERSION)) {
1634 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1635 break;
1638 s = it->Next(); // Maximum index id allocated.
1639 if (!s.ok())
1640 break;
1641 if (!CheckObjectStoreAndMetaDataType(
1642 it.get(),
1643 stop_key,
1644 object_store_id,
1645 ObjectStoreMetaDataKey::MAX_INDEX_ID)) {
1646 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1647 break;
1649 int64 max_index_id;
1651 StringPiece slice(it->Value());
1652 if (!DecodeInt(&slice, &max_index_id) || !slice.empty())
1653 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1656 s = it->Next(); // [optional] has key path (is not null)
1657 if (!s.ok())
1658 break;
1659 if (CheckObjectStoreAndMetaDataType(it.get(),
1660 stop_key,
1661 object_store_id,
1662 ObjectStoreMetaDataKey::HAS_KEY_PATH)) {
1663 bool has_key_path;
1665 StringPiece slice(it->Value());
1666 if (!DecodeBool(&slice, &has_key_path))
1667 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1669 // This check accounts for two layers of legacy coding:
1670 // (1) Initially, has_key_path was added to distinguish null vs. string.
1671 // (2) Later, null vs. string vs. array was stored in the key_path itself.
1672 // So this check is only relevant for string-type key_paths.
1673 if (!has_key_path &&
1674 (key_path.type() == blink::WebIDBKeyPathTypeString &&
1675 !key_path.string().empty())) {
1676 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1677 break;
1679 if (!has_key_path)
1680 key_path = IndexedDBKeyPath();
1681 s = it->Next();
1682 if (!s.ok())
1683 break;
1686 int64 key_generator_current_number = -1;
1687 if (CheckObjectStoreAndMetaDataType(
1688 it.get(),
1689 stop_key,
1690 object_store_id,
1691 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER)) {
1692 StringPiece slice(it->Value());
1693 if (!DecodeInt(&slice, &key_generator_current_number) || !slice.empty())
1694 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1696 // TODO(jsbell): Return key_generator_current_number, cache in
1697 // object store, and write lazily to backing store. For now,
1698 // just assert that if it was written it was valid.
1699 DCHECK_GE(key_generator_current_number, kKeyGeneratorInitialNumber);
1700 s = it->Next();
1701 if (!s.ok())
1702 break;
1705 IndexedDBObjectStoreMetadata metadata(object_store_name,
1706 object_store_id,
1707 key_path,
1708 auto_increment,
1709 max_index_id);
1710 s = GetIndexes(database_id, object_store_id, &metadata.indexes);
1711 if (!s.ok())
1712 break;
1713 (*object_stores)[object_store_id] = metadata;
1716 if (!s.ok())
1717 INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES);
1719 return s;
1722 WARN_UNUSED_RESULT static leveldb::Status SetMaxObjectStoreId(
1723 LevelDBTransaction* transaction,
1724 int64 database_id,
1725 int64 object_store_id) {
1726 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
1727 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
1728 int64 max_object_store_id = -1;
1729 leveldb::Status s = GetMaxObjectStoreId(
1730 transaction, max_object_store_id_key, &max_object_store_id);
1731 if (!s.ok()) {
1732 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1733 return s;
1736 if (object_store_id <= max_object_store_id) {
1737 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1738 return InternalInconsistencyStatus();
1740 PutInt(transaction, max_object_store_id_key, object_store_id);
1741 return s;
1744 void IndexedDBBackingStore::Compact() { db_->CompactAll(); }
1746 leveldb::Status IndexedDBBackingStore::CreateObjectStore(
1747 IndexedDBBackingStore::Transaction* transaction,
1748 int64 database_id,
1749 int64 object_store_id,
1750 const base::string16& name,
1751 const IndexedDBKeyPath& key_path,
1752 bool auto_increment) {
1753 IDB_TRACE("IndexedDBBackingStore::CreateObjectStore");
1754 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1755 return InvalidDBKeyStatus();
1756 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1757 leveldb::Status s =
1758 SetMaxObjectStoreId(leveldb_transaction, database_id, object_store_id);
1759 if (!s.ok())
1760 return s;
1762 const std::string name_key = ObjectStoreMetaDataKey::Encode(
1763 database_id, object_store_id, ObjectStoreMetaDataKey::NAME);
1764 const std::string key_path_key = ObjectStoreMetaDataKey::Encode(
1765 database_id, object_store_id, ObjectStoreMetaDataKey::KEY_PATH);
1766 const std::string auto_increment_key = ObjectStoreMetaDataKey::Encode(
1767 database_id, object_store_id, ObjectStoreMetaDataKey::AUTO_INCREMENT);
1768 const std::string evictable_key = ObjectStoreMetaDataKey::Encode(
1769 database_id, object_store_id, ObjectStoreMetaDataKey::EVICTABLE);
1770 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1771 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1772 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
1773 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
1774 const std::string has_key_path_key = ObjectStoreMetaDataKey::Encode(
1775 database_id, object_store_id, ObjectStoreMetaDataKey::HAS_KEY_PATH);
1776 const std::string key_generator_current_number_key =
1777 ObjectStoreMetaDataKey::Encode(
1778 database_id,
1779 object_store_id,
1780 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
1781 const std::string names_key = ObjectStoreNamesKey::Encode(database_id, name);
1783 PutString(leveldb_transaction, name_key, name);
1784 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
1785 PutInt(leveldb_transaction, auto_increment_key, auto_increment);
1786 PutInt(leveldb_transaction, evictable_key, false);
1787 PutInt(leveldb_transaction, last_version_key, 1);
1788 PutInt(leveldb_transaction, max_index_id_key, kMinimumIndexId);
1789 PutBool(leveldb_transaction, has_key_path_key, !key_path.IsNull());
1790 PutInt(leveldb_transaction,
1791 key_generator_current_number_key,
1792 kKeyGeneratorInitialNumber);
1793 PutInt(leveldb_transaction, names_key, object_store_id);
1794 return s;
1797 leveldb::Status IndexedDBBackingStore::DeleteObjectStore(
1798 IndexedDBBackingStore::Transaction* transaction,
1799 int64 database_id,
1800 int64 object_store_id) {
1801 IDB_TRACE("IndexedDBBackingStore::DeleteObjectStore");
1802 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1803 return InvalidDBKeyStatus();
1804 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1806 base::string16 object_store_name;
1807 bool found = false;
1808 leveldb::Status s =
1809 GetString(leveldb_transaction,
1810 ObjectStoreMetaDataKey::Encode(
1811 database_id, object_store_id, ObjectStoreMetaDataKey::NAME),
1812 &object_store_name,
1813 &found);
1814 if (!s.ok()) {
1815 INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1816 return s;
1818 if (!found) {
1819 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1820 return InternalInconsistencyStatus();
1823 s = DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
1824 if (!s.ok()) {
1825 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1826 return s;
1829 s = DeleteRangeBasic(
1830 leveldb_transaction,
1831 ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0),
1832 ObjectStoreMetaDataKey::EncodeMaxKey(database_id, object_store_id),
1833 true);
1835 if (s.ok()) {
1836 leveldb_transaction->Remove(
1837 ObjectStoreNamesKey::Encode(database_id, object_store_name));
1839 s = DeleteRangeBasic(
1840 leveldb_transaction,
1841 IndexFreeListKey::Encode(database_id, object_store_id, 0),
1842 IndexFreeListKey::EncodeMaxKey(database_id, object_store_id),
1843 true);
1846 if (s.ok()) {
1847 s = DeleteRangeBasic(
1848 leveldb_transaction,
1849 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0),
1850 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id),
1851 true);
1854 if (!s.ok()) {
1855 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1856 return s;
1859 return ClearObjectStore(transaction, database_id, object_store_id);
1862 leveldb::Status IndexedDBBackingStore::GetRecord(
1863 IndexedDBBackingStore::Transaction* transaction,
1864 int64 database_id,
1865 int64 object_store_id,
1866 const IndexedDBKey& key,
1867 IndexedDBValue* record) {
1868 IDB_TRACE("IndexedDBBackingStore::GetRecord");
1869 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1870 return InvalidDBKeyStatus();
1871 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1873 const std::string leveldb_key =
1874 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1875 std::string data;
1877 record->clear();
1879 bool found = false;
1880 leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
1881 if (!s.ok()) {
1882 INTERNAL_READ_ERROR(GET_RECORD);
1883 return s;
1885 if (!found)
1886 return s;
1887 if (data.empty()) {
1888 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1889 return leveldb::Status::NotFound("Record contained no data");
1892 int64 version;
1893 StringPiece slice(data);
1894 if (!DecodeVarInt(&slice, &version)) {
1895 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1896 return InternalInconsistencyStatus();
1899 record->bits = slice.as_string();
1900 return transaction->GetBlobInfoForRecord(database_id, leveldb_key, record);
1903 WARN_UNUSED_RESULT static leveldb::Status GetNewVersionNumber(
1904 LevelDBTransaction* transaction,
1905 int64 database_id,
1906 int64 object_store_id,
1907 int64* new_version_number) {
1908 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1909 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1911 *new_version_number = -1;
1912 int64 last_version = -1;
1913 bool found = false;
1914 leveldb::Status s =
1915 GetInt(transaction, last_version_key, &last_version, &found);
1916 if (!s.ok()) {
1917 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER);
1918 return s;
1920 if (!found)
1921 last_version = 0;
1923 DCHECK_GE(last_version, 0);
1925 int64 version = last_version + 1;
1926 PutInt(transaction, last_version_key, version);
1928 // TODO(jsbell): Think about how we want to handle the overflow scenario.
1929 DCHECK(version > last_version);
1931 *new_version_number = version;
1932 return s;
1935 leveldb::Status IndexedDBBackingStore::PutRecord(
1936 IndexedDBBackingStore::Transaction* transaction,
1937 int64 database_id,
1938 int64 object_store_id,
1939 const IndexedDBKey& key,
1940 IndexedDBValue* value,
1941 ScopedVector<storage::BlobDataHandle>* handles,
1942 RecordIdentifier* record_identifier) {
1943 IDB_TRACE("IndexedDBBackingStore::PutRecord");
1944 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1945 return InvalidDBKeyStatus();
1946 DCHECK(key.IsValid());
1948 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1949 int64 version = -1;
1950 leveldb::Status s = GetNewVersionNumber(
1951 leveldb_transaction, database_id, object_store_id, &version);
1952 if (!s.ok())
1953 return s;
1954 DCHECK_GE(version, 0);
1955 const std::string object_store_data_key =
1956 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1958 std::string v;
1959 EncodeVarInt(version, &v);
1960 v.append(value->bits);
1962 leveldb_transaction->Put(object_store_data_key, &v);
1963 s = transaction->PutBlobInfoIfNeeded(database_id,
1964 object_store_id,
1965 object_store_data_key,
1966 &value->blob_info,
1967 handles);
1968 if (!s.ok())
1969 return s;
1970 DCHECK(!handles->size());
1972 const std::string exists_entry_key =
1973 ExistsEntryKey::Encode(database_id, object_store_id, key);
1974 std::string version_encoded;
1975 EncodeInt(version, &version_encoded);
1976 leveldb_transaction->Put(exists_entry_key, &version_encoded);
1978 std::string key_encoded;
1979 EncodeIDBKey(key, &key_encoded);
1980 record_identifier->Reset(key_encoded, version);
1981 return s;
1984 leveldb::Status IndexedDBBackingStore::ClearObjectStore(
1985 IndexedDBBackingStore::Transaction* transaction,
1986 int64 database_id,
1987 int64 object_store_id) {
1988 IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
1989 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1990 return InvalidDBKeyStatus();
1991 const std::string start_key =
1992 KeyPrefix(database_id, object_store_id).Encode();
1993 const std::string stop_key =
1994 KeyPrefix(database_id, object_store_id + 1).Encode();
1996 leveldb::Status s =
1997 DeleteRangeBasic(transaction->transaction(), start_key, stop_key, true);
1998 if (!s.ok()) {
1999 INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE);
2000 return s;
2002 return DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
2005 leveldb::Status IndexedDBBackingStore::DeleteRecord(
2006 IndexedDBBackingStore::Transaction* transaction,
2007 int64 database_id,
2008 int64 object_store_id,
2009 const RecordIdentifier& record_identifier) {
2010 IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
2011 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2012 return InvalidDBKeyStatus();
2013 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2015 const std::string object_store_data_key = ObjectStoreDataKey::Encode(
2016 database_id, object_store_id, record_identifier.primary_key());
2017 leveldb_transaction->Remove(object_store_data_key);
2018 leveldb::Status s = transaction->PutBlobInfoIfNeeded(
2019 database_id, object_store_id, object_store_data_key, NULL, NULL);
2020 if (!s.ok())
2021 return s;
2023 const std::string exists_entry_key = ExistsEntryKey::Encode(
2024 database_id, object_store_id, record_identifier.primary_key());
2025 leveldb_transaction->Remove(exists_entry_key);
2026 return leveldb::Status::OK();
2029 leveldb::Status IndexedDBBackingStore::DeleteRange(
2030 IndexedDBBackingStore::Transaction* transaction,
2031 int64 database_id,
2032 int64 object_store_id,
2033 const IndexedDBKeyRange& key_range) {
2034 leveldb::Status s;
2035 scoped_ptr<IndexedDBBackingStore::Cursor> start_cursor =
2036 OpenObjectStoreCursor(transaction,
2037 database_id,
2038 object_store_id,
2039 key_range,
2040 blink::WebIDBCursorDirectionNext,
2041 &s);
2042 if (!s.ok())
2043 return s;
2044 if (!start_cursor)
2045 return leveldb::Status::OK(); // Empty range == delete success.
2047 scoped_ptr<IndexedDBBackingStore::Cursor> end_cursor =
2048 OpenObjectStoreCursor(transaction,
2049 database_id,
2050 object_store_id,
2051 key_range,
2052 blink::WebIDBCursorDirectionPrev,
2053 &s);
2055 if (!s.ok())
2056 return s;
2057 if (!end_cursor)
2058 return leveldb::Status::OK(); // Empty range == delete success.
2060 BlobEntryKey start_blob_key, end_blob_key;
2062 std::string start_key = ObjectStoreDataKey::Encode(
2063 database_id, object_store_id, start_cursor->key());
2064 base::StringPiece start_key_piece(start_key);
2065 if (!BlobEntryKey::FromObjectStoreDataKey(&start_key_piece, &start_blob_key))
2066 return InternalInconsistencyStatus();
2067 std::string stop_key = ObjectStoreDataKey::Encode(
2068 database_id, object_store_id, end_cursor->key());
2069 base::StringPiece stop_key_piece(stop_key);
2070 if (!BlobEntryKey::FromObjectStoreDataKey(&stop_key_piece, &end_blob_key))
2071 return InternalInconsistencyStatus();
2073 s = DeleteBlobsInRange(transaction,
2074 database_id,
2075 object_store_id,
2076 start_blob_key.Encode(),
2077 end_blob_key.Encode(),
2078 false);
2079 if (!s.ok())
2080 return s;
2081 s = DeleteRangeBasic(transaction->transaction(), start_key, stop_key, false);
2082 if (!s.ok())
2083 return s;
2084 start_key =
2085 ExistsEntryKey::Encode(database_id, object_store_id, start_cursor->key());
2086 stop_key =
2087 ExistsEntryKey::Encode(database_id, object_store_id, end_cursor->key());
2088 return DeleteRangeBasic(
2089 transaction->transaction(), start_key, stop_key, false);
2092 leveldb::Status IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
2093 IndexedDBBackingStore::Transaction* transaction,
2094 int64 database_id,
2095 int64 object_store_id,
2096 int64* key_generator_current_number) {
2097 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2098 return InvalidDBKeyStatus();
2099 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2101 const std::string key_generator_current_number_key =
2102 ObjectStoreMetaDataKey::Encode(
2103 database_id,
2104 object_store_id,
2105 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
2107 *key_generator_current_number = -1;
2108 std::string data;
2110 bool found = false;
2111 leveldb::Status s =
2112 leveldb_transaction->Get(key_generator_current_number_key, &data, &found);
2113 if (!s.ok()) {
2114 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2115 return s;
2117 if (found && !data.empty()) {
2118 StringPiece slice(data);
2119 if (!DecodeInt(&slice, key_generator_current_number) || !slice.empty()) {
2120 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2121 return InternalInconsistencyStatus();
2123 return s;
2126 // Previously, the key generator state was not stored explicitly
2127 // but derived from the maximum numeric key present in existing
2128 // data. This violates the spec as the data may be cleared but the
2129 // key generator state must be preserved.
2130 // TODO(jsbell): Fix this for all stores on database open?
2131 const std::string start_key =
2132 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
2133 const std::string stop_key =
2134 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
2136 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
2137 int64 max_numeric_key = 0;
2139 for (s = it->Seek(start_key);
2140 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
2141 s = it->Next()) {
2142 StringPiece slice(it->Key());
2143 ObjectStoreDataKey data_key;
2144 if (!ObjectStoreDataKey::Decode(&slice, &data_key) || !slice.empty()) {
2145 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2146 return InternalInconsistencyStatus();
2148 scoped_ptr<IndexedDBKey> user_key = data_key.user_key();
2149 if (user_key->type() == blink::WebIDBKeyTypeNumber) {
2150 int64 n = static_cast<int64>(user_key->number());
2151 if (n > max_numeric_key)
2152 max_numeric_key = n;
2156 if (s.ok())
2157 *key_generator_current_number = max_numeric_key + 1;
2158 else
2159 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2161 return s;
2164 leveldb::Status IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
2165 IndexedDBBackingStore::Transaction* transaction,
2166 int64 database_id,
2167 int64 object_store_id,
2168 int64 new_number,
2169 bool check_current) {
2170 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2171 return InvalidDBKeyStatus();
2173 if (check_current) {
2174 int64 current_number;
2175 leveldb::Status s = GetKeyGeneratorCurrentNumber(
2176 transaction, database_id, object_store_id, &current_number);
2177 if (!s.ok())
2178 return s;
2179 if (new_number <= current_number)
2180 return s;
2183 const std::string key_generator_current_number_key =
2184 ObjectStoreMetaDataKey::Encode(
2185 database_id,
2186 object_store_id,
2187 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
2188 PutInt(
2189 transaction->transaction(), key_generator_current_number_key, new_number);
2190 return leveldb::Status::OK();
2193 leveldb::Status IndexedDBBackingStore::KeyExistsInObjectStore(
2194 IndexedDBBackingStore::Transaction* transaction,
2195 int64 database_id,
2196 int64 object_store_id,
2197 const IndexedDBKey& key,
2198 RecordIdentifier* found_record_identifier,
2199 bool* found) {
2200 IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
2201 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2202 return InvalidDBKeyStatus();
2203 *found = false;
2204 const std::string leveldb_key =
2205 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
2206 std::string data;
2208 leveldb::Status s =
2209 transaction->transaction()->Get(leveldb_key, &data, found);
2210 if (!s.ok()) {
2211 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
2212 return s;
2214 if (!*found)
2215 return leveldb::Status::OK();
2216 if (!data.size()) {
2217 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
2218 return InternalInconsistencyStatus();
2221 int64 version;
2222 StringPiece slice(data);
2223 if (!DecodeVarInt(&slice, &version))
2224 return InternalInconsistencyStatus();
2226 std::string encoded_key;
2227 EncodeIDBKey(key, &encoded_key);
2228 found_record_identifier->Reset(encoded_key, version);
2229 return s;
2232 class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
2233 : public IndexedDBBackingStore::Transaction::ChainedBlobWriter {
2234 public:
2235 typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
2236 WriteDescriptorVec;
2237 ChainedBlobWriterImpl(
2238 int64 database_id,
2239 IndexedDBBackingStore* backing_store,
2240 WriteDescriptorVec* blobs,
2241 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback)
2242 : waiting_for_callback_(false),
2243 database_id_(database_id),
2244 backing_store_(backing_store),
2245 callback_(callback) {
2246 blobs_.swap(*blobs);
2247 iter_ = blobs_.begin();
2248 backing_store->task_runner()->PostTask(
2249 FROM_HERE, base::Bind(&ChainedBlobWriterImpl::WriteNextFile, this));
2252 void set_delegate(scoped_ptr<FileWriterDelegate> delegate) override {
2253 delegate_.reset(delegate.release());
2256 void ReportWriteCompletion(bool succeeded, int64 bytes_written) override {
2257 DCHECK(waiting_for_callback_);
2258 DCHECK(!succeeded || bytes_written >= 0);
2259 waiting_for_callback_ = false;
2260 if (delegate_.get()) // Only present for Blob, not File.
2261 content::BrowserThread::DeleteSoon(
2262 content::BrowserThread::IO, FROM_HERE, delegate_.release());
2263 if (aborted_self_ref_.get()) {
2264 aborted_self_ref_ = NULL;
2265 return;
2267 if (iter_->size() != -1 && iter_->size() != bytes_written)
2268 succeeded = false;
2269 if (succeeded) {
2270 ++iter_;
2271 WriteNextFile();
2272 } else {
2273 callback_->Run(false);
2277 void Abort() override {
2278 if (!waiting_for_callback_)
2279 return;
2280 aborted_self_ref_ = this;
2283 private:
2284 ~ChainedBlobWriterImpl() override { DCHECK(!waiting_for_callback_); }
2286 void WriteNextFile() {
2287 DCHECK(!waiting_for_callback_);
2288 DCHECK(!aborted_self_ref_.get());
2289 if (iter_ == blobs_.end()) {
2290 callback_->Run(true);
2291 return;
2292 } else {
2293 waiting_for_callback_ = true;
2294 if (!backing_store_->WriteBlobFile(database_id_, *iter_, this)) {
2295 waiting_for_callback_ = false;
2296 callback_->Run(false);
2297 return;
2302 bool waiting_for_callback_;
2303 scoped_refptr<ChainedBlobWriterImpl> aborted_self_ref_;
2304 WriteDescriptorVec blobs_;
2305 WriteDescriptorVec::const_iterator iter_;
2306 int64 database_id_;
2307 IndexedDBBackingStore* backing_store_;
2308 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback_;
2309 scoped_ptr<FileWriterDelegate> delegate_;
2311 DISALLOW_COPY_AND_ASSIGN(ChainedBlobWriterImpl);
2314 class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
2315 public base::RefCountedThreadSafe<LocalWriteClosure> {
2316 public:
2317 LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter*
2318 chained_blob_writer,
2319 base::SequencedTaskRunner* task_runner)
2320 : chained_blob_writer_(chained_blob_writer),
2321 task_runner_(task_runner),
2322 bytes_written_(0) {}
2324 void Run(base::File::Error rv,
2325 int64 bytes,
2326 FileWriterDelegate::WriteProgressStatus write_status) {
2327 DCHECK_GE(bytes, 0);
2328 bytes_written_ += bytes;
2329 if (write_status == FileWriterDelegate::SUCCESS_IO_PENDING)
2330 return; // We don't care about progress events.
2331 if (rv == base::File::FILE_OK) {
2332 DCHECK_EQ(write_status, FileWriterDelegate::SUCCESS_COMPLETED);
2333 } else {
2334 DCHECK(write_status == FileWriterDelegate::ERROR_WRITE_STARTED ||
2335 write_status == FileWriterDelegate::ERROR_WRITE_NOT_STARTED);
2338 bool success = write_status == FileWriterDelegate::SUCCESS_COMPLETED;
2339 if (success && !bytes_written_) {
2340 // LocalFileStreamWriter only creates a file if data is actually written.
2341 // If none was then create one now.
2342 task_runner_->PostTask(
2343 FROM_HERE, base::Bind(&LocalWriteClosure::CreateEmptyFile, this));
2344 } else if (success && !last_modified_.is_null()) {
2345 task_runner_->PostTask(
2346 FROM_HERE, base::Bind(&LocalWriteClosure::UpdateTimeStamp, this));
2347 } else {
2348 task_runner_->PostTask(
2349 FROM_HERE,
2350 base::Bind(&IndexedDBBackingStore::Transaction::ChainedBlobWriter::
2351 ReportWriteCompletion,
2352 chained_blob_writer_,
2353 success,
2354 bytes_written_));
2358 void WriteBlobToFileOnIOThread(const FilePath& file_path,
2359 const GURL& blob_url,
2360 const base::Time& last_modified,
2361 net::URLRequestContext* request_context) {
2362 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
2363 scoped_ptr<storage::FileStreamWriter> writer(
2364 storage::FileStreamWriter::CreateForLocalFile(
2365 task_runner_.get(),
2366 file_path,
2368 storage::FileStreamWriter::CREATE_NEW_FILE));
2369 scoped_ptr<FileWriterDelegate> delegate(new FileWriterDelegate(
2370 writer.Pass(), storage::FlushPolicy::FLUSH_ON_COMPLETION));
2372 DCHECK(blob_url.is_valid());
2373 scoped_ptr<net::URLRequest> blob_request(request_context->CreateRequest(
2374 blob_url, net::DEFAULT_PRIORITY, delegate.get()));
2376 this->file_path_ = file_path;
2377 this->last_modified_ = last_modified;
2379 delegate->Start(blob_request.Pass(),
2380 base::Bind(&LocalWriteClosure::Run, this));
2381 chained_blob_writer_->set_delegate(delegate.Pass());
2384 private:
2385 virtual ~LocalWriteClosure() {
2386 // Make sure the last reference to a ChainedBlobWriter is released (and
2387 // deleted) on the IDB thread since it owns a transaction which has thread
2388 // affinity.
2389 IndexedDBBackingStore::Transaction::ChainedBlobWriter* raw_tmp =
2390 chained_blob_writer_.get();
2391 raw_tmp->AddRef();
2392 chained_blob_writer_ = NULL;
2393 task_runner_->ReleaseSoon(FROM_HERE, raw_tmp);
2395 friend class base::RefCountedThreadSafe<LocalWriteClosure>;
2397 // If necessary, update the timestamps on the file as a final
2398 // step before reporting success.
2399 void UpdateTimeStamp() {
2400 DCHECK(task_runner_->RunsTasksOnCurrentThread());
2401 if (!base::TouchFile(file_path_, last_modified_, last_modified_)) {
2402 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2404 chained_blob_writer_->ReportWriteCompletion(true, bytes_written_);
2407 // Create an empty file.
2408 void CreateEmptyFile() {
2409 DCHECK(task_runner_->RunsTasksOnCurrentThread());
2410 base::File file(file_path_,
2411 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
2412 bool success = file.created();
2413 if (success && !last_modified_.is_null() &&
2414 !file.SetTimes(last_modified_, last_modified_)) {
2415 // TODO(cmumford): Complain quietly; timestamp's probably not vital.
2417 file.Close();
2418 chained_blob_writer_->ReportWriteCompletion(success, bytes_written_);
2421 scoped_refptr<IndexedDBBackingStore::Transaction::ChainedBlobWriter>
2422 chained_blob_writer_;
2423 scoped_refptr<base::SequencedTaskRunner> task_runner_;
2424 int64 bytes_written_;
2426 base::FilePath file_path_;
2427 base::Time last_modified_;
2429 DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure);
2432 bool IndexedDBBackingStore::WriteBlobFile(
2433 int64 database_id,
2434 const Transaction::WriteDescriptor& descriptor,
2435 Transaction::ChainedBlobWriter* chained_blob_writer) {
2437 if (!MakeIDBBlobDirectory(blob_path_, database_id, descriptor.key()))
2438 return false;
2440 FilePath path = GetBlobFileName(database_id, descriptor.key());
2442 if (descriptor.is_file() && !descriptor.file_path().empty()) {
2443 if (!base::CopyFile(descriptor.file_path(), path))
2444 return false;
2446 base::File::Info info;
2447 if (base::GetFileInfo(descriptor.file_path(), &info)) {
2448 if (descriptor.size() != -1) {
2449 if (descriptor.size() != info.size)
2450 return false;
2451 // The round-trip can be lossy; round to nearest millisecond.
2452 int64 delta = (descriptor.last_modified() -
2453 info.last_modified).InMilliseconds();
2454 if (std::abs(delta) > 1)
2455 return false;
2457 if (!base::TouchFile(path, info.last_accessed, info.last_modified)) {
2458 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2460 } else {
2461 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2464 task_runner_->PostTask(
2465 FROM_HERE,
2466 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion,
2467 chained_blob_writer,
2468 true,
2469 info.size));
2470 } else {
2471 DCHECK(descriptor.url().is_valid());
2472 scoped_refptr<LocalWriteClosure> write_closure(
2473 new LocalWriteClosure(chained_blob_writer, task_runner_.get()));
2474 content::BrowserThread::PostTask(
2475 content::BrowserThread::IO,
2476 FROM_HERE,
2477 base::Bind(&LocalWriteClosure::WriteBlobToFileOnIOThread,
2478 write_closure.get(),
2479 path,
2480 descriptor.url(),
2481 descriptor.last_modified(),
2482 request_context_));
2484 return true;
2487 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
2488 int64 blob_key) {
2489 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2490 bool all_blobs = blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2491 DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2492 scoped_refptr<LevelDBTransaction> transaction =
2493 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2495 BlobJournalType live_blob_journal, primary_journal;
2496 if (!GetLiveBlobJournal(transaction.get(), &live_blob_journal).ok())
2497 return;
2498 DCHECK(!live_blob_journal.empty());
2499 if (!GetPrimaryBlobJournal(transaction.get(), &primary_journal).ok())
2500 return;
2502 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2503 // remove all entries with database_id from the live_blob journal and add only
2504 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2505 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2506 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2507 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2508 // matching (database_id, blob_key) tuple, we should move it to the primary
2509 // journal.
2510 BlobJournalType new_live_blob_journal;
2511 for (BlobJournalType::iterator journal_iter = live_blob_journal.begin();
2512 journal_iter != live_blob_journal.end();
2513 ++journal_iter) {
2514 int64 current_database_id = journal_iter->first;
2515 int64 current_blob_key = journal_iter->second;
2516 bool current_all_blobs =
2517 current_blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2518 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id) ||
2519 current_all_blobs);
2520 if (current_database_id == database_id &&
2521 (all_blobs || current_all_blobs || blob_key == current_blob_key)) {
2522 if (!all_blobs) {
2523 primary_journal.push_back(
2524 std::make_pair(database_id, current_blob_key));
2525 if (current_all_blobs)
2526 new_live_blob_journal.push_back(*journal_iter);
2527 new_live_blob_journal.insert(new_live_blob_journal.end(),
2528 ++journal_iter,
2529 live_blob_journal.end()); // All the rest.
2530 break;
2532 } else {
2533 new_live_blob_journal.push_back(*journal_iter);
2536 if (all_blobs) {
2537 primary_journal.push_back(
2538 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
2540 UpdatePrimaryBlobJournal(transaction.get(), primary_journal);
2541 UpdateLiveBlobJournal(transaction.get(), new_live_blob_journal);
2542 transaction->Commit();
2543 // We could just do the deletions/cleaning here, but if there are a lot of
2544 // blobs about to be garbage collected, it'd be better to wait and do them all
2545 // at once.
2546 StartJournalCleaningTimer();
2549 // The this reference is a raw pointer that's declared Unretained inside the
2550 // timer code, so this won't confuse IndexedDBFactory's check for
2551 // HasLastBackingStoreReference. It's safe because if the backing store is
2552 // deleted, the timer will automatically be canceled on destruction.
2553 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2554 journal_cleaning_timer_.Start(
2555 FROM_HERE,
2556 base::TimeDelta::FromSeconds(5),
2557 this,
2558 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn);
2561 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
2562 FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id,
2563 int64 key) const {
2564 return GetBlobFileNameForKey(blob_path_, database_id, key);
2567 static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
2568 const std::string& stop_key,
2569 int64 index_id,
2570 unsigned char meta_data_type) {
2571 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
2572 return false;
2574 StringPiece slice(it->Key());
2575 IndexMetaDataKey meta_data_key;
2576 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2577 DCHECK(ok);
2578 if (meta_data_key.IndexId() != index_id)
2579 return false;
2580 if (meta_data_key.meta_data_type() != meta_data_type)
2581 return false;
2582 return true;
2585 // TODO(jsbell): This should do some error handling rather than plowing ahead
2586 // when bad data is encountered.
2587 leveldb::Status IndexedDBBackingStore::GetIndexes(
2588 int64 database_id,
2589 int64 object_store_id,
2590 IndexedDBObjectStoreMetadata::IndexMap* indexes) {
2591 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2592 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2593 return InvalidDBKeyStatus();
2594 const std::string start_key =
2595 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0);
2596 const std::string stop_key =
2597 IndexMetaDataKey::Encode(database_id, object_store_id + 1, 0, 0);
2599 DCHECK(indexes->empty());
2601 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
2602 leveldb::Status s = it->Seek(start_key);
2603 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
2604 StringPiece slice(it->Key());
2605 IndexMetaDataKey meta_data_key;
2606 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2607 DCHECK(ok);
2608 if (meta_data_key.meta_data_type() != IndexMetaDataKey::NAME) {
2609 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2610 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2611 // the load.
2612 s = it->Next();
2613 if (!s.ok())
2614 break;
2615 continue;
2618 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2619 // simplify.
2620 int64 index_id = meta_data_key.IndexId();
2621 base::string16 index_name;
2623 StringPiece slice(it->Value());
2624 if (!DecodeString(&slice, &index_name) || !slice.empty())
2625 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2628 s = it->Next(); // unique flag
2629 if (!s.ok())
2630 break;
2631 if (!CheckIndexAndMetaDataKey(
2632 it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
2633 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2634 break;
2636 bool index_unique;
2638 StringPiece slice(it->Value());
2639 if (!DecodeBool(&slice, &index_unique) || !slice.empty())
2640 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2643 s = it->Next(); // key_path
2644 if (!s.ok())
2645 break;
2646 if (!CheckIndexAndMetaDataKey(
2647 it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
2648 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2649 break;
2651 IndexedDBKeyPath key_path;
2653 StringPiece slice(it->Value());
2654 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
2655 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2658 s = it->Next(); // [optional] multi_entry flag
2659 if (!s.ok())
2660 break;
2661 bool index_multi_entry = false;
2662 if (CheckIndexAndMetaDataKey(
2663 it.get(), stop_key, index_id, IndexMetaDataKey::MULTI_ENTRY)) {
2664 StringPiece slice(it->Value());
2665 if (!DecodeBool(&slice, &index_multi_entry) || !slice.empty())
2666 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2668 s = it->Next();
2669 if (!s.ok())
2670 break;
2673 (*indexes)[index_id] = IndexedDBIndexMetadata(
2674 index_name, index_id, key_path, index_unique, index_multi_entry);
2677 if (!s.ok())
2678 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES);
2680 return s;
2683 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) const {
2684 FilePath path = GetBlobFileName(database_id, key);
2685 return base::DeleteFile(path, false);
2688 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) const {
2689 FilePath path = GetBlobDirectoryName(blob_path_, database_id);
2690 return base::DeleteFile(path, true);
2693 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournalEntries(
2694 const BlobJournalType& journal) const {
2695 if (journal.empty())
2696 return leveldb::Status::OK();
2697 for (const auto& entry : journal) {
2698 int64 database_id = entry.first;
2699 int64 blob_key = entry.second;
2700 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2701 if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
2702 if (!RemoveBlobDirectory(database_id))
2703 return IOErrorStatus();
2704 } else {
2705 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2706 if (!RemoveBlobFile(database_id, blob_key))
2707 return IOErrorStatus();
2710 return leveldb::Status::OK();
2713 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal(
2714 const std::string& level_db_key) const {
2715 DCHECK(!committing_transaction_count_);
2716 leveldb::Status s;
2717 scoped_refptr<LevelDBTransaction> journal_transaction =
2718 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2719 BlobJournalType journal;
2721 s = GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
2722 if (!s.ok())
2723 return s;
2724 if (journal.empty())
2725 return leveldb::Status::OK();
2726 s = CleanUpBlobJournalEntries(journal);
2727 if (!s.ok())
2728 return s;
2729 ClearBlobJournal(journal_transaction.get(), level_db_key);
2730 return journal_transaction->Commit();
2733 leveldb::Status IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
2734 int64 database_id,
2735 const std::string& object_store_data_key,
2736 IndexedDBValue* value) {
2737 BlobChangeRecord* change_record = NULL;
2738 BlobChangeMap::const_iterator blob_iter =
2739 blob_change_map_.find(object_store_data_key);
2740 if (blob_iter != blob_change_map_.end()) {
2741 change_record = blob_iter->second;
2742 } else {
2743 blob_iter = incognito_blob_map_.find(object_store_data_key);
2744 if (blob_iter != incognito_blob_map_.end())
2745 change_record = blob_iter->second;
2747 if (change_record) {
2748 // Either we haven't written the blob to disk yet or we're in incognito
2749 // mode, so we have to send back the one they sent us. This change record
2750 // includes the original UUID.
2751 value->blob_info = change_record->blob_info();
2752 return leveldb::Status::OK();
2755 BlobEntryKey blob_entry_key;
2756 StringPiece leveldb_key_piece(object_store_data_key);
2757 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
2758 &blob_entry_key)) {
2759 NOTREACHED();
2760 return InternalInconsistencyStatus();
2762 std::string encoded_key = blob_entry_key.Encode();
2763 bool found;
2764 std::string encoded_value;
2765 leveldb::Status s = transaction()->Get(encoded_key, &encoded_value, &found);
2766 if (!s.ok())
2767 return s;
2768 if (found) {
2769 if (!DecodeBlobData(encoded_value, &value->blob_info)) {
2770 INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD);
2771 return InternalInconsistencyStatus();
2773 for (auto& entry : value->blob_info) {
2774 entry.set_file_path(
2775 backing_store_->GetBlobFileName(database_id, entry.key()));
2776 entry.set_mark_used_callback(
2777 backing_store_->active_blob_registry()->GetAddBlobRefCallback(
2778 database_id, entry.key()));
2779 entry.set_release_callback(
2780 backing_store_->active_blob_registry()->GetFinalReleaseCallback(
2781 database_id, entry.key()));
2782 if (entry.is_file() && !entry.file_path().empty()) {
2783 base::File::Info info;
2784 if (base::GetFileInfo(entry.file_path(), &info)) {
2785 // This should always work, but it isn't fatal if it doesn't; it just
2786 // means a potential slow synchronous call from the renderer later.
2787 entry.set_last_modified(info.last_modified);
2788 entry.set_size(info.size);
2793 return leveldb::Status::OK();
2796 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2797 // While a transaction is busy it is not safe to clean the journal.
2798 if (committing_transaction_count_ > 0)
2799 StartJournalCleaningTimer();
2800 else
2801 CleanUpBlobJournal(BlobJournalKey::Encode());
2804 WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId(
2805 LevelDBTransaction* transaction,
2806 int64 database_id,
2807 int64 object_store_id,
2808 int64 index_id) {
2809 int64 max_index_id = -1;
2810 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
2811 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
2812 bool found = false;
2813 leveldb::Status s =
2814 GetInt(transaction, max_index_id_key, &max_index_id, &found);
2815 if (!s.ok()) {
2816 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2817 return s;
2819 if (!found)
2820 max_index_id = kMinimumIndexId;
2822 if (index_id <= max_index_id) {
2823 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2824 return InternalInconsistencyStatus();
2827 PutInt(transaction, max_index_id_key, index_id);
2828 return s;
2831 leveldb::Status IndexedDBBackingStore::CreateIndex(
2832 IndexedDBBackingStore::Transaction* transaction,
2833 int64 database_id,
2834 int64 object_store_id,
2835 int64 index_id,
2836 const base::string16& name,
2837 const IndexedDBKeyPath& key_path,
2838 bool is_unique,
2839 bool is_multi_entry) {
2840 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2841 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2842 return InvalidDBKeyStatus();
2843 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2844 leveldb::Status s = SetMaxIndexId(
2845 leveldb_transaction, database_id, object_store_id, index_id);
2847 if (!s.ok())
2848 return s;
2850 const std::string name_key = IndexMetaDataKey::Encode(
2851 database_id, object_store_id, index_id, IndexMetaDataKey::NAME);
2852 const std::string unique_key = IndexMetaDataKey::Encode(
2853 database_id, object_store_id, index_id, IndexMetaDataKey::UNIQUE);
2854 const std::string key_path_key = IndexMetaDataKey::Encode(
2855 database_id, object_store_id, index_id, IndexMetaDataKey::KEY_PATH);
2856 const std::string multi_entry_key = IndexMetaDataKey::Encode(
2857 database_id, object_store_id, index_id, IndexMetaDataKey::MULTI_ENTRY);
2859 PutString(leveldb_transaction, name_key, name);
2860 PutBool(leveldb_transaction, unique_key, is_unique);
2861 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
2862 PutBool(leveldb_transaction, multi_entry_key, is_multi_entry);
2863 return s;
2866 leveldb::Status IndexedDBBackingStore::DeleteIndex(
2867 IndexedDBBackingStore::Transaction* transaction,
2868 int64 database_id,
2869 int64 object_store_id,
2870 int64 index_id) {
2871 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2872 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2873 return InvalidDBKeyStatus();
2874 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2876 const std::string index_meta_data_start =
2877 IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0);
2878 const std::string index_meta_data_end =
2879 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2880 leveldb::Status s = DeleteRangeBasic(
2881 leveldb_transaction, index_meta_data_start, index_meta_data_end, true);
2883 if (s.ok()) {
2884 const std::string index_data_start =
2885 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
2886 const std::string index_data_end =
2887 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2888 s = DeleteRangeBasic(
2889 leveldb_transaction, index_data_start, index_data_end, true);
2892 if (!s.ok())
2893 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX);
2895 return s;
2898 leveldb::Status IndexedDBBackingStore::PutIndexDataForRecord(
2899 IndexedDBBackingStore::Transaction* transaction,
2900 int64 database_id,
2901 int64 object_store_id,
2902 int64 index_id,
2903 const IndexedDBKey& key,
2904 const RecordIdentifier& record_identifier) {
2905 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2906 DCHECK(key.IsValid());
2907 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2908 return InvalidDBKeyStatus();
2910 std::string encoded_key;
2911 EncodeIDBKey(key, &encoded_key);
2913 const std::string index_data_key =
2914 IndexDataKey::Encode(database_id,
2915 object_store_id,
2916 index_id,
2917 encoded_key,
2918 record_identifier.primary_key(),
2921 std::string data;
2922 EncodeVarInt(record_identifier.version(), &data);
2923 data.append(record_identifier.primary_key());
2925 transaction->transaction()->Put(index_data_key, &data);
2926 return leveldb::Status::OK();
2929 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
2930 const std::string& target,
2931 std::string* found_key,
2932 leveldb::Status* s) {
2933 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
2934 *s = it->Seek(target);
2935 if (!s->ok())
2936 return false;
2938 if (!it->IsValid()) {
2939 *s = it->SeekToLast();
2940 if (!s->ok() || !it->IsValid())
2941 return false;
2944 while (CompareIndexKeys(it->Key(), target) > 0) {
2945 *s = it->Prev();
2946 if (!s->ok() || !it->IsValid())
2947 return false;
2950 do {
2951 *found_key = it->Key().as_string();
2953 // There can be several index keys that compare equal. We want the last one.
2954 *s = it->Next();
2955 } while (s->ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
2957 return true;
2960 static leveldb::Status VersionExists(LevelDBTransaction* transaction,
2961 int64 database_id,
2962 int64 object_store_id,
2963 int64 version,
2964 const std::string& encoded_primary_key,
2965 bool* exists) {
2966 const std::string key =
2967 ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
2968 std::string data;
2970 leveldb::Status s = transaction->Get(key, &data, exists);
2971 if (!s.ok()) {
2972 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS);
2973 return s;
2975 if (!*exists)
2976 return s;
2978 StringPiece slice(data);
2979 int64 decoded;
2980 if (!DecodeInt(&slice, &decoded) || !slice.empty())
2981 return InternalInconsistencyStatus();
2982 *exists = (decoded == version);
2983 return s;
2986 leveldb::Status IndexedDBBackingStore::FindKeyInIndex(
2987 IndexedDBBackingStore::Transaction* transaction,
2988 int64 database_id,
2989 int64 object_store_id,
2990 int64 index_id,
2991 const IndexedDBKey& key,
2992 std::string* found_encoded_primary_key,
2993 bool* found) {
2994 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
2995 DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id));
2997 DCHECK(found_encoded_primary_key->empty());
2998 *found = false;
3000 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3001 const std::string leveldb_key =
3002 IndexDataKey::Encode(database_id, object_store_id, index_id, key);
3003 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
3004 leveldb::Status s = it->Seek(leveldb_key);
3005 if (!s.ok()) {
3006 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
3007 return s;
3010 for (;;) {
3011 if (!it->IsValid())
3012 return leveldb::Status::OK();
3013 if (CompareIndexKeys(it->Key(), leveldb_key) > 0)
3014 return leveldb::Status::OK();
3016 StringPiece slice(it->Value());
3018 int64 version;
3019 if (!DecodeVarInt(&slice, &version)) {
3020 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
3021 return InternalInconsistencyStatus();
3023 *found_encoded_primary_key = slice.as_string();
3025 bool exists = false;
3026 s = VersionExists(leveldb_transaction,
3027 database_id,
3028 object_store_id,
3029 version,
3030 *found_encoded_primary_key,
3031 &exists);
3032 if (!s.ok())
3033 return s;
3034 if (!exists) {
3035 // Delete stale index data entry and continue.
3036 leveldb_transaction->Remove(it->Key());
3037 s = it->Next();
3038 continue;
3040 *found = true;
3041 return s;
3045 leveldb::Status IndexedDBBackingStore::GetPrimaryKeyViaIndex(
3046 IndexedDBBackingStore::Transaction* transaction,
3047 int64 database_id,
3048 int64 object_store_id,
3049 int64 index_id,
3050 const IndexedDBKey& key,
3051 scoped_ptr<IndexedDBKey>* primary_key) {
3052 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
3053 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3054 return InvalidDBKeyStatus();
3056 bool found = false;
3057 std::string found_encoded_primary_key;
3058 leveldb::Status s = FindKeyInIndex(transaction,
3059 database_id,
3060 object_store_id,
3061 index_id,
3062 key,
3063 &found_encoded_primary_key,
3064 &found);
3065 if (!s.ok()) {
3066 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
3067 return s;
3069 if (!found)
3070 return s;
3071 if (!found_encoded_primary_key.size()) {
3072 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
3073 return InvalidDBKeyStatus();
3076 StringPiece slice(found_encoded_primary_key);
3077 if (DecodeIDBKey(&slice, primary_key) && slice.empty())
3078 return s;
3079 else
3080 return InvalidDBKeyStatus();
3083 leveldb::Status IndexedDBBackingStore::KeyExistsInIndex(
3084 IndexedDBBackingStore::Transaction* transaction,
3085 int64 database_id,
3086 int64 object_store_id,
3087 int64 index_id,
3088 const IndexedDBKey& index_key,
3089 scoped_ptr<IndexedDBKey>* found_primary_key,
3090 bool* exists) {
3091 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
3092 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3093 return InvalidDBKeyStatus();
3095 *exists = false;
3096 std::string found_encoded_primary_key;
3097 leveldb::Status s = FindKeyInIndex(transaction,
3098 database_id,
3099 object_store_id,
3100 index_id,
3101 index_key,
3102 &found_encoded_primary_key,
3103 exists);
3104 if (!s.ok()) {
3105 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
3106 return s;
3108 if (!*exists)
3109 return leveldb::Status::OK();
3110 if (found_encoded_primary_key.empty()) {
3111 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
3112 return InvalidDBKeyStatus();
3115 StringPiece slice(found_encoded_primary_key);
3116 if (DecodeIDBKey(&slice, found_primary_key) && slice.empty())
3117 return s;
3118 else
3119 return InvalidDBKeyStatus();
3122 IndexedDBBackingStore::Cursor::Cursor(
3123 const IndexedDBBackingStore::Cursor* other)
3124 : backing_store_(other->backing_store_),
3125 transaction_(other->transaction_),
3126 database_id_(other->database_id_),
3127 cursor_options_(other->cursor_options_),
3128 current_key_(new IndexedDBKey(*other->current_key_)) {
3129 if (other->iterator_) {
3130 iterator_ = transaction_->transaction()->CreateIterator();
3132 if (other->iterator_->IsValid()) {
3133 leveldb::Status s = iterator_->Seek(other->iterator_->Key());
3134 // TODO(cmumford): Handle this error (crbug.com/363397)
3135 DCHECK(iterator_->IsValid());
3140 IndexedDBBackingStore::Cursor::Cursor(
3141 scoped_refptr<IndexedDBBackingStore> backing_store,
3142 IndexedDBBackingStore::Transaction* transaction,
3143 int64 database_id,
3144 const CursorOptions& cursor_options)
3145 : backing_store_(backing_store.get()),
3146 transaction_(transaction),
3147 database_id_(database_id),
3148 cursor_options_(cursor_options) {
3150 IndexedDBBackingStore::Cursor::~Cursor() {}
3152 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) {
3153 iterator_ = transaction_->transaction()->CreateIterator();
3154 if (cursor_options_.forward)
3155 *s = iterator_->Seek(cursor_options_.low_key);
3156 else
3157 *s = iterator_->Seek(cursor_options_.high_key);
3158 if (!s->ok())
3159 return false;
3161 return Continue(0, READY, s);
3164 bool IndexedDBBackingStore::Cursor::Advance(uint32 count, leveldb::Status* s) {
3165 *s = leveldb::Status::OK();
3166 while (count--) {
3167 if (!Continue(s))
3168 return false;
3170 return true;
3173 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
3174 const IndexedDBKey* primary_key,
3175 IteratorState next_state,
3176 leveldb::Status* s) {
3177 DCHECK(!key || key->IsValid());
3178 DCHECK(!primary_key || primary_key->IsValid());
3179 *s = leveldb::Status::OK();
3181 // TODO(alecflett): avoid a copy here?
3182 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3184 // When iterating with PrevNoDuplicate, spec requires that the
3185 // value we yield for each key is the first duplicate in forwards
3186 // order.
3187 IndexedDBKey last_duplicate_key;
3189 bool forward = cursor_options_.forward;
3190 bool first_iteration_forward = forward;
3191 bool flipped = false;
3193 for (;;) {
3194 if (next_state == SEEK) {
3195 // TODO(jsbell): Optimize seeking for reverse cursors as well.
3196 if (first_iteration_forward && key) {
3197 first_iteration_forward = false;
3198 std::string leveldb_key;
3199 if (primary_key) {
3200 leveldb_key = EncodeKey(*key, *primary_key);
3201 } else {
3202 leveldb_key = EncodeKey(*key);
3204 *s = iterator_->Seek(leveldb_key);
3205 } else if (forward) {
3206 *s = iterator_->Next();
3207 } else {
3208 *s = iterator_->Prev();
3210 if (!s->ok())
3211 return false;
3212 } else {
3213 next_state = SEEK; // for subsequent iterations
3216 if (!iterator_->IsValid()) {
3217 if (!forward && last_duplicate_key.IsValid()) {
3218 // We need to walk forward because we hit the end of
3219 // the data.
3220 forward = true;
3221 flipped = true;
3222 continue;
3225 return false;
3228 if (IsPastBounds()) {
3229 if (!forward && last_duplicate_key.IsValid()) {
3230 // We need to walk forward because now we're beyond the
3231 // bounds defined by the cursor.
3232 forward = true;
3233 flipped = true;
3234 continue;
3237 return false;
3240 if (!HaveEnteredRange())
3241 continue;
3243 // The row may not load because there's a stale entry in the
3244 // index. This is not fatal.
3245 if (!LoadCurrentRow())
3246 continue;
3248 if (key) {
3249 if (forward) {
3250 if (primary_key && current_key_->Equals(*key) &&
3251 this->primary_key().IsLessThan(*primary_key))
3252 continue;
3253 if (!flipped && current_key_->IsLessThan(*key))
3254 continue;
3255 } else {
3256 if (primary_key && key->Equals(*current_key_) &&
3257 primary_key->IsLessThan(this->primary_key()))
3258 continue;
3259 if (key->IsLessThan(*current_key_))
3260 continue;
3264 if (cursor_options_.unique) {
3265 if (previous_key.IsValid() && current_key_->Equals(previous_key)) {
3266 // We should never be able to walk forward all the way
3267 // to the previous key.
3268 DCHECK(!last_duplicate_key.IsValid());
3269 continue;
3272 if (!forward) {
3273 if (!last_duplicate_key.IsValid()) {
3274 last_duplicate_key = *current_key_;
3275 continue;
3278 // We need to walk forward because we hit the boundary
3279 // between key ranges.
3280 if (!last_duplicate_key.Equals(*current_key_)) {
3281 forward = true;
3282 flipped = true;
3283 continue;
3286 continue;
3289 break;
3292 DCHECK(!last_duplicate_key.IsValid() ||
3293 (forward && last_duplicate_key.Equals(*current_key_)));
3294 return true;
3297 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3298 if (cursor_options_.forward) {
3299 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3300 if (cursor_options_.low_open) {
3301 return compare > 0;
3303 return compare >= 0;
3305 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3306 if (cursor_options_.high_open) {
3307 return compare < 0;
3309 return compare <= 0;
3312 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
3313 if (cursor_options_.forward) {
3314 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3315 if (cursor_options_.high_open) {
3316 return compare >= 0;
3318 return compare > 0;
3320 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3321 if (cursor_options_.low_open) {
3322 return compare <= 0;
3324 return compare < 0;
3327 const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const {
3328 return *current_key_;
3331 const IndexedDBBackingStore::RecordIdentifier&
3332 IndexedDBBackingStore::Cursor::record_identifier() const {
3333 return record_identifier_;
3336 class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3337 public:
3338 ObjectStoreKeyCursorImpl(
3339 scoped_refptr<IndexedDBBackingStore> backing_store,
3340 IndexedDBBackingStore::Transaction* transaction,
3341 int64 database_id,
3342 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3343 : IndexedDBBackingStore::Cursor(backing_store,
3344 transaction,
3345 database_id,
3346 cursor_options) {}
3348 Cursor* Clone() override { return new ObjectStoreKeyCursorImpl(this); }
3350 // IndexedDBBackingStore::Cursor
3351 IndexedDBValue* value() override {
3352 NOTREACHED();
3353 return NULL;
3355 bool LoadCurrentRow() override;
3357 protected:
3358 std::string EncodeKey(const IndexedDBKey& key) override {
3359 return ObjectStoreDataKey::Encode(
3360 cursor_options_.database_id, cursor_options_.object_store_id, key);
3362 std::string EncodeKey(const IndexedDBKey& key,
3363 const IndexedDBKey& primary_key) override {
3364 NOTREACHED();
3365 return std::string();
3368 private:
3369 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other)
3370 : IndexedDBBackingStore::Cursor(other) {}
3372 DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl);
3375 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
3376 StringPiece slice(iterator_->Key());
3377 ObjectStoreDataKey object_store_data_key;
3378 if (!ObjectStoreDataKey::Decode(&slice, &object_store_data_key)) {
3379 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3380 return false;
3383 current_key_ = object_store_data_key.user_key();
3385 int64 version;
3386 slice = StringPiece(iterator_->Value());
3387 if (!DecodeVarInt(&slice, &version)) {
3388 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3389 return false;
3392 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3393 std::string encoded_key;
3394 EncodeIDBKey(*current_key_, &encoded_key);
3395 record_identifier_.Reset(encoded_key, version);
3397 return true;
3400 class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
3401 public:
3402 ObjectStoreCursorImpl(
3403 scoped_refptr<IndexedDBBackingStore> backing_store,
3404 IndexedDBBackingStore::Transaction* transaction,
3405 int64 database_id,
3406 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3407 : IndexedDBBackingStore::Cursor(backing_store,
3408 transaction,
3409 database_id,
3410 cursor_options) {}
3412 Cursor* Clone() override { return new ObjectStoreCursorImpl(this); }
3414 // IndexedDBBackingStore::Cursor
3415 IndexedDBValue* value() override { return &current_value_; }
3416 bool LoadCurrentRow() override;
3418 protected:
3419 std::string EncodeKey(const IndexedDBKey& key) override {
3420 return ObjectStoreDataKey::Encode(
3421 cursor_options_.database_id, cursor_options_.object_store_id, key);
3423 std::string EncodeKey(const IndexedDBKey& key,
3424 const IndexedDBKey& primary_key) override {
3425 NOTREACHED();
3426 return std::string();
3429 private:
3430 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other)
3431 : IndexedDBBackingStore::Cursor(other),
3432 current_value_(other->current_value_) {}
3434 IndexedDBValue current_value_;
3436 DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl);
3439 bool ObjectStoreCursorImpl::LoadCurrentRow() {
3440 StringPiece key_slice(iterator_->Key());
3441 ObjectStoreDataKey object_store_data_key;
3442 if (!ObjectStoreDataKey::Decode(&key_slice, &object_store_data_key)) {
3443 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3444 return false;
3447 current_key_ = object_store_data_key.user_key();
3449 int64 version;
3450 StringPiece value_slice = StringPiece(iterator_->Value());
3451 if (!DecodeVarInt(&value_slice, &version)) {
3452 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3453 return false;
3456 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3457 std::string encoded_key;
3458 EncodeIDBKey(*current_key_, &encoded_key);
3459 record_identifier_.Reset(encoded_key, version);
3461 if (!transaction_->GetBlobInfoForRecord(database_id_,
3462 iterator_->Key().as_string(),
3463 &current_value_).ok()) {
3464 return false;
3466 current_value_.bits = value_slice.as_string();
3467 return true;
3470 class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3471 public:
3472 IndexKeyCursorImpl(
3473 scoped_refptr<IndexedDBBackingStore> backing_store,
3474 IndexedDBBackingStore::Transaction* transaction,
3475 int64 database_id,
3476 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3477 : IndexedDBBackingStore::Cursor(backing_store,
3478 transaction,
3479 database_id,
3480 cursor_options) {}
3482 Cursor* Clone() override { return new IndexKeyCursorImpl(this); }
3484 // IndexedDBBackingStore::Cursor
3485 IndexedDBValue* value() override {
3486 NOTREACHED();
3487 return NULL;
3489 const IndexedDBKey& primary_key() const override { return *primary_key_; }
3490 const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3491 const override {
3492 NOTREACHED();
3493 return record_identifier_;
3495 bool LoadCurrentRow() override;
3497 protected:
3498 std::string EncodeKey(const IndexedDBKey& key) override {
3499 return IndexDataKey::Encode(cursor_options_.database_id,
3500 cursor_options_.object_store_id,
3501 cursor_options_.index_id,
3502 key);
3504 std::string EncodeKey(const IndexedDBKey& key,
3505 const IndexedDBKey& primary_key) override {
3506 return IndexDataKey::Encode(cursor_options_.database_id,
3507 cursor_options_.object_store_id,
3508 cursor_options_.index_id,
3509 key,
3510 primary_key);
3513 private:
3514 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl* other)
3515 : IndexedDBBackingStore::Cursor(other),
3516 primary_key_(new IndexedDBKey(*other->primary_key_)) {}
3518 scoped_ptr<IndexedDBKey> primary_key_;
3520 DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl);
3523 bool IndexKeyCursorImpl::LoadCurrentRow() {
3524 StringPiece slice(iterator_->Key());
3525 IndexDataKey index_data_key;
3526 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3527 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3528 return false;
3531 current_key_ = index_data_key.user_key();
3532 DCHECK(current_key_);
3534 slice = StringPiece(iterator_->Value());
3535 int64 index_data_version;
3536 if (!DecodeVarInt(&slice, &index_data_version)) {
3537 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3538 return false;
3541 if (!DecodeIDBKey(&slice, &primary_key_) || !slice.empty()) {
3542 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3543 return false;
3546 std::string primary_leveldb_key =
3547 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3548 index_data_key.ObjectStoreId(),
3549 *primary_key_);
3551 std::string result;
3552 bool found = false;
3553 leveldb::Status s =
3554 transaction_->transaction()->Get(primary_leveldb_key, &result, &found);
3555 if (!s.ok()) {
3556 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3557 return false;
3559 if (!found) {
3560 transaction_->transaction()->Remove(iterator_->Key());
3561 return false;
3563 if (!result.size()) {
3564 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3565 return false;
3568 int64 object_store_data_version;
3569 slice = StringPiece(result);
3570 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3571 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3572 return false;
3575 if (object_store_data_version != index_data_version) {
3576 transaction_->transaction()->Remove(iterator_->Key());
3577 return false;
3580 return true;
3583 class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
3584 public:
3585 IndexCursorImpl(
3586 scoped_refptr<IndexedDBBackingStore> backing_store,
3587 IndexedDBBackingStore::Transaction* transaction,
3588 int64 database_id,
3589 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3590 : IndexedDBBackingStore::Cursor(backing_store,
3591 transaction,
3592 database_id,
3593 cursor_options) {}
3595 Cursor* Clone() override { return new IndexCursorImpl(this); }
3597 // IndexedDBBackingStore::Cursor
3598 IndexedDBValue* value() override { return &current_value_; }
3599 const IndexedDBKey& primary_key() const override { return *primary_key_; }
3600 const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3601 const override {
3602 NOTREACHED();
3603 return record_identifier_;
3605 bool LoadCurrentRow() override;
3607 protected:
3608 std::string EncodeKey(const IndexedDBKey& key) override {
3609 return IndexDataKey::Encode(cursor_options_.database_id,
3610 cursor_options_.object_store_id,
3611 cursor_options_.index_id,
3612 key);
3614 std::string EncodeKey(const IndexedDBKey& key,
3615 const IndexedDBKey& primary_key) override {
3616 return IndexDataKey::Encode(cursor_options_.database_id,
3617 cursor_options_.object_store_id,
3618 cursor_options_.index_id,
3619 key,
3620 primary_key);
3623 private:
3624 explicit IndexCursorImpl(const IndexCursorImpl* other)
3625 : IndexedDBBackingStore::Cursor(other),
3626 primary_key_(new IndexedDBKey(*other->primary_key_)),
3627 current_value_(other->current_value_),
3628 primary_leveldb_key_(other->primary_leveldb_key_) {}
3630 scoped_ptr<IndexedDBKey> primary_key_;
3631 IndexedDBValue current_value_;
3632 std::string primary_leveldb_key_;
3634 DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl);
3637 bool IndexCursorImpl::LoadCurrentRow() {
3638 StringPiece slice(iterator_->Key());
3639 IndexDataKey index_data_key;
3640 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3641 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3642 return false;
3645 current_key_ = index_data_key.user_key();
3646 DCHECK(current_key_);
3648 slice = StringPiece(iterator_->Value());
3649 int64 index_data_version;
3650 if (!DecodeVarInt(&slice, &index_data_version)) {
3651 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3652 return false;
3654 if (!DecodeIDBKey(&slice, &primary_key_)) {
3655 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3656 return false;
3659 DCHECK_EQ(index_data_key.DatabaseId(), database_id_);
3660 primary_leveldb_key_ =
3661 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3662 index_data_key.ObjectStoreId(),
3663 *primary_key_);
3665 std::string result;
3666 bool found = false;
3667 leveldb::Status s =
3668 transaction_->transaction()->Get(primary_leveldb_key_, &result, &found);
3669 if (!s.ok()) {
3670 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3671 return false;
3673 if (!found) {
3674 transaction_->transaction()->Remove(iterator_->Key());
3675 return false;
3677 if (!result.size()) {
3678 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3679 return false;
3682 int64 object_store_data_version;
3683 slice = StringPiece(result);
3684 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3685 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3686 return false;
3689 if (object_store_data_version != index_data_version) {
3690 transaction_->transaction()->Remove(iterator_->Key());
3691 return false;
3694 current_value_.bits = slice.as_string();
3695 return transaction_->GetBlobInfoForRecord(database_id_,
3696 primary_leveldb_key_,
3697 &current_value_).ok();
3700 bool ObjectStoreCursorOptions(
3701 LevelDBTransaction* transaction,
3702 int64 database_id,
3703 int64 object_store_id,
3704 const IndexedDBKeyRange& range,
3705 blink::WebIDBCursorDirection direction,
3706 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3707 cursor_options->database_id = database_id;
3708 cursor_options->object_store_id = object_store_id;
3710 bool lower_bound = range.lower().IsValid();
3711 bool upper_bound = range.upper().IsValid();
3712 cursor_options->forward =
3713 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3714 direction == blink::WebIDBCursorDirectionNext);
3715 cursor_options->unique =
3716 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3717 direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
3719 if (!lower_bound) {
3720 cursor_options->low_key =
3721 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
3722 cursor_options->low_open = true; // Not included.
3723 } else {
3724 cursor_options->low_key =
3725 ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower());
3726 cursor_options->low_open = range.lower_open();
3729 leveldb::Status s;
3731 if (!upper_bound) {
3732 cursor_options->high_key =
3733 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
3735 if (cursor_options->forward) {
3736 cursor_options->high_open = true; // Not included.
3737 } else {
3738 // We need a key that exists.
3739 // TODO(cmumford): Handle this error (crbug.com/363397)
3740 if (!FindGreatestKeyLessThanOrEqual(transaction,
3741 cursor_options->high_key,
3742 &cursor_options->high_key,
3743 &s))
3744 return false;
3745 cursor_options->high_open = false;
3747 } else {
3748 cursor_options->high_key =
3749 ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper());
3750 cursor_options->high_open = range.upper_open();
3752 if (!cursor_options->forward) {
3753 // For reverse cursors, we need a key that exists.
3754 std::string found_high_key;
3755 // TODO(cmumford): Handle this error (crbug.com/363397)
3756 if (!FindGreatestKeyLessThanOrEqual(
3757 transaction, cursor_options->high_key, &found_high_key, &s))
3758 return false;
3760 // If the target key should not be included, but we end up with a smaller
3761 // key, we should include that.
3762 if (cursor_options->high_open &&
3763 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3764 cursor_options->high_open = false;
3766 cursor_options->high_key = found_high_key;
3770 return true;
3773 bool IndexCursorOptions(
3774 LevelDBTransaction* transaction,
3775 int64 database_id,
3776 int64 object_store_id,
3777 int64 index_id,
3778 const IndexedDBKeyRange& range,
3779 blink::WebIDBCursorDirection direction,
3780 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3781 DCHECK(transaction);
3782 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3783 return false;
3785 cursor_options->database_id = database_id;
3786 cursor_options->object_store_id = object_store_id;
3787 cursor_options->index_id = index_id;
3789 bool lower_bound = range.lower().IsValid();
3790 bool upper_bound = range.upper().IsValid();
3791 cursor_options->forward =
3792 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3793 direction == blink::WebIDBCursorDirectionNext);
3794 cursor_options->unique =
3795 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3796 direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
3798 if (!lower_bound) {
3799 cursor_options->low_key =
3800 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
3801 cursor_options->low_open = false; // Included.
3802 } else {
3803 cursor_options->low_key = IndexDataKey::Encode(
3804 database_id, object_store_id, index_id, range.lower());
3805 cursor_options->low_open = range.lower_open();
3808 leveldb::Status s;
3810 if (!upper_bound) {
3811 cursor_options->high_key =
3812 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
3813 cursor_options->high_open = false; // Included.
3815 if (!cursor_options->forward) { // We need a key that exists.
3816 if (!FindGreatestKeyLessThanOrEqual(transaction,
3817 cursor_options->high_key,
3818 &cursor_options->high_key,
3819 &s))
3820 return false;
3821 cursor_options->high_open = false;
3823 } else {
3824 cursor_options->high_key = IndexDataKey::Encode(
3825 database_id, object_store_id, index_id, range.upper());
3826 cursor_options->high_open = range.upper_open();
3828 std::string found_high_key;
3829 // Seek to the *last* key in the set of non-unique keys
3830 // TODO(cmumford): Handle this error (crbug.com/363397)
3831 if (!FindGreatestKeyLessThanOrEqual(
3832 transaction, cursor_options->high_key, &found_high_key, &s))
3833 return false;
3835 // If the target key should not be included, but we end up with a smaller
3836 // key, we should include that.
3837 if (cursor_options->high_open &&
3838 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3839 cursor_options->high_open = false;
3841 cursor_options->high_key = found_high_key;
3844 return true;
3847 scoped_ptr<IndexedDBBackingStore::Cursor>
3848 IndexedDBBackingStore::OpenObjectStoreCursor(
3849 IndexedDBBackingStore::Transaction* transaction,
3850 int64 database_id,
3851 int64 object_store_id,
3852 const IndexedDBKeyRange& range,
3853 blink::WebIDBCursorDirection direction,
3854 leveldb::Status* s) {
3855 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3856 *s = leveldb::Status::OK();
3857 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3858 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3859 if (!ObjectStoreCursorOptions(leveldb_transaction,
3860 database_id,
3861 object_store_id,
3862 range,
3863 direction,
3864 &cursor_options))
3865 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3866 scoped_ptr<ObjectStoreCursorImpl> cursor(new ObjectStoreCursorImpl(
3867 this, transaction, database_id, cursor_options));
3868 if (!cursor->FirstSeek(s))
3869 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3871 return cursor.Pass();
3874 scoped_ptr<IndexedDBBackingStore::Cursor>
3875 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3876 IndexedDBBackingStore::Transaction* transaction,
3877 int64 database_id,
3878 int64 object_store_id,
3879 const IndexedDBKeyRange& range,
3880 blink::WebIDBCursorDirection direction,
3881 leveldb::Status* s) {
3882 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3883 *s = leveldb::Status::OK();
3884 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3885 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3886 if (!ObjectStoreCursorOptions(leveldb_transaction,
3887 database_id,
3888 object_store_id,
3889 range,
3890 direction,
3891 &cursor_options))
3892 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3893 scoped_ptr<ObjectStoreKeyCursorImpl> cursor(new ObjectStoreKeyCursorImpl(
3894 this, transaction, database_id, cursor_options));
3895 if (!cursor->FirstSeek(s))
3896 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3898 return cursor.Pass();
3901 scoped_ptr<IndexedDBBackingStore::Cursor>
3902 IndexedDBBackingStore::OpenIndexKeyCursor(
3903 IndexedDBBackingStore::Transaction* transaction,
3904 int64 database_id,
3905 int64 object_store_id,
3906 int64 index_id,
3907 const IndexedDBKeyRange& range,
3908 blink::WebIDBCursorDirection direction,
3909 leveldb::Status* s) {
3910 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3911 *s = leveldb::Status::OK();
3912 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3913 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3914 if (!IndexCursorOptions(leveldb_transaction,
3915 database_id,
3916 object_store_id,
3917 index_id,
3918 range,
3919 direction,
3920 &cursor_options))
3921 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3922 scoped_ptr<IndexKeyCursorImpl> cursor(
3923 new IndexKeyCursorImpl(this, transaction, database_id, cursor_options));
3924 if (!cursor->FirstSeek(s))
3925 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3927 return cursor.Pass();
3930 scoped_ptr<IndexedDBBackingStore::Cursor>
3931 IndexedDBBackingStore::OpenIndexCursor(
3932 IndexedDBBackingStore::Transaction* transaction,
3933 int64 database_id,
3934 int64 object_store_id,
3935 int64 index_id,
3936 const IndexedDBKeyRange& range,
3937 blink::WebIDBCursorDirection direction,
3938 leveldb::Status* s) {
3939 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
3940 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3941 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3942 if (!IndexCursorOptions(leveldb_transaction,
3943 database_id,
3944 object_store_id,
3945 index_id,
3946 range,
3947 direction,
3948 &cursor_options))
3949 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3950 scoped_ptr<IndexCursorImpl> cursor(
3951 new IndexCursorImpl(this, transaction, database_id, cursor_options));
3952 if (!cursor->FirstSeek(s))
3953 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3955 return cursor.Pass();
3958 IndexedDBBackingStore::Transaction::Transaction(
3959 IndexedDBBackingStore* backing_store)
3960 : backing_store_(backing_store), database_id_(-1), committing_(false) {
3963 IndexedDBBackingStore::Transaction::~Transaction() {
3964 STLDeleteContainerPairSecondPointers(
3965 blob_change_map_.begin(), blob_change_map_.end());
3966 STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
3967 incognito_blob_map_.end());
3968 DCHECK(!committing_);
3971 void IndexedDBBackingStore::Transaction::Begin() {
3972 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
3973 DCHECK(!transaction_.get());
3974 transaction_ = IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3975 backing_store_->db_.get());
3977 // If incognito, this snapshots blobs just as the above transaction_
3978 // constructor snapshots the leveldb.
3979 for (const auto& iter : backing_store_->incognito_blob_map_)
3980 incognito_blob_map_[iter.first] = iter.second->Clone().release();
3983 static GURL GetURLFromUUID(const string& uuid) {
3984 return GURL("blob:uuid/" + uuid);
3987 leveldb::Status IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
3988 BlobEntryKeyValuePairVec* new_blob_entries,
3989 WriteDescriptorVec* new_files_to_write) {
3990 if (backing_store_->is_incognito())
3991 return leveldb::Status::OK();
3993 DCHECK(new_blob_entries->empty());
3994 DCHECK(new_files_to_write->empty());
3995 DCHECK(blobs_to_write_.empty());
3997 if (blob_change_map_.empty())
3998 return leveldb::Status::OK();
4000 // Create LevelDBTransaction for the name generator seed and add-journal.
4001 scoped_refptr<LevelDBTransaction> pre_transaction =
4002 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4003 backing_store_->db_.get());
4005 for (auto& iter : blob_change_map_) {
4006 std::vector<IndexedDBBlobInfo*> new_blob_keys;
4007 for (auto& entry : iter.second->mutable_blob_info()) {
4008 int64 next_blob_key = -1;
4009 bool result = GetBlobKeyGeneratorCurrentNumber(
4010 pre_transaction.get(), database_id_, &next_blob_key);
4011 if (!result || next_blob_key < 0)
4012 return InternalInconsistencyStatus();
4013 blobs_to_write_.push_back(std::make_pair(database_id_, next_blob_key));
4014 if (entry.is_file() && !entry.file_path().empty()) {
4015 new_files_to_write->push_back(
4016 WriteDescriptor(entry.file_path(), next_blob_key, entry.size(),
4017 entry.last_modified()));
4018 } else {
4019 new_files_to_write->push_back(
4020 WriteDescriptor(GetURLFromUUID(entry.uuid()), next_blob_key,
4021 entry.size(), entry.last_modified()));
4023 entry.set_key(next_blob_key);
4024 new_blob_keys.push_back(&entry);
4025 result = UpdateBlobKeyGeneratorCurrentNumber(
4026 pre_transaction.get(), database_id_, next_blob_key + 1);
4027 if (!result)
4028 return InternalInconsistencyStatus();
4030 BlobEntryKey blob_entry_key;
4031 StringPiece key_piece(iter.second->key());
4032 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
4033 NOTREACHED();
4034 return InternalInconsistencyStatus();
4036 new_blob_entries->push_back(
4037 std::make_pair(blob_entry_key, EncodeBlobData(new_blob_keys)));
4040 AppendBlobsToPrimaryBlobJournal(pre_transaction.get(), blobs_to_write_);
4041 leveldb::Status s = pre_transaction->Commit();
4042 if (!s.ok())
4043 return InternalInconsistencyStatus();
4045 return leveldb::Status::OK();
4048 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
4049 if (backing_store_->is_incognito())
4050 return true;
4052 // Look up all old files to remove as part of the transaction, store their
4053 // names in blobs_to_remove_, and remove their old blob data entries.
4054 for (const auto& iter : blob_change_map_) {
4055 BlobEntryKey blob_entry_key;
4056 StringPiece key_piece(iter.second->key());
4057 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
4058 NOTREACHED();
4059 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4060 transaction_ = NULL;
4061 return false;
4063 if (database_id_ < 0)
4064 database_id_ = blob_entry_key.database_id();
4065 else
4066 DCHECK_EQ(database_id_, blob_entry_key.database_id());
4067 std::string blob_entry_key_bytes = blob_entry_key.Encode();
4068 bool found;
4069 std::string blob_entry_value_bytes;
4070 leveldb::Status s = transaction_->Get(
4071 blob_entry_key_bytes, &blob_entry_value_bytes, &found);
4072 if (s.ok() && found) {
4073 std::vector<IndexedDBBlobInfo> blob_info;
4074 if (!DecodeBlobData(blob_entry_value_bytes, &blob_info)) {
4075 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4076 transaction_ = NULL;
4077 return false;
4079 for (const auto& blob : blob_info) {
4080 blobs_to_remove_.push_back(std::make_pair(database_id_, blob.key()));
4081 transaction_->Remove(blob_entry_key_bytes);
4085 return true;
4088 void IndexedDBBackingStore::Transaction::PartitionBlobsToRemove(
4089 BlobJournalType* dead_blobs,
4090 BlobJournalType* live_blobs) const {
4091 IndexedDBActiveBlobRegistry* registry =
4092 backing_store_->active_blob_registry();
4093 for (const auto& iter : blobs_to_remove_) {
4094 if (registry->MarkDeletedCheckIfUsed(iter.first, iter.second))
4095 live_blobs->push_back(iter);
4096 else
4097 dead_blobs->push_back(iter);
4101 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne(
4102 scoped_refptr<BlobWriteCallback> callback) {
4103 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
4104 DCHECK(transaction_.get());
4105 DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
4107 leveldb::Status s;
4109 BlobEntryKeyValuePairVec new_blob_entries;
4110 WriteDescriptorVec new_files_to_write;
4111 s = HandleBlobPreTransaction(&new_blob_entries, &new_files_to_write);
4112 if (!s.ok()) {
4113 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4114 transaction_ = NULL;
4115 return s;
4118 DCHECK(!new_files_to_write.size() ||
4119 KeyPrefix::IsValidDatabaseId(database_id_));
4120 if (!CollectBlobFilesToRemove()) {
4121 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4122 transaction_ = NULL;
4123 return InternalInconsistencyStatus();
4126 committing_ = true;
4127 ++backing_store_->committing_transaction_count_;
4129 if (!new_files_to_write.empty()) {
4130 // This kicks off the writes of the new blobs, if any.
4131 // This call will zero out new_blob_entries and new_files_to_write.
4132 WriteNewBlobs(&new_blob_entries, &new_files_to_write, callback);
4133 } else {
4134 callback->Run(true);
4137 return leveldb::Status::OK();
4140 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
4141 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
4142 leveldb::Status s;
4144 DCHECK(committing_);
4145 committing_ = false;
4146 DCHECK_GT(backing_store_->committing_transaction_count_, 0UL);
4147 --backing_store_->committing_transaction_count_;
4149 BlobJournalType primary_journal, live_journal, saved_primary_journal,
4150 dead_blobs;
4151 if (!blob_change_map_.empty()) {
4152 // Read the persisted states of the primary/live blob journals,
4153 // so that they can be updated correctly by the transaction.
4154 scoped_refptr<LevelDBTransaction> journal_transaction =
4155 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4156 backing_store_->db_.get());
4157 s = GetPrimaryBlobJournal(journal_transaction.get(), &primary_journal);
4158 if (!s.ok())
4159 return s;
4160 s = GetLiveBlobJournal(journal_transaction.get(), &live_journal);
4161 if (!s.ok())
4162 return s;
4164 // Remove newly added blobs from the journal - they will be accounted
4165 // for in blob entry tables in the transaction.
4166 std::sort(primary_journal.begin(), primary_journal.end());
4167 std::sort(blobs_to_write_.begin(), blobs_to_write_.end());
4168 BlobJournalType new_journal = base::STLSetDifference<BlobJournalType>(
4169 primary_journal, blobs_to_write_);
4170 primary_journal.swap(new_journal);
4172 // Append newly deleted blobs to appropriate primary/live journals.
4173 saved_primary_journal = primary_journal;
4174 BlobJournalType live_blobs;
4175 if (!blobs_to_remove_.empty()) {
4176 DCHECK(!backing_store_->is_incognito());
4177 PartitionBlobsToRemove(&dead_blobs, &live_blobs);
4179 primary_journal.insert(primary_journal.end(), dead_blobs.begin(),
4180 dead_blobs.end());
4181 live_journal.insert(live_journal.end(), live_blobs.begin(),
4182 live_blobs.end());
4183 UpdatePrimaryBlobJournal(transaction_.get(), primary_journal);
4184 UpdateLiveBlobJournal(transaction_.get(), live_journal);
4187 // Actually commit. If this succeeds, the journals will appropriately
4188 // reflect pending blob work - dead files that should be deleted
4189 // immediately, and live files to monitor.
4190 s = transaction_->Commit();
4191 transaction_ = NULL;
4193 if (!s.ok()) {
4194 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
4195 return s;
4198 if (backing_store_->is_incognito()) {
4199 if (!blob_change_map_.empty()) {
4200 BlobChangeMap& target_map = backing_store_->incognito_blob_map_;
4201 for (auto& iter : blob_change_map_) {
4202 BlobChangeMap::iterator target_record = target_map.find(iter.first);
4203 if (target_record != target_map.end()) {
4204 delete target_record->second;
4205 target_map.erase(target_record);
4207 if (iter.second) {
4208 target_map[iter.first] = iter.second;
4209 iter.second = NULL;
4213 return leveldb::Status::OK();
4216 // Actually delete dead blob files, then remove those entries
4217 // from the persisted primary journal.
4218 if (dead_blobs.empty())
4219 return leveldb::Status::OK();
4221 DCHECK(!blob_change_map_.empty());
4223 s = backing_store_->CleanUpBlobJournalEntries(dead_blobs);
4224 if (!s.ok()) {
4225 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4226 return s;
4229 scoped_refptr<LevelDBTransaction> update_journal_transaction =
4230 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4231 backing_store_->db_.get());
4232 UpdatePrimaryBlobJournal(update_journal_transaction.get(),
4233 saved_primary_journal);
4234 s = update_journal_transaction->Commit();
4235 return s;
4239 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
4240 : public IndexedDBBackingStore::BlobWriteCallback {
4241 public:
4242 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction,
4243 scoped_refptr<BlobWriteCallback> callback)
4244 : transaction_(transaction), callback_(callback) {}
4245 void Run(bool succeeded) override {
4246 callback_->Run(succeeded);
4247 if (succeeded) // Else it's already been deleted during rollback.
4248 transaction_->chained_blob_writer_ = NULL;
4251 private:
4252 ~BlobWriteCallbackWrapper() override {}
4253 friend class base::RefCounted<IndexedDBBackingStore::BlobWriteCallback>;
4255 IndexedDBBackingStore::Transaction* transaction_;
4256 scoped_refptr<BlobWriteCallback> callback_;
4258 DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper);
4261 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
4262 BlobEntryKeyValuePairVec* new_blob_entries,
4263 WriteDescriptorVec* new_files_to_write,
4264 scoped_refptr<BlobWriteCallback> callback) {
4265 DCHECK_GT(new_files_to_write->size(), 0UL);
4266 DCHECK_GT(database_id_, 0);
4267 for (auto& blob_entry_iter : *new_blob_entries) {
4268 // Add the new blob-table entry for each blob to the main transaction, or
4269 // remove any entry that may exist if there's no new one.
4270 if (blob_entry_iter.second.empty())
4271 transaction_->Remove(blob_entry_iter.first.Encode());
4272 else
4273 transaction_->Put(blob_entry_iter.first.Encode(),
4274 &blob_entry_iter.second);
4276 // Creating the writer will start it going asynchronously.
4277 chained_blob_writer_ =
4278 new ChainedBlobWriterImpl(database_id_,
4279 backing_store_,
4280 new_files_to_write,
4281 new BlobWriteCallbackWrapper(this, callback));
4284 void IndexedDBBackingStore::Transaction::Rollback() {
4285 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
4286 if (committing_) {
4287 committing_ = false;
4288 DCHECK_GT(backing_store_->committing_transaction_count_, 0UL);
4289 --backing_store_->committing_transaction_count_;
4292 if (chained_blob_writer_.get()) {
4293 chained_blob_writer_->Abort();
4294 chained_blob_writer_ = NULL;
4296 if (transaction_.get() == NULL)
4297 return;
4298 transaction_->Rollback();
4299 transaction_ = NULL;
4302 IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
4303 const std::string& key,
4304 int64 object_store_id)
4305 : key_(key), object_store_id_(object_store_id) {
4308 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
4311 void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
4312 std::vector<IndexedDBBlobInfo>* blob_info) {
4313 blob_info_.clear();
4314 if (blob_info)
4315 blob_info_.swap(*blob_info);
4318 void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
4319 ScopedVector<storage::BlobDataHandle>* handles) {
4320 handles_.clear();
4321 if (handles)
4322 handles_.swap(*handles);
4325 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord>
4326 IndexedDBBackingStore::BlobChangeRecord::Clone() const {
4327 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord> record(
4328 new BlobChangeRecord(key_, object_store_id_));
4329 record->blob_info_ = blob_info_;
4331 for (const auto* handle : handles_)
4332 record->handles_.push_back(new storage::BlobDataHandle(*handle));
4333 return record.Pass();
4336 leveldb::Status IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
4337 int64 database_id,
4338 int64 object_store_id,
4339 const std::string& object_store_data_key,
4340 std::vector<IndexedDBBlobInfo>* blob_info,
4341 ScopedVector<storage::BlobDataHandle>* handles) {
4342 if (!blob_info || blob_info->empty()) {
4343 blob_change_map_.erase(object_store_data_key);
4344 incognito_blob_map_.erase(object_store_data_key);
4346 BlobEntryKey blob_entry_key;
4347 StringPiece leveldb_key_piece(object_store_data_key);
4348 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
4349 &blob_entry_key)) {
4350 NOTREACHED();
4351 return InternalInconsistencyStatus();
4353 std::string value;
4354 bool found = false;
4355 leveldb::Status s =
4356 transaction()->Get(blob_entry_key.Encode(), &value, &found);
4357 if (!s.ok())
4358 return s;
4359 if (!found)
4360 return leveldb::Status::OK();
4362 PutBlobInfo(
4363 database_id, object_store_id, object_store_data_key, blob_info, handles);
4364 return leveldb::Status::OK();
4367 // This is storing an info, even if empty, even if the previous key had no blob
4368 // info that we know of. It duplicates a bunch of information stored in the
4369 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
4370 // changes to exists or index keys here.
4371 void IndexedDBBackingStore::Transaction::PutBlobInfo(
4372 int64 database_id,
4373 int64 object_store_id,
4374 const std::string& object_store_data_key,
4375 std::vector<IndexedDBBlobInfo>* blob_info,
4376 ScopedVector<storage::BlobDataHandle>* handles) {
4377 DCHECK_GT(object_store_data_key.size(), 0UL);
4378 if (database_id_ < 0)
4379 database_id_ = database_id;
4380 DCHECK_EQ(database_id_, database_id);
4382 BlobChangeMap::iterator it = blob_change_map_.find(object_store_data_key);
4383 BlobChangeRecord* record = NULL;
4384 if (it == blob_change_map_.end()) {
4385 record = new BlobChangeRecord(object_store_data_key, object_store_id);
4386 blob_change_map_[object_store_data_key] = record;
4387 } else {
4388 record = it->second;
4390 DCHECK_EQ(record->object_store_id(), object_store_id);
4391 record->SetBlobInfo(blob_info);
4392 record->SetHandles(handles);
4393 DCHECK(!handles || !handles->size());
4396 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4397 const GURL& url,
4398 int64_t key,
4399 int64_t size,
4400 base::Time last_modified)
4401 : is_file_(false),
4402 url_(url),
4403 key_(key),
4404 size_(size),
4405 last_modified_(last_modified) {
4408 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4409 const FilePath& file_path,
4410 int64_t key,
4411 int64_t size,
4412 base::Time last_modified)
4413 : is_file_(true),
4414 file_path_(file_path),
4415 key_(key),
4416 size_(size),
4417 last_modified_(last_modified) {
4420 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4421 const WriteDescriptor& other) = default;
4422 IndexedDBBackingStore::Transaction::WriteDescriptor::~WriteDescriptor() =
4423 default;
4424 IndexedDBBackingStore::Transaction::WriteDescriptor&
4425 IndexedDBBackingStore::Transaction::WriteDescriptor::
4426 operator=(const WriteDescriptor& other) = default;
4428 } // namespace content