Refactors gesture conversion functions to ui/events/blink
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_backing_store.cc
blob0db7b6141b3d341fc3a0ad0766afc9f011721146
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 aborted_(false) {
2247 blobs_.swap(*blobs);
2248 iter_ = blobs_.begin();
2249 backing_store->task_runner()->PostTask(
2250 FROM_HERE, base::Bind(&ChainedBlobWriterImpl::WriteNextFile, this));
2253 void set_delegate(scoped_ptr<FileWriterDelegate> delegate) override {
2254 delegate_.reset(delegate.release());
2257 void ReportWriteCompletion(bool succeeded, int64 bytes_written) override {
2258 DCHECK(waiting_for_callback_);
2259 DCHECK(!succeeded || bytes_written >= 0);
2260 waiting_for_callback_ = false;
2261 if (delegate_.get()) // Only present for Blob, not File.
2262 content::BrowserThread::DeleteSoon(
2263 content::BrowserThread::IO, FROM_HERE, delegate_.release());
2264 if (aborted_) {
2265 self_ref_ = NULL;
2266 return;
2268 if (iter_->size() != -1 && iter_->size() != bytes_written)
2269 succeeded = false;
2270 if (succeeded) {
2271 ++iter_;
2272 WriteNextFile();
2273 } else {
2274 callback_->Run(false);
2278 void Abort() override {
2279 if (!waiting_for_callback_)
2280 return;
2281 self_ref_ = this;
2282 aborted_ = true;
2285 private:
2286 ~ChainedBlobWriterImpl() override {}
2288 void WriteNextFile() {
2289 DCHECK(!waiting_for_callback_);
2290 DCHECK(!aborted_);
2291 if (iter_ == blobs_.end()) {
2292 DCHECK(!self_ref_.get());
2293 callback_->Run(true);
2294 return;
2295 } else {
2296 if (!backing_store_->WriteBlobFile(database_id_, *iter_, this)) {
2297 callback_->Run(false);
2298 return;
2300 waiting_for_callback_ = true;
2304 bool waiting_for_callback_;
2305 scoped_refptr<ChainedBlobWriterImpl> self_ref_;
2306 WriteDescriptorVec blobs_;
2307 WriteDescriptorVec::const_iterator iter_;
2308 int64 database_id_;
2309 IndexedDBBackingStore* backing_store_;
2310 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback_;
2311 scoped_ptr<FileWriterDelegate> delegate_;
2312 bool aborted_;
2314 DISALLOW_COPY_AND_ASSIGN(ChainedBlobWriterImpl);
2317 class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
2318 public base::RefCountedThreadSafe<LocalWriteClosure> {
2319 public:
2320 LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter*
2321 chained_blob_writer,
2322 base::SequencedTaskRunner* task_runner)
2323 : chained_blob_writer_(chained_blob_writer),
2324 task_runner_(task_runner),
2325 bytes_written_(0) {}
2327 void Run(base::File::Error rv,
2328 int64 bytes,
2329 FileWriterDelegate::WriteProgressStatus write_status) {
2330 DCHECK_GE(bytes, 0);
2331 bytes_written_ += bytes;
2332 if (write_status == FileWriterDelegate::SUCCESS_IO_PENDING)
2333 return; // We don't care about progress events.
2334 if (rv == base::File::FILE_OK) {
2335 DCHECK_EQ(write_status, FileWriterDelegate::SUCCESS_COMPLETED);
2336 } else {
2337 DCHECK(write_status == FileWriterDelegate::ERROR_WRITE_STARTED ||
2338 write_status == FileWriterDelegate::ERROR_WRITE_NOT_STARTED);
2341 bool success = write_status == FileWriterDelegate::SUCCESS_COMPLETED;
2342 if (success && !bytes_written_) {
2343 // LocalFileStreamWriter only creates a file if data is actually written.
2344 // If none was then create one now.
2345 task_runner_->PostTask(
2346 FROM_HERE, base::Bind(&LocalWriteClosure::CreateEmptyFile, this));
2347 } else if (success && !last_modified_.is_null()) {
2348 task_runner_->PostTask(
2349 FROM_HERE, base::Bind(&LocalWriteClosure::UpdateTimeStamp, this));
2350 } else {
2351 task_runner_->PostTask(
2352 FROM_HERE,
2353 base::Bind(&IndexedDBBackingStore::Transaction::ChainedBlobWriter::
2354 ReportWriteCompletion,
2355 chained_blob_writer_,
2356 success,
2357 bytes_written_));
2361 void WriteBlobToFileOnIOThread(const FilePath& file_path,
2362 const GURL& blob_url,
2363 const base::Time& last_modified,
2364 net::URLRequestContext* request_context) {
2365 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
2366 scoped_ptr<storage::FileStreamWriter> writer(
2367 storage::FileStreamWriter::CreateForLocalFile(
2368 task_runner_.get(),
2369 file_path,
2371 storage::FileStreamWriter::CREATE_NEW_FILE));
2372 scoped_ptr<FileWriterDelegate> delegate(new FileWriterDelegate(
2373 writer.Pass(), storage::FlushPolicy::FLUSH_ON_COMPLETION));
2375 DCHECK(blob_url.is_valid());
2376 scoped_ptr<net::URLRequest> blob_request(request_context->CreateRequest(
2377 blob_url, net::DEFAULT_PRIORITY, delegate.get()));
2379 this->file_path_ = file_path;
2380 this->last_modified_ = last_modified;
2382 delegate->Start(blob_request.Pass(),
2383 base::Bind(&LocalWriteClosure::Run, this));
2384 chained_blob_writer_->set_delegate(delegate.Pass());
2387 private:
2388 virtual ~LocalWriteClosure() {
2389 // Make sure the last reference to a ChainedBlobWriter is released (and
2390 // deleted) on the IDB thread since it owns a transaction which has thread
2391 // affinity.
2392 IndexedDBBackingStore::Transaction::ChainedBlobWriter* raw_tmp =
2393 chained_blob_writer_.get();
2394 raw_tmp->AddRef();
2395 chained_blob_writer_ = NULL;
2396 task_runner_->ReleaseSoon(FROM_HERE, raw_tmp);
2398 friend class base::RefCountedThreadSafe<LocalWriteClosure>;
2400 // If necessary, update the timestamps on the file as a final
2401 // step before reporting success.
2402 void UpdateTimeStamp() {
2403 DCHECK(task_runner_->RunsTasksOnCurrentThread());
2404 if (!base::TouchFile(file_path_, last_modified_, last_modified_)) {
2405 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2407 chained_blob_writer_->ReportWriteCompletion(true, bytes_written_);
2410 // Create an empty file.
2411 void CreateEmptyFile() {
2412 DCHECK(task_runner_->RunsTasksOnCurrentThread());
2413 base::File file(file_path_,
2414 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
2415 bool success = file.created();
2416 if (success && !last_modified_.is_null() &&
2417 !file.SetTimes(last_modified_, last_modified_)) {
2418 // TODO(cmumford): Complain quietly; timestamp's probably not vital.
2420 file.Close();
2421 chained_blob_writer_->ReportWriteCompletion(success, bytes_written_);
2424 scoped_refptr<IndexedDBBackingStore::Transaction::ChainedBlobWriter>
2425 chained_blob_writer_;
2426 scoped_refptr<base::SequencedTaskRunner> task_runner_;
2427 int64 bytes_written_;
2429 base::FilePath file_path_;
2430 base::Time last_modified_;
2432 DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure);
2435 bool IndexedDBBackingStore::WriteBlobFile(
2436 int64 database_id,
2437 const Transaction::WriteDescriptor& descriptor,
2438 Transaction::ChainedBlobWriter* chained_blob_writer) {
2440 if (!MakeIDBBlobDirectory(blob_path_, database_id, descriptor.key()))
2441 return false;
2443 FilePath path = GetBlobFileName(database_id, descriptor.key());
2445 if (descriptor.is_file() && !descriptor.file_path().empty()) {
2446 if (!base::CopyFile(descriptor.file_path(), path))
2447 return false;
2449 base::File::Info info;
2450 if (base::GetFileInfo(descriptor.file_path(), &info)) {
2451 if (descriptor.size() != -1) {
2452 if (descriptor.size() != info.size)
2453 return false;
2454 // The round-trip can be lossy; round to nearest millisecond.
2455 int64 delta = (descriptor.last_modified() -
2456 info.last_modified).InMilliseconds();
2457 if (std::abs(delta) > 1)
2458 return false;
2460 if (!base::TouchFile(path, info.last_accessed, info.last_modified)) {
2461 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2463 } else {
2464 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2467 task_runner_->PostTask(
2468 FROM_HERE,
2469 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion,
2470 chained_blob_writer,
2471 true,
2472 info.size));
2473 } else {
2474 DCHECK(descriptor.url().is_valid());
2475 scoped_refptr<LocalWriteClosure> write_closure(
2476 new LocalWriteClosure(chained_blob_writer, task_runner_.get()));
2477 content::BrowserThread::PostTask(
2478 content::BrowserThread::IO,
2479 FROM_HERE,
2480 base::Bind(&LocalWriteClosure::WriteBlobToFileOnIOThread,
2481 write_closure.get(),
2482 path,
2483 descriptor.url(),
2484 descriptor.last_modified(),
2485 request_context_));
2487 return true;
2490 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
2491 int64 blob_key) {
2492 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2493 bool all_blobs = blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2494 DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2495 scoped_refptr<LevelDBTransaction> transaction =
2496 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2498 BlobJournalType live_blob_journal, primary_journal;
2499 if (!GetLiveBlobJournal(transaction.get(), &live_blob_journal).ok())
2500 return;
2501 DCHECK(!live_blob_journal.empty());
2502 if (!GetPrimaryBlobJournal(transaction.get(), &primary_journal).ok())
2503 return;
2505 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2506 // remove all entries with database_id from the live_blob journal and add only
2507 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2508 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2509 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2510 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2511 // matching (database_id, blob_key) tuple, we should move it to the primary
2512 // journal.
2513 BlobJournalType new_live_blob_journal;
2514 for (BlobJournalType::iterator journal_iter = live_blob_journal.begin();
2515 journal_iter != live_blob_journal.end();
2516 ++journal_iter) {
2517 int64 current_database_id = journal_iter->first;
2518 int64 current_blob_key = journal_iter->second;
2519 bool current_all_blobs =
2520 current_blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2521 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id) ||
2522 current_all_blobs);
2523 if (current_database_id == database_id &&
2524 (all_blobs || current_all_blobs || blob_key == current_blob_key)) {
2525 if (!all_blobs) {
2526 primary_journal.push_back(
2527 std::make_pair(database_id, current_blob_key));
2528 if (current_all_blobs)
2529 new_live_blob_journal.push_back(*journal_iter);
2530 new_live_blob_journal.insert(new_live_blob_journal.end(),
2531 ++journal_iter,
2532 live_blob_journal.end()); // All the rest.
2533 break;
2535 } else {
2536 new_live_blob_journal.push_back(*journal_iter);
2539 if (all_blobs) {
2540 primary_journal.push_back(
2541 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
2543 UpdatePrimaryBlobJournal(transaction.get(), primary_journal);
2544 UpdateLiveBlobJournal(transaction.get(), new_live_blob_journal);
2545 transaction->Commit();
2546 // We could just do the deletions/cleaning here, but if there are a lot of
2547 // blobs about to be garbage collected, it'd be better to wait and do them all
2548 // at once.
2549 StartJournalCleaningTimer();
2552 // The this reference is a raw pointer that's declared Unretained inside the
2553 // timer code, so this won't confuse IndexedDBFactory's check for
2554 // HasLastBackingStoreReference. It's safe because if the backing store is
2555 // deleted, the timer will automatically be canceled on destruction.
2556 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2557 journal_cleaning_timer_.Start(
2558 FROM_HERE,
2559 base::TimeDelta::FromSeconds(5),
2560 this,
2561 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn);
2564 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
2565 FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id,
2566 int64 key) const {
2567 return GetBlobFileNameForKey(blob_path_, database_id, key);
2570 static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
2571 const std::string& stop_key,
2572 int64 index_id,
2573 unsigned char meta_data_type) {
2574 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
2575 return false;
2577 StringPiece slice(it->Key());
2578 IndexMetaDataKey meta_data_key;
2579 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2580 DCHECK(ok);
2581 if (meta_data_key.IndexId() != index_id)
2582 return false;
2583 if (meta_data_key.meta_data_type() != meta_data_type)
2584 return false;
2585 return true;
2588 // TODO(jsbell): This should do some error handling rather than plowing ahead
2589 // when bad data is encountered.
2590 leveldb::Status IndexedDBBackingStore::GetIndexes(
2591 int64 database_id,
2592 int64 object_store_id,
2593 IndexedDBObjectStoreMetadata::IndexMap* indexes) {
2594 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2595 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2596 return InvalidDBKeyStatus();
2597 const std::string start_key =
2598 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0);
2599 const std::string stop_key =
2600 IndexMetaDataKey::Encode(database_id, object_store_id + 1, 0, 0);
2602 DCHECK(indexes->empty());
2604 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
2605 leveldb::Status s = it->Seek(start_key);
2606 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
2607 StringPiece slice(it->Key());
2608 IndexMetaDataKey meta_data_key;
2609 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2610 DCHECK(ok);
2611 if (meta_data_key.meta_data_type() != IndexMetaDataKey::NAME) {
2612 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2613 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2614 // the load.
2615 s = it->Next();
2616 if (!s.ok())
2617 break;
2618 continue;
2621 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2622 // simplify.
2623 int64 index_id = meta_data_key.IndexId();
2624 base::string16 index_name;
2626 StringPiece slice(it->Value());
2627 if (!DecodeString(&slice, &index_name) || !slice.empty())
2628 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2631 s = it->Next(); // unique flag
2632 if (!s.ok())
2633 break;
2634 if (!CheckIndexAndMetaDataKey(
2635 it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
2636 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2637 break;
2639 bool index_unique;
2641 StringPiece slice(it->Value());
2642 if (!DecodeBool(&slice, &index_unique) || !slice.empty())
2643 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2646 s = it->Next(); // key_path
2647 if (!s.ok())
2648 break;
2649 if (!CheckIndexAndMetaDataKey(
2650 it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
2651 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2652 break;
2654 IndexedDBKeyPath key_path;
2656 StringPiece slice(it->Value());
2657 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
2658 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2661 s = it->Next(); // [optional] multi_entry flag
2662 if (!s.ok())
2663 break;
2664 bool index_multi_entry = false;
2665 if (CheckIndexAndMetaDataKey(
2666 it.get(), stop_key, index_id, IndexMetaDataKey::MULTI_ENTRY)) {
2667 StringPiece slice(it->Value());
2668 if (!DecodeBool(&slice, &index_multi_entry) || !slice.empty())
2669 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2671 s = it->Next();
2672 if (!s.ok())
2673 break;
2676 (*indexes)[index_id] = IndexedDBIndexMetadata(
2677 index_name, index_id, key_path, index_unique, index_multi_entry);
2680 if (!s.ok())
2681 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES);
2683 return s;
2686 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) const {
2687 FilePath path = GetBlobFileName(database_id, key);
2688 return base::DeleteFile(path, false);
2691 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) const {
2692 FilePath path = GetBlobDirectoryName(blob_path_, database_id);
2693 return base::DeleteFile(path, true);
2696 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournalEntries(
2697 const BlobJournalType& journal) const {
2698 if (journal.empty())
2699 return leveldb::Status::OK();
2700 for (const auto& entry : journal) {
2701 int64 database_id = entry.first;
2702 int64 blob_key = entry.second;
2703 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2704 if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
2705 if (!RemoveBlobDirectory(database_id))
2706 return IOErrorStatus();
2707 } else {
2708 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2709 if (!RemoveBlobFile(database_id, blob_key))
2710 return IOErrorStatus();
2713 return leveldb::Status::OK();
2716 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal(
2717 const std::string& level_db_key) const {
2718 DCHECK(!committing_transaction_count_);
2719 leveldb::Status s;
2720 scoped_refptr<LevelDBTransaction> journal_transaction =
2721 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2722 BlobJournalType journal;
2724 s = GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
2725 if (!s.ok())
2726 return s;
2727 if (journal.empty())
2728 return leveldb::Status::OK();
2729 s = CleanUpBlobJournalEntries(journal);
2730 if (!s.ok())
2731 return s;
2732 ClearBlobJournal(journal_transaction.get(), level_db_key);
2733 return journal_transaction->Commit();
2736 leveldb::Status IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
2737 int64 database_id,
2738 const std::string& object_store_data_key,
2739 IndexedDBValue* value) {
2740 BlobChangeRecord* change_record = NULL;
2741 BlobChangeMap::const_iterator blob_iter =
2742 blob_change_map_.find(object_store_data_key);
2743 if (blob_iter != blob_change_map_.end()) {
2744 change_record = blob_iter->second;
2745 } else {
2746 blob_iter = incognito_blob_map_.find(object_store_data_key);
2747 if (blob_iter != incognito_blob_map_.end())
2748 change_record = blob_iter->second;
2750 if (change_record) {
2751 // Either we haven't written the blob to disk yet or we're in incognito
2752 // mode, so we have to send back the one they sent us. This change record
2753 // includes the original UUID.
2754 value->blob_info = change_record->blob_info();
2755 return leveldb::Status::OK();
2758 BlobEntryKey blob_entry_key;
2759 StringPiece leveldb_key_piece(object_store_data_key);
2760 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
2761 &blob_entry_key)) {
2762 NOTREACHED();
2763 return InternalInconsistencyStatus();
2765 std::string encoded_key = blob_entry_key.Encode();
2766 bool found;
2767 std::string encoded_value;
2768 leveldb::Status s = transaction()->Get(encoded_key, &encoded_value, &found);
2769 if (!s.ok())
2770 return s;
2771 if (found) {
2772 if (!DecodeBlobData(encoded_value, &value->blob_info)) {
2773 INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD);
2774 return InternalInconsistencyStatus();
2776 for (auto& entry : value->blob_info) {
2777 entry.set_file_path(
2778 backing_store_->GetBlobFileName(database_id, entry.key()));
2779 entry.set_mark_used_callback(
2780 backing_store_->active_blob_registry()->GetAddBlobRefCallback(
2781 database_id, entry.key()));
2782 entry.set_release_callback(
2783 backing_store_->active_blob_registry()->GetFinalReleaseCallback(
2784 database_id, entry.key()));
2785 if (entry.is_file() && !entry.file_path().empty()) {
2786 base::File::Info info;
2787 if (base::GetFileInfo(entry.file_path(), &info)) {
2788 // This should always work, but it isn't fatal if it doesn't; it just
2789 // means a potential slow synchronous call from the renderer later.
2790 entry.set_last_modified(info.last_modified);
2791 entry.set_size(info.size);
2796 return leveldb::Status::OK();
2799 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2800 // While a transaction is busy it is not safe to clean the journal.
2801 if (committing_transaction_count_ > 0)
2802 StartJournalCleaningTimer();
2803 else
2804 CleanUpBlobJournal(BlobJournalKey::Encode());
2807 WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId(
2808 LevelDBTransaction* transaction,
2809 int64 database_id,
2810 int64 object_store_id,
2811 int64 index_id) {
2812 int64 max_index_id = -1;
2813 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
2814 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
2815 bool found = false;
2816 leveldb::Status s =
2817 GetInt(transaction, max_index_id_key, &max_index_id, &found);
2818 if (!s.ok()) {
2819 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2820 return s;
2822 if (!found)
2823 max_index_id = kMinimumIndexId;
2825 if (index_id <= max_index_id) {
2826 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2827 return InternalInconsistencyStatus();
2830 PutInt(transaction, max_index_id_key, index_id);
2831 return s;
2834 leveldb::Status IndexedDBBackingStore::CreateIndex(
2835 IndexedDBBackingStore::Transaction* transaction,
2836 int64 database_id,
2837 int64 object_store_id,
2838 int64 index_id,
2839 const base::string16& name,
2840 const IndexedDBKeyPath& key_path,
2841 bool is_unique,
2842 bool is_multi_entry) {
2843 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2844 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2845 return InvalidDBKeyStatus();
2846 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2847 leveldb::Status s = SetMaxIndexId(
2848 leveldb_transaction, database_id, object_store_id, index_id);
2850 if (!s.ok())
2851 return s;
2853 const std::string name_key = IndexMetaDataKey::Encode(
2854 database_id, object_store_id, index_id, IndexMetaDataKey::NAME);
2855 const std::string unique_key = IndexMetaDataKey::Encode(
2856 database_id, object_store_id, index_id, IndexMetaDataKey::UNIQUE);
2857 const std::string key_path_key = IndexMetaDataKey::Encode(
2858 database_id, object_store_id, index_id, IndexMetaDataKey::KEY_PATH);
2859 const std::string multi_entry_key = IndexMetaDataKey::Encode(
2860 database_id, object_store_id, index_id, IndexMetaDataKey::MULTI_ENTRY);
2862 PutString(leveldb_transaction, name_key, name);
2863 PutBool(leveldb_transaction, unique_key, is_unique);
2864 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
2865 PutBool(leveldb_transaction, multi_entry_key, is_multi_entry);
2866 return s;
2869 leveldb::Status IndexedDBBackingStore::DeleteIndex(
2870 IndexedDBBackingStore::Transaction* transaction,
2871 int64 database_id,
2872 int64 object_store_id,
2873 int64 index_id) {
2874 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2875 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2876 return InvalidDBKeyStatus();
2877 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2879 const std::string index_meta_data_start =
2880 IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0);
2881 const std::string index_meta_data_end =
2882 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2883 leveldb::Status s = DeleteRangeBasic(
2884 leveldb_transaction, index_meta_data_start, index_meta_data_end, true);
2886 if (s.ok()) {
2887 const std::string index_data_start =
2888 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
2889 const std::string index_data_end =
2890 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2891 s = DeleteRangeBasic(
2892 leveldb_transaction, index_data_start, index_data_end, true);
2895 if (!s.ok())
2896 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX);
2898 return s;
2901 leveldb::Status IndexedDBBackingStore::PutIndexDataForRecord(
2902 IndexedDBBackingStore::Transaction* transaction,
2903 int64 database_id,
2904 int64 object_store_id,
2905 int64 index_id,
2906 const IndexedDBKey& key,
2907 const RecordIdentifier& record_identifier) {
2908 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2909 DCHECK(key.IsValid());
2910 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2911 return InvalidDBKeyStatus();
2913 std::string encoded_key;
2914 EncodeIDBKey(key, &encoded_key);
2916 const std::string index_data_key =
2917 IndexDataKey::Encode(database_id,
2918 object_store_id,
2919 index_id,
2920 encoded_key,
2921 record_identifier.primary_key(),
2924 std::string data;
2925 EncodeVarInt(record_identifier.version(), &data);
2926 data.append(record_identifier.primary_key());
2928 transaction->transaction()->Put(index_data_key, &data);
2929 return leveldb::Status::OK();
2932 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
2933 const std::string& target,
2934 std::string* found_key,
2935 leveldb::Status* s) {
2936 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
2937 *s = it->Seek(target);
2938 if (!s->ok())
2939 return false;
2941 if (!it->IsValid()) {
2942 *s = it->SeekToLast();
2943 if (!s->ok() || !it->IsValid())
2944 return false;
2947 while (CompareIndexKeys(it->Key(), target) > 0) {
2948 *s = it->Prev();
2949 if (!s->ok() || !it->IsValid())
2950 return false;
2953 do {
2954 *found_key = it->Key().as_string();
2956 // There can be several index keys that compare equal. We want the last one.
2957 *s = it->Next();
2958 } while (s->ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
2960 return true;
2963 static leveldb::Status VersionExists(LevelDBTransaction* transaction,
2964 int64 database_id,
2965 int64 object_store_id,
2966 int64 version,
2967 const std::string& encoded_primary_key,
2968 bool* exists) {
2969 const std::string key =
2970 ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
2971 std::string data;
2973 leveldb::Status s = transaction->Get(key, &data, exists);
2974 if (!s.ok()) {
2975 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS);
2976 return s;
2978 if (!*exists)
2979 return s;
2981 StringPiece slice(data);
2982 int64 decoded;
2983 if (!DecodeInt(&slice, &decoded) || !slice.empty())
2984 return InternalInconsistencyStatus();
2985 *exists = (decoded == version);
2986 return s;
2989 leveldb::Status IndexedDBBackingStore::FindKeyInIndex(
2990 IndexedDBBackingStore::Transaction* transaction,
2991 int64 database_id,
2992 int64 object_store_id,
2993 int64 index_id,
2994 const IndexedDBKey& key,
2995 std::string* found_encoded_primary_key,
2996 bool* found) {
2997 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
2998 DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id));
3000 DCHECK(found_encoded_primary_key->empty());
3001 *found = false;
3003 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3004 const std::string leveldb_key =
3005 IndexDataKey::Encode(database_id, object_store_id, index_id, key);
3006 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
3007 leveldb::Status s = it->Seek(leveldb_key);
3008 if (!s.ok()) {
3009 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
3010 return s;
3013 for (;;) {
3014 if (!it->IsValid())
3015 return leveldb::Status::OK();
3016 if (CompareIndexKeys(it->Key(), leveldb_key) > 0)
3017 return leveldb::Status::OK();
3019 StringPiece slice(it->Value());
3021 int64 version;
3022 if (!DecodeVarInt(&slice, &version)) {
3023 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
3024 return InternalInconsistencyStatus();
3026 *found_encoded_primary_key = slice.as_string();
3028 bool exists = false;
3029 s = VersionExists(leveldb_transaction,
3030 database_id,
3031 object_store_id,
3032 version,
3033 *found_encoded_primary_key,
3034 &exists);
3035 if (!s.ok())
3036 return s;
3037 if (!exists) {
3038 // Delete stale index data entry and continue.
3039 leveldb_transaction->Remove(it->Key());
3040 s = it->Next();
3041 continue;
3043 *found = true;
3044 return s;
3048 leveldb::Status IndexedDBBackingStore::GetPrimaryKeyViaIndex(
3049 IndexedDBBackingStore::Transaction* transaction,
3050 int64 database_id,
3051 int64 object_store_id,
3052 int64 index_id,
3053 const IndexedDBKey& key,
3054 scoped_ptr<IndexedDBKey>* primary_key) {
3055 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
3056 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3057 return InvalidDBKeyStatus();
3059 bool found = false;
3060 std::string found_encoded_primary_key;
3061 leveldb::Status s = FindKeyInIndex(transaction,
3062 database_id,
3063 object_store_id,
3064 index_id,
3065 key,
3066 &found_encoded_primary_key,
3067 &found);
3068 if (!s.ok()) {
3069 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
3070 return s;
3072 if (!found)
3073 return s;
3074 if (!found_encoded_primary_key.size()) {
3075 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
3076 return InvalidDBKeyStatus();
3079 StringPiece slice(found_encoded_primary_key);
3080 if (DecodeIDBKey(&slice, primary_key) && slice.empty())
3081 return s;
3082 else
3083 return InvalidDBKeyStatus();
3086 leveldb::Status IndexedDBBackingStore::KeyExistsInIndex(
3087 IndexedDBBackingStore::Transaction* transaction,
3088 int64 database_id,
3089 int64 object_store_id,
3090 int64 index_id,
3091 const IndexedDBKey& index_key,
3092 scoped_ptr<IndexedDBKey>* found_primary_key,
3093 bool* exists) {
3094 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
3095 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3096 return InvalidDBKeyStatus();
3098 *exists = false;
3099 std::string found_encoded_primary_key;
3100 leveldb::Status s = FindKeyInIndex(transaction,
3101 database_id,
3102 object_store_id,
3103 index_id,
3104 index_key,
3105 &found_encoded_primary_key,
3106 exists);
3107 if (!s.ok()) {
3108 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
3109 return s;
3111 if (!*exists)
3112 return leveldb::Status::OK();
3113 if (found_encoded_primary_key.empty()) {
3114 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
3115 return InvalidDBKeyStatus();
3118 StringPiece slice(found_encoded_primary_key);
3119 if (DecodeIDBKey(&slice, found_primary_key) && slice.empty())
3120 return s;
3121 else
3122 return InvalidDBKeyStatus();
3125 IndexedDBBackingStore::Cursor::Cursor(
3126 const IndexedDBBackingStore::Cursor* other)
3127 : backing_store_(other->backing_store_),
3128 transaction_(other->transaction_),
3129 database_id_(other->database_id_),
3130 cursor_options_(other->cursor_options_),
3131 current_key_(new IndexedDBKey(*other->current_key_)) {
3132 if (other->iterator_) {
3133 iterator_ = transaction_->transaction()->CreateIterator();
3135 if (other->iterator_->IsValid()) {
3136 leveldb::Status s = iterator_->Seek(other->iterator_->Key());
3137 // TODO(cmumford): Handle this error (crbug.com/363397)
3138 DCHECK(iterator_->IsValid());
3143 IndexedDBBackingStore::Cursor::Cursor(
3144 scoped_refptr<IndexedDBBackingStore> backing_store,
3145 IndexedDBBackingStore::Transaction* transaction,
3146 int64 database_id,
3147 const CursorOptions& cursor_options)
3148 : backing_store_(backing_store.get()),
3149 transaction_(transaction),
3150 database_id_(database_id),
3151 cursor_options_(cursor_options) {
3153 IndexedDBBackingStore::Cursor::~Cursor() {}
3155 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) {
3156 iterator_ = transaction_->transaction()->CreateIterator();
3157 if (cursor_options_.forward)
3158 *s = iterator_->Seek(cursor_options_.low_key);
3159 else
3160 *s = iterator_->Seek(cursor_options_.high_key);
3161 if (!s->ok())
3162 return false;
3164 return Continue(0, READY, s);
3167 bool IndexedDBBackingStore::Cursor::Advance(uint32 count, leveldb::Status* s) {
3168 *s = leveldb::Status::OK();
3169 while (count--) {
3170 if (!Continue(s))
3171 return false;
3173 return true;
3176 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
3177 const IndexedDBKey* primary_key,
3178 IteratorState next_state,
3179 leveldb::Status* s) {
3180 DCHECK(!key || key->IsValid());
3181 DCHECK(!primary_key || primary_key->IsValid());
3182 *s = leveldb::Status::OK();
3184 // TODO(alecflett): avoid a copy here?
3185 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3187 // When iterating with PrevNoDuplicate, spec requires that the
3188 // value we yield for each key is the first duplicate in forwards
3189 // order.
3190 IndexedDBKey last_duplicate_key;
3192 bool forward = cursor_options_.forward;
3193 bool first_iteration_forward = forward;
3194 bool flipped = false;
3196 for (;;) {
3197 if (next_state == SEEK) {
3198 // TODO(jsbell): Optimize seeking for reverse cursors as well.
3199 if (first_iteration_forward && key) {
3200 first_iteration_forward = false;
3201 std::string leveldb_key;
3202 if (primary_key) {
3203 leveldb_key = EncodeKey(*key, *primary_key);
3204 } else {
3205 leveldb_key = EncodeKey(*key);
3207 *s = iterator_->Seek(leveldb_key);
3208 } else if (forward) {
3209 *s = iterator_->Next();
3210 } else {
3211 *s = iterator_->Prev();
3213 if (!s->ok())
3214 return false;
3215 } else {
3216 next_state = SEEK; // for subsequent iterations
3219 if (!iterator_->IsValid()) {
3220 if (!forward && last_duplicate_key.IsValid()) {
3221 // We need to walk forward because we hit the end of
3222 // the data.
3223 forward = true;
3224 flipped = true;
3225 continue;
3228 return false;
3231 if (IsPastBounds()) {
3232 if (!forward && last_duplicate_key.IsValid()) {
3233 // We need to walk forward because now we're beyond the
3234 // bounds defined by the cursor.
3235 forward = true;
3236 flipped = true;
3237 continue;
3240 return false;
3243 if (!HaveEnteredRange())
3244 continue;
3246 // The row may not load because there's a stale entry in the
3247 // index. This is not fatal.
3248 if (!LoadCurrentRow())
3249 continue;
3251 if (key) {
3252 if (forward) {
3253 if (primary_key && current_key_->Equals(*key) &&
3254 this->primary_key().IsLessThan(*primary_key))
3255 continue;
3256 if (!flipped && current_key_->IsLessThan(*key))
3257 continue;
3258 } else {
3259 if (primary_key && key->Equals(*current_key_) &&
3260 primary_key->IsLessThan(this->primary_key()))
3261 continue;
3262 if (key->IsLessThan(*current_key_))
3263 continue;
3267 if (cursor_options_.unique) {
3268 if (previous_key.IsValid() && current_key_->Equals(previous_key)) {
3269 // We should never be able to walk forward all the way
3270 // to the previous key.
3271 DCHECK(!last_duplicate_key.IsValid());
3272 continue;
3275 if (!forward) {
3276 if (!last_duplicate_key.IsValid()) {
3277 last_duplicate_key = *current_key_;
3278 continue;
3281 // We need to walk forward because we hit the boundary
3282 // between key ranges.
3283 if (!last_duplicate_key.Equals(*current_key_)) {
3284 forward = true;
3285 flipped = true;
3286 continue;
3289 continue;
3292 break;
3295 DCHECK(!last_duplicate_key.IsValid() ||
3296 (forward && last_duplicate_key.Equals(*current_key_)));
3297 return true;
3300 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3301 if (cursor_options_.forward) {
3302 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3303 if (cursor_options_.low_open) {
3304 return compare > 0;
3306 return compare >= 0;
3308 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3309 if (cursor_options_.high_open) {
3310 return compare < 0;
3312 return compare <= 0;
3315 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
3316 if (cursor_options_.forward) {
3317 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3318 if (cursor_options_.high_open) {
3319 return compare >= 0;
3321 return compare > 0;
3323 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3324 if (cursor_options_.low_open) {
3325 return compare <= 0;
3327 return compare < 0;
3330 const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const {
3331 return *current_key_;
3334 const IndexedDBBackingStore::RecordIdentifier&
3335 IndexedDBBackingStore::Cursor::record_identifier() const {
3336 return record_identifier_;
3339 class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3340 public:
3341 ObjectStoreKeyCursorImpl(
3342 scoped_refptr<IndexedDBBackingStore> backing_store,
3343 IndexedDBBackingStore::Transaction* transaction,
3344 int64 database_id,
3345 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3346 : IndexedDBBackingStore::Cursor(backing_store,
3347 transaction,
3348 database_id,
3349 cursor_options) {}
3351 Cursor* Clone() override { return new ObjectStoreKeyCursorImpl(this); }
3353 // IndexedDBBackingStore::Cursor
3354 IndexedDBValue* value() override {
3355 NOTREACHED();
3356 return NULL;
3358 bool LoadCurrentRow() override;
3360 protected:
3361 std::string EncodeKey(const IndexedDBKey& key) override {
3362 return ObjectStoreDataKey::Encode(
3363 cursor_options_.database_id, cursor_options_.object_store_id, key);
3365 std::string EncodeKey(const IndexedDBKey& key,
3366 const IndexedDBKey& primary_key) override {
3367 NOTREACHED();
3368 return std::string();
3371 private:
3372 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other)
3373 : IndexedDBBackingStore::Cursor(other) {}
3375 DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl);
3378 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
3379 StringPiece slice(iterator_->Key());
3380 ObjectStoreDataKey object_store_data_key;
3381 if (!ObjectStoreDataKey::Decode(&slice, &object_store_data_key)) {
3382 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3383 return false;
3386 current_key_ = object_store_data_key.user_key();
3388 int64 version;
3389 slice = StringPiece(iterator_->Value());
3390 if (!DecodeVarInt(&slice, &version)) {
3391 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3392 return false;
3395 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3396 std::string encoded_key;
3397 EncodeIDBKey(*current_key_, &encoded_key);
3398 record_identifier_.Reset(encoded_key, version);
3400 return true;
3403 class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
3404 public:
3405 ObjectStoreCursorImpl(
3406 scoped_refptr<IndexedDBBackingStore> backing_store,
3407 IndexedDBBackingStore::Transaction* transaction,
3408 int64 database_id,
3409 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3410 : IndexedDBBackingStore::Cursor(backing_store,
3411 transaction,
3412 database_id,
3413 cursor_options) {}
3415 Cursor* Clone() override { return new ObjectStoreCursorImpl(this); }
3417 // IndexedDBBackingStore::Cursor
3418 IndexedDBValue* value() override { return &current_value_; }
3419 bool LoadCurrentRow() override;
3421 protected:
3422 std::string EncodeKey(const IndexedDBKey& key) override {
3423 return ObjectStoreDataKey::Encode(
3424 cursor_options_.database_id, cursor_options_.object_store_id, key);
3426 std::string EncodeKey(const IndexedDBKey& key,
3427 const IndexedDBKey& primary_key) override {
3428 NOTREACHED();
3429 return std::string();
3432 private:
3433 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other)
3434 : IndexedDBBackingStore::Cursor(other),
3435 current_value_(other->current_value_) {}
3437 IndexedDBValue current_value_;
3439 DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl);
3442 bool ObjectStoreCursorImpl::LoadCurrentRow() {
3443 StringPiece key_slice(iterator_->Key());
3444 ObjectStoreDataKey object_store_data_key;
3445 if (!ObjectStoreDataKey::Decode(&key_slice, &object_store_data_key)) {
3446 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3447 return false;
3450 current_key_ = object_store_data_key.user_key();
3452 int64 version;
3453 StringPiece value_slice = StringPiece(iterator_->Value());
3454 if (!DecodeVarInt(&value_slice, &version)) {
3455 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3456 return false;
3459 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3460 std::string encoded_key;
3461 EncodeIDBKey(*current_key_, &encoded_key);
3462 record_identifier_.Reset(encoded_key, version);
3464 if (!transaction_->GetBlobInfoForRecord(database_id_,
3465 iterator_->Key().as_string(),
3466 &current_value_).ok()) {
3467 return false;
3469 current_value_.bits = value_slice.as_string();
3470 return true;
3473 class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3474 public:
3475 IndexKeyCursorImpl(
3476 scoped_refptr<IndexedDBBackingStore> backing_store,
3477 IndexedDBBackingStore::Transaction* transaction,
3478 int64 database_id,
3479 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3480 : IndexedDBBackingStore::Cursor(backing_store,
3481 transaction,
3482 database_id,
3483 cursor_options) {}
3485 Cursor* Clone() override { return new IndexKeyCursorImpl(this); }
3487 // IndexedDBBackingStore::Cursor
3488 IndexedDBValue* value() override {
3489 NOTREACHED();
3490 return NULL;
3492 const IndexedDBKey& primary_key() const override { return *primary_key_; }
3493 const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3494 const override {
3495 NOTREACHED();
3496 return record_identifier_;
3498 bool LoadCurrentRow() override;
3500 protected:
3501 std::string EncodeKey(const IndexedDBKey& key) override {
3502 return IndexDataKey::Encode(cursor_options_.database_id,
3503 cursor_options_.object_store_id,
3504 cursor_options_.index_id,
3505 key);
3507 std::string EncodeKey(const IndexedDBKey& key,
3508 const IndexedDBKey& primary_key) override {
3509 return IndexDataKey::Encode(cursor_options_.database_id,
3510 cursor_options_.object_store_id,
3511 cursor_options_.index_id,
3512 key,
3513 primary_key);
3516 private:
3517 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl* other)
3518 : IndexedDBBackingStore::Cursor(other),
3519 primary_key_(new IndexedDBKey(*other->primary_key_)) {}
3521 scoped_ptr<IndexedDBKey> primary_key_;
3523 DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl);
3526 bool IndexKeyCursorImpl::LoadCurrentRow() {
3527 StringPiece slice(iterator_->Key());
3528 IndexDataKey index_data_key;
3529 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3530 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3531 return false;
3534 current_key_ = index_data_key.user_key();
3535 DCHECK(current_key_);
3537 slice = StringPiece(iterator_->Value());
3538 int64 index_data_version;
3539 if (!DecodeVarInt(&slice, &index_data_version)) {
3540 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3541 return false;
3544 if (!DecodeIDBKey(&slice, &primary_key_) || !slice.empty()) {
3545 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3546 return false;
3549 std::string primary_leveldb_key =
3550 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3551 index_data_key.ObjectStoreId(),
3552 *primary_key_);
3554 std::string result;
3555 bool found = false;
3556 leveldb::Status s =
3557 transaction_->transaction()->Get(primary_leveldb_key, &result, &found);
3558 if (!s.ok()) {
3559 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3560 return false;
3562 if (!found) {
3563 transaction_->transaction()->Remove(iterator_->Key());
3564 return false;
3566 if (!result.size()) {
3567 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3568 return false;
3571 int64 object_store_data_version;
3572 slice = StringPiece(result);
3573 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3574 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3575 return false;
3578 if (object_store_data_version != index_data_version) {
3579 transaction_->transaction()->Remove(iterator_->Key());
3580 return false;
3583 return true;
3586 class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
3587 public:
3588 IndexCursorImpl(
3589 scoped_refptr<IndexedDBBackingStore> backing_store,
3590 IndexedDBBackingStore::Transaction* transaction,
3591 int64 database_id,
3592 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3593 : IndexedDBBackingStore::Cursor(backing_store,
3594 transaction,
3595 database_id,
3596 cursor_options) {}
3598 Cursor* Clone() override { return new IndexCursorImpl(this); }
3600 // IndexedDBBackingStore::Cursor
3601 IndexedDBValue* value() override { return &current_value_; }
3602 const IndexedDBKey& primary_key() const override { return *primary_key_; }
3603 const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3604 const override {
3605 NOTREACHED();
3606 return record_identifier_;
3608 bool LoadCurrentRow() override;
3610 protected:
3611 std::string EncodeKey(const IndexedDBKey& key) override {
3612 return IndexDataKey::Encode(cursor_options_.database_id,
3613 cursor_options_.object_store_id,
3614 cursor_options_.index_id,
3615 key);
3617 std::string EncodeKey(const IndexedDBKey& key,
3618 const IndexedDBKey& primary_key) override {
3619 return IndexDataKey::Encode(cursor_options_.database_id,
3620 cursor_options_.object_store_id,
3621 cursor_options_.index_id,
3622 key,
3623 primary_key);
3626 private:
3627 explicit IndexCursorImpl(const IndexCursorImpl* other)
3628 : IndexedDBBackingStore::Cursor(other),
3629 primary_key_(new IndexedDBKey(*other->primary_key_)),
3630 current_value_(other->current_value_),
3631 primary_leveldb_key_(other->primary_leveldb_key_) {}
3633 scoped_ptr<IndexedDBKey> primary_key_;
3634 IndexedDBValue current_value_;
3635 std::string primary_leveldb_key_;
3637 DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl);
3640 bool IndexCursorImpl::LoadCurrentRow() {
3641 StringPiece slice(iterator_->Key());
3642 IndexDataKey index_data_key;
3643 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3644 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3645 return false;
3648 current_key_ = index_data_key.user_key();
3649 DCHECK(current_key_);
3651 slice = StringPiece(iterator_->Value());
3652 int64 index_data_version;
3653 if (!DecodeVarInt(&slice, &index_data_version)) {
3654 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3655 return false;
3657 if (!DecodeIDBKey(&slice, &primary_key_)) {
3658 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3659 return false;
3662 DCHECK_EQ(index_data_key.DatabaseId(), database_id_);
3663 primary_leveldb_key_ =
3664 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3665 index_data_key.ObjectStoreId(),
3666 *primary_key_);
3668 std::string result;
3669 bool found = false;
3670 leveldb::Status s =
3671 transaction_->transaction()->Get(primary_leveldb_key_, &result, &found);
3672 if (!s.ok()) {
3673 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3674 return false;
3676 if (!found) {
3677 transaction_->transaction()->Remove(iterator_->Key());
3678 return false;
3680 if (!result.size()) {
3681 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3682 return false;
3685 int64 object_store_data_version;
3686 slice = StringPiece(result);
3687 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3688 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3689 return false;
3692 if (object_store_data_version != index_data_version) {
3693 transaction_->transaction()->Remove(iterator_->Key());
3694 return false;
3697 current_value_.bits = slice.as_string();
3698 return transaction_->GetBlobInfoForRecord(database_id_,
3699 primary_leveldb_key_,
3700 &current_value_).ok();
3703 bool ObjectStoreCursorOptions(
3704 LevelDBTransaction* transaction,
3705 int64 database_id,
3706 int64 object_store_id,
3707 const IndexedDBKeyRange& range,
3708 blink::WebIDBCursorDirection direction,
3709 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3710 cursor_options->database_id = database_id;
3711 cursor_options->object_store_id = object_store_id;
3713 bool lower_bound = range.lower().IsValid();
3714 bool upper_bound = range.upper().IsValid();
3715 cursor_options->forward =
3716 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3717 direction == blink::WebIDBCursorDirectionNext);
3718 cursor_options->unique =
3719 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3720 direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
3722 if (!lower_bound) {
3723 cursor_options->low_key =
3724 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
3725 cursor_options->low_open = true; // Not included.
3726 } else {
3727 cursor_options->low_key =
3728 ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower());
3729 cursor_options->low_open = range.lower_open();
3732 leveldb::Status s;
3734 if (!upper_bound) {
3735 cursor_options->high_key =
3736 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
3738 if (cursor_options->forward) {
3739 cursor_options->high_open = true; // Not included.
3740 } else {
3741 // We need a key that exists.
3742 // TODO(cmumford): Handle this error (crbug.com/363397)
3743 if (!FindGreatestKeyLessThanOrEqual(transaction,
3744 cursor_options->high_key,
3745 &cursor_options->high_key,
3746 &s))
3747 return false;
3748 cursor_options->high_open = false;
3750 } else {
3751 cursor_options->high_key =
3752 ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper());
3753 cursor_options->high_open = range.upper_open();
3755 if (!cursor_options->forward) {
3756 // For reverse cursors, we need a key that exists.
3757 std::string found_high_key;
3758 // TODO(cmumford): Handle this error (crbug.com/363397)
3759 if (!FindGreatestKeyLessThanOrEqual(
3760 transaction, cursor_options->high_key, &found_high_key, &s))
3761 return false;
3763 // If the target key should not be included, but we end up with a smaller
3764 // key, we should include that.
3765 if (cursor_options->high_open &&
3766 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3767 cursor_options->high_open = false;
3769 cursor_options->high_key = found_high_key;
3773 return true;
3776 bool IndexCursorOptions(
3777 LevelDBTransaction* transaction,
3778 int64 database_id,
3779 int64 object_store_id,
3780 int64 index_id,
3781 const IndexedDBKeyRange& range,
3782 blink::WebIDBCursorDirection direction,
3783 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3784 DCHECK(transaction);
3785 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3786 return false;
3788 cursor_options->database_id = database_id;
3789 cursor_options->object_store_id = object_store_id;
3790 cursor_options->index_id = index_id;
3792 bool lower_bound = range.lower().IsValid();
3793 bool upper_bound = range.upper().IsValid();
3794 cursor_options->forward =
3795 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3796 direction == blink::WebIDBCursorDirectionNext);
3797 cursor_options->unique =
3798 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3799 direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
3801 if (!lower_bound) {
3802 cursor_options->low_key =
3803 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
3804 cursor_options->low_open = false; // Included.
3805 } else {
3806 cursor_options->low_key = IndexDataKey::Encode(
3807 database_id, object_store_id, index_id, range.lower());
3808 cursor_options->low_open = range.lower_open();
3811 leveldb::Status s;
3813 if (!upper_bound) {
3814 cursor_options->high_key =
3815 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
3816 cursor_options->high_open = false; // Included.
3818 if (!cursor_options->forward) { // We need a key that exists.
3819 if (!FindGreatestKeyLessThanOrEqual(transaction,
3820 cursor_options->high_key,
3821 &cursor_options->high_key,
3822 &s))
3823 return false;
3824 cursor_options->high_open = false;
3826 } else {
3827 cursor_options->high_key = IndexDataKey::Encode(
3828 database_id, object_store_id, index_id, range.upper());
3829 cursor_options->high_open = range.upper_open();
3831 std::string found_high_key;
3832 // Seek to the *last* key in the set of non-unique keys
3833 // TODO(cmumford): Handle this error (crbug.com/363397)
3834 if (!FindGreatestKeyLessThanOrEqual(
3835 transaction, cursor_options->high_key, &found_high_key, &s))
3836 return false;
3838 // If the target key should not be included, but we end up with a smaller
3839 // key, we should include that.
3840 if (cursor_options->high_open &&
3841 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3842 cursor_options->high_open = false;
3844 cursor_options->high_key = found_high_key;
3847 return true;
3850 scoped_ptr<IndexedDBBackingStore::Cursor>
3851 IndexedDBBackingStore::OpenObjectStoreCursor(
3852 IndexedDBBackingStore::Transaction* transaction,
3853 int64 database_id,
3854 int64 object_store_id,
3855 const IndexedDBKeyRange& range,
3856 blink::WebIDBCursorDirection direction,
3857 leveldb::Status* s) {
3858 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3859 *s = leveldb::Status::OK();
3860 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3861 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3862 if (!ObjectStoreCursorOptions(leveldb_transaction,
3863 database_id,
3864 object_store_id,
3865 range,
3866 direction,
3867 &cursor_options))
3868 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3869 scoped_ptr<ObjectStoreCursorImpl> cursor(new ObjectStoreCursorImpl(
3870 this, transaction, database_id, cursor_options));
3871 if (!cursor->FirstSeek(s))
3872 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3874 return cursor.Pass();
3877 scoped_ptr<IndexedDBBackingStore::Cursor>
3878 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3879 IndexedDBBackingStore::Transaction* transaction,
3880 int64 database_id,
3881 int64 object_store_id,
3882 const IndexedDBKeyRange& range,
3883 blink::WebIDBCursorDirection direction,
3884 leveldb::Status* s) {
3885 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3886 *s = leveldb::Status::OK();
3887 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3888 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3889 if (!ObjectStoreCursorOptions(leveldb_transaction,
3890 database_id,
3891 object_store_id,
3892 range,
3893 direction,
3894 &cursor_options))
3895 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3896 scoped_ptr<ObjectStoreKeyCursorImpl> cursor(new ObjectStoreKeyCursorImpl(
3897 this, transaction, database_id, cursor_options));
3898 if (!cursor->FirstSeek(s))
3899 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3901 return cursor.Pass();
3904 scoped_ptr<IndexedDBBackingStore::Cursor>
3905 IndexedDBBackingStore::OpenIndexKeyCursor(
3906 IndexedDBBackingStore::Transaction* transaction,
3907 int64 database_id,
3908 int64 object_store_id,
3909 int64 index_id,
3910 const IndexedDBKeyRange& range,
3911 blink::WebIDBCursorDirection direction,
3912 leveldb::Status* s) {
3913 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3914 *s = leveldb::Status::OK();
3915 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3916 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3917 if (!IndexCursorOptions(leveldb_transaction,
3918 database_id,
3919 object_store_id,
3920 index_id,
3921 range,
3922 direction,
3923 &cursor_options))
3924 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3925 scoped_ptr<IndexKeyCursorImpl> cursor(
3926 new IndexKeyCursorImpl(this, transaction, database_id, cursor_options));
3927 if (!cursor->FirstSeek(s))
3928 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3930 return cursor.Pass();
3933 scoped_ptr<IndexedDBBackingStore::Cursor>
3934 IndexedDBBackingStore::OpenIndexCursor(
3935 IndexedDBBackingStore::Transaction* transaction,
3936 int64 database_id,
3937 int64 object_store_id,
3938 int64 index_id,
3939 const IndexedDBKeyRange& range,
3940 blink::WebIDBCursorDirection direction,
3941 leveldb::Status* s) {
3942 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
3943 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3944 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3945 if (!IndexCursorOptions(leveldb_transaction,
3946 database_id,
3947 object_store_id,
3948 index_id,
3949 range,
3950 direction,
3951 &cursor_options))
3952 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3953 scoped_ptr<IndexCursorImpl> cursor(
3954 new IndexCursorImpl(this, transaction, database_id, cursor_options));
3955 if (!cursor->FirstSeek(s))
3956 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3958 return cursor.Pass();
3961 IndexedDBBackingStore::Transaction::Transaction(
3962 IndexedDBBackingStore* backing_store)
3963 : backing_store_(backing_store), database_id_(-1), committing_(false) {
3966 IndexedDBBackingStore::Transaction::~Transaction() {
3967 STLDeleteContainerPairSecondPointers(
3968 blob_change_map_.begin(), blob_change_map_.end());
3969 STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
3970 incognito_blob_map_.end());
3971 DCHECK(!committing_);
3974 void IndexedDBBackingStore::Transaction::Begin() {
3975 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
3976 DCHECK(!transaction_.get());
3977 transaction_ = IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3978 backing_store_->db_.get());
3980 // If incognito, this snapshots blobs just as the above transaction_
3981 // constructor snapshots the leveldb.
3982 for (const auto& iter : backing_store_->incognito_blob_map_)
3983 incognito_blob_map_[iter.first] = iter.second->Clone().release();
3986 static GURL GetURLFromUUID(const string& uuid) {
3987 return GURL("blob:uuid/" + uuid);
3990 leveldb::Status IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
3991 BlobEntryKeyValuePairVec* new_blob_entries,
3992 WriteDescriptorVec* new_files_to_write) {
3993 if (backing_store_->is_incognito())
3994 return leveldb::Status::OK();
3996 DCHECK(new_blob_entries->empty());
3997 DCHECK(new_files_to_write->empty());
3998 DCHECK(blobs_to_write_.empty());
4000 if (blob_change_map_.empty())
4001 return leveldb::Status::OK();
4003 // Create LevelDBTransaction for the name generator seed and add-journal.
4004 scoped_refptr<LevelDBTransaction> pre_transaction =
4005 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4006 backing_store_->db_.get());
4008 for (auto& iter : blob_change_map_) {
4009 std::vector<IndexedDBBlobInfo*> new_blob_keys;
4010 for (auto& entry : iter.second->mutable_blob_info()) {
4011 int64 next_blob_key = -1;
4012 bool result = GetBlobKeyGeneratorCurrentNumber(
4013 pre_transaction.get(), database_id_, &next_blob_key);
4014 if (!result || next_blob_key < 0)
4015 return InternalInconsistencyStatus();
4016 blobs_to_write_.push_back(std::make_pair(database_id_, next_blob_key));
4017 if (entry.is_file() && !entry.file_path().empty()) {
4018 new_files_to_write->push_back(
4019 WriteDescriptor(entry.file_path(), next_blob_key, entry.size(),
4020 entry.last_modified()));
4021 } else {
4022 new_files_to_write->push_back(
4023 WriteDescriptor(GetURLFromUUID(entry.uuid()), next_blob_key,
4024 entry.size(), entry.last_modified()));
4026 entry.set_key(next_blob_key);
4027 new_blob_keys.push_back(&entry);
4028 result = UpdateBlobKeyGeneratorCurrentNumber(
4029 pre_transaction.get(), database_id_, next_blob_key + 1);
4030 if (!result)
4031 return InternalInconsistencyStatus();
4033 BlobEntryKey blob_entry_key;
4034 StringPiece key_piece(iter.second->key());
4035 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
4036 NOTREACHED();
4037 return InternalInconsistencyStatus();
4039 new_blob_entries->push_back(
4040 std::make_pair(blob_entry_key, EncodeBlobData(new_blob_keys)));
4043 AppendBlobsToPrimaryBlobJournal(pre_transaction.get(), blobs_to_write_);
4044 leveldb::Status s = pre_transaction->Commit();
4045 if (!s.ok())
4046 return InternalInconsistencyStatus();
4048 return leveldb::Status::OK();
4051 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
4052 if (backing_store_->is_incognito())
4053 return true;
4055 // Look up all old files to remove as part of the transaction, store their
4056 // names in blobs_to_remove_, and remove their old blob data entries.
4057 for (const auto& iter : blob_change_map_) {
4058 BlobEntryKey blob_entry_key;
4059 StringPiece key_piece(iter.second->key());
4060 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
4061 NOTREACHED();
4062 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4063 transaction_ = NULL;
4064 return false;
4066 if (database_id_ < 0)
4067 database_id_ = blob_entry_key.database_id();
4068 else
4069 DCHECK_EQ(database_id_, blob_entry_key.database_id());
4070 std::string blob_entry_key_bytes = blob_entry_key.Encode();
4071 bool found;
4072 std::string blob_entry_value_bytes;
4073 leveldb::Status s = transaction_->Get(
4074 blob_entry_key_bytes, &blob_entry_value_bytes, &found);
4075 if (s.ok() && found) {
4076 std::vector<IndexedDBBlobInfo> blob_info;
4077 if (!DecodeBlobData(blob_entry_value_bytes, &blob_info)) {
4078 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4079 transaction_ = NULL;
4080 return false;
4082 for (const auto& blob : blob_info) {
4083 blobs_to_remove_.push_back(std::make_pair(database_id_, blob.key()));
4084 transaction_->Remove(blob_entry_key_bytes);
4088 return true;
4091 void IndexedDBBackingStore::Transaction::PartitionBlobsToRemove(
4092 BlobJournalType* dead_blobs,
4093 BlobJournalType* live_blobs) const {
4094 IndexedDBActiveBlobRegistry* registry =
4095 backing_store_->active_blob_registry();
4096 for (const auto& iter : blobs_to_remove_) {
4097 if (registry->MarkDeletedCheckIfUsed(iter.first, iter.second))
4098 live_blobs->push_back(iter);
4099 else
4100 dead_blobs->push_back(iter);
4104 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne(
4105 scoped_refptr<BlobWriteCallback> callback) {
4106 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
4107 DCHECK(transaction_.get());
4108 DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
4110 leveldb::Status s;
4112 BlobEntryKeyValuePairVec new_blob_entries;
4113 WriteDescriptorVec new_files_to_write;
4114 s = HandleBlobPreTransaction(&new_blob_entries, &new_files_to_write);
4115 if (!s.ok()) {
4116 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4117 transaction_ = NULL;
4118 return s;
4121 DCHECK(!new_files_to_write.size() ||
4122 KeyPrefix::IsValidDatabaseId(database_id_));
4123 if (!CollectBlobFilesToRemove()) {
4124 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4125 transaction_ = NULL;
4126 return InternalInconsistencyStatus();
4129 committing_ = true;
4130 ++backing_store_->committing_transaction_count_;
4132 if (!new_files_to_write.empty()) {
4133 // This kicks off the writes of the new blobs, if any.
4134 // This call will zero out new_blob_entries and new_files_to_write.
4135 WriteNewBlobs(&new_blob_entries, &new_files_to_write, callback);
4136 } else {
4137 callback->Run(true);
4140 return leveldb::Status::OK();
4143 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
4144 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
4145 leveldb::Status s;
4147 DCHECK(committing_);
4148 committing_ = false;
4149 DCHECK_GT(backing_store_->committing_transaction_count_, 0UL);
4150 --backing_store_->committing_transaction_count_;
4152 BlobJournalType primary_journal, live_journal, saved_primary_journal,
4153 dead_blobs;
4154 if (!blob_change_map_.empty()) {
4155 // Read the persisted states of the primary/live blob journals,
4156 // so that they can be updated correctly by the transaction.
4157 scoped_refptr<LevelDBTransaction> journal_transaction =
4158 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4159 backing_store_->db_.get());
4160 s = GetPrimaryBlobJournal(journal_transaction.get(), &primary_journal);
4161 if (!s.ok())
4162 return s;
4163 s = GetLiveBlobJournal(journal_transaction.get(), &live_journal);
4164 if (!s.ok())
4165 return s;
4167 // Remove newly added blobs from the journal - they will be accounted
4168 // for in blob entry tables in the transaction.
4169 std::sort(primary_journal.begin(), primary_journal.end());
4170 std::sort(blobs_to_write_.begin(), blobs_to_write_.end());
4171 BlobJournalType new_journal = base::STLSetDifference<BlobJournalType>(
4172 primary_journal, blobs_to_write_);
4173 primary_journal.swap(new_journal);
4175 // Append newly deleted blobs to appropriate primary/live journals.
4176 saved_primary_journal = primary_journal;
4177 BlobJournalType live_blobs;
4178 if (!blobs_to_remove_.empty()) {
4179 DCHECK(!backing_store_->is_incognito());
4180 PartitionBlobsToRemove(&dead_blobs, &live_blobs);
4182 primary_journal.insert(primary_journal.end(), dead_blobs.begin(),
4183 dead_blobs.end());
4184 live_journal.insert(live_journal.end(), live_blobs.begin(),
4185 live_blobs.end());
4186 UpdatePrimaryBlobJournal(transaction_.get(), primary_journal);
4187 UpdateLiveBlobJournal(transaction_.get(), live_journal);
4190 // Actually commit. If this succeeds, the journals will appropriately
4191 // reflect pending blob work - dead files that should be deleted
4192 // immediately, and live files to monitor.
4193 s = transaction_->Commit();
4194 transaction_ = NULL;
4196 if (!s.ok()) {
4197 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
4198 return s;
4201 if (backing_store_->is_incognito()) {
4202 if (!blob_change_map_.empty()) {
4203 BlobChangeMap& target_map = backing_store_->incognito_blob_map_;
4204 for (auto& iter : blob_change_map_) {
4205 BlobChangeMap::iterator target_record = target_map.find(iter.first);
4206 if (target_record != target_map.end()) {
4207 delete target_record->second;
4208 target_map.erase(target_record);
4210 if (iter.second) {
4211 target_map[iter.first] = iter.second;
4212 iter.second = NULL;
4216 return leveldb::Status::OK();
4219 // Actually delete dead blob files, then remove those entries
4220 // from the persisted primary journal.
4221 if (dead_blobs.empty())
4222 return leveldb::Status::OK();
4224 DCHECK(!blob_change_map_.empty());
4226 s = backing_store_->CleanUpBlobJournalEntries(dead_blobs);
4227 if (!s.ok()) {
4228 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4229 return s;
4232 scoped_refptr<LevelDBTransaction> update_journal_transaction =
4233 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4234 backing_store_->db_.get());
4235 UpdatePrimaryBlobJournal(update_journal_transaction.get(),
4236 saved_primary_journal);
4237 s = update_journal_transaction->Commit();
4238 return s;
4242 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
4243 : public IndexedDBBackingStore::BlobWriteCallback {
4244 public:
4245 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction,
4246 scoped_refptr<BlobWriteCallback> callback)
4247 : transaction_(transaction), callback_(callback) {}
4248 void Run(bool succeeded) override {
4249 callback_->Run(succeeded);
4250 if (succeeded) // Else it's already been deleted during rollback.
4251 transaction_->chained_blob_writer_ = NULL;
4254 private:
4255 ~BlobWriteCallbackWrapper() override {}
4256 friend class base::RefCounted<IndexedDBBackingStore::BlobWriteCallback>;
4258 IndexedDBBackingStore::Transaction* transaction_;
4259 scoped_refptr<BlobWriteCallback> callback_;
4261 DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper);
4264 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
4265 BlobEntryKeyValuePairVec* new_blob_entries,
4266 WriteDescriptorVec* new_files_to_write,
4267 scoped_refptr<BlobWriteCallback> callback) {
4268 DCHECK_GT(new_files_to_write->size(), 0UL);
4269 DCHECK_GT(database_id_, 0);
4270 for (auto& blob_entry_iter : *new_blob_entries) {
4271 // Add the new blob-table entry for each blob to the main transaction, or
4272 // remove any entry that may exist if there's no new one.
4273 if (blob_entry_iter.second.empty())
4274 transaction_->Remove(blob_entry_iter.first.Encode());
4275 else
4276 transaction_->Put(blob_entry_iter.first.Encode(),
4277 &blob_entry_iter.second);
4279 // Creating the writer will start it going asynchronously.
4280 chained_blob_writer_ =
4281 new ChainedBlobWriterImpl(database_id_,
4282 backing_store_,
4283 new_files_to_write,
4284 new BlobWriteCallbackWrapper(this, callback));
4287 void IndexedDBBackingStore::Transaction::Rollback() {
4288 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
4289 if (committing_) {
4290 committing_ = false;
4291 DCHECK_GT(backing_store_->committing_transaction_count_, 0UL);
4292 --backing_store_->committing_transaction_count_;
4295 if (chained_blob_writer_.get()) {
4296 chained_blob_writer_->Abort();
4297 chained_blob_writer_ = NULL;
4299 if (transaction_.get() == NULL)
4300 return;
4301 transaction_->Rollback();
4302 transaction_ = NULL;
4305 IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
4306 const std::string& key,
4307 int64 object_store_id)
4308 : key_(key), object_store_id_(object_store_id) {
4311 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
4314 void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
4315 std::vector<IndexedDBBlobInfo>* blob_info) {
4316 blob_info_.clear();
4317 if (blob_info)
4318 blob_info_.swap(*blob_info);
4321 void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
4322 ScopedVector<storage::BlobDataHandle>* handles) {
4323 handles_.clear();
4324 if (handles)
4325 handles_.swap(*handles);
4328 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord>
4329 IndexedDBBackingStore::BlobChangeRecord::Clone() const {
4330 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord> record(
4331 new BlobChangeRecord(key_, object_store_id_));
4332 record->blob_info_ = blob_info_;
4334 for (const auto* handle : handles_)
4335 record->handles_.push_back(new storage::BlobDataHandle(*handle));
4336 return record.Pass();
4339 leveldb::Status IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
4340 int64 database_id,
4341 int64 object_store_id,
4342 const std::string& object_store_data_key,
4343 std::vector<IndexedDBBlobInfo>* blob_info,
4344 ScopedVector<storage::BlobDataHandle>* handles) {
4345 if (!blob_info || blob_info->empty()) {
4346 blob_change_map_.erase(object_store_data_key);
4347 incognito_blob_map_.erase(object_store_data_key);
4349 BlobEntryKey blob_entry_key;
4350 StringPiece leveldb_key_piece(object_store_data_key);
4351 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
4352 &blob_entry_key)) {
4353 NOTREACHED();
4354 return InternalInconsistencyStatus();
4356 std::string value;
4357 bool found = false;
4358 leveldb::Status s =
4359 transaction()->Get(blob_entry_key.Encode(), &value, &found);
4360 if (!s.ok())
4361 return s;
4362 if (!found)
4363 return leveldb::Status::OK();
4365 PutBlobInfo(
4366 database_id, object_store_id, object_store_data_key, blob_info, handles);
4367 return leveldb::Status::OK();
4370 // This is storing an info, even if empty, even if the previous key had no blob
4371 // info that we know of. It duplicates a bunch of information stored in the
4372 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
4373 // changes to exists or index keys here.
4374 void IndexedDBBackingStore::Transaction::PutBlobInfo(
4375 int64 database_id,
4376 int64 object_store_id,
4377 const std::string& object_store_data_key,
4378 std::vector<IndexedDBBlobInfo>* blob_info,
4379 ScopedVector<storage::BlobDataHandle>* handles) {
4380 DCHECK_GT(object_store_data_key.size(), 0UL);
4381 if (database_id_ < 0)
4382 database_id_ = database_id;
4383 DCHECK_EQ(database_id_, database_id);
4385 BlobChangeMap::iterator it = blob_change_map_.find(object_store_data_key);
4386 BlobChangeRecord* record = NULL;
4387 if (it == blob_change_map_.end()) {
4388 record = new BlobChangeRecord(object_store_data_key, object_store_id);
4389 blob_change_map_[object_store_data_key] = record;
4390 } else {
4391 record = it->second;
4393 DCHECK_EQ(record->object_store_id(), object_store_id);
4394 record->SetBlobInfo(blob_info);
4395 record->SetHandles(handles);
4396 DCHECK(!handles || !handles->size());
4399 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4400 const GURL& url,
4401 int64_t key,
4402 int64_t size,
4403 base::Time last_modified)
4404 : is_file_(false),
4405 url_(url),
4406 key_(key),
4407 size_(size),
4408 last_modified_(last_modified) {
4411 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4412 const FilePath& file_path,
4413 int64_t key,
4414 int64_t size,
4415 base::Time last_modified)
4416 : is_file_(true),
4417 file_path_(file_path),
4418 key_(key),
4419 size_(size),
4420 last_modified_(last_modified) {
4423 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4424 const WriteDescriptor& other) = default;
4425 IndexedDBBackingStore::Transaction::WriteDescriptor::~WriteDescriptor() =
4426 default;
4427 IndexedDBBackingStore::Transaction::WriteDescriptor&
4428 IndexedDBBackingStore::Transaction::WriteDescriptor::
4429 operator=(const WriteDescriptor& other) = default;
4431 } // namespace content