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"
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"
48 using base::StringPiece
;
50 using storage::FileWriterDelegate
;
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
,
63 FilePath path
= GetBlobDirectoryName(path_base
, database_id
);
64 path
= path
.AppendASCII(base::StringPrintf(
65 "%02x", static_cast<int>(key
& 0x000000000000ff00) >> 8));
69 FilePath
GetBlobFileNameForKey(const FilePath
& path_base
,
72 FilePath path
= GetBlobDirectoryNameForKey(path_base
, database_id
, key
);
73 path
= path
.AppendASCII(base::StringPrintf("%" PRIx64
, key
));
77 bool MakeIDBBlobDirectory(const FilePath
& path_base
,
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"));
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
,
115 GET_KEY_GENERATOR_CURRENT_NUMBER
,
118 KEY_EXISTS_IN_OBJECT_STORE
,
121 GET_PRIMARY_KEY_VIA_INDEX
,
125 SET_MAX_OBJECT_STORE_ID
,
128 GET_NEW_VERSION_NUMBER
,
129 CREATE_IDBDATABASE_METADATA
,
131 TRANSACTION_COMMIT_METHOD
, // TRANSACTION_COMMIT is a WinNT.h macro
137 GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER
,
138 GET_BLOB_INFO_FOR_RECORD
,
142 static void RecordInternalError(const char* type
,
143 IndexedDBBackingStoreErrorSource location
) {
145 name
.append("WebCore.IndexedDB.BackingStore.").append(type
).append("Error");
146 base::Histogram::FactoryGet(name
,
149 INTERNAL_ERROR_MAX
+ 1,
150 base::HistogramBase::kUmaTargetedHistogramFlag
)
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) \
159 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
160 RecordInternalError(type, location); \
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) \
175 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
177 RecordInternalError(type, location); \
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
,
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
,
215 leveldb::Status s
= db
->Get(key
, &result
, found
);
219 return leveldb::Status::OK();
220 StringPiece
slice(result
);
221 if (DecodeInt(&slice
, found_int
) && slice
.empty())
223 return InternalInconsistencyStatus();
226 static void PutInt(LevelDBTransaction
* transaction
,
227 const StringPiece
& key
,
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
,
241 leveldb::Status s
= db
->Get(key
, &result
, found
);
245 return leveldb::Status::OK();
246 StringPiece
slice(result
);
247 if (DecodeVarInt(&slice
, found_int
) && slice
.empty())
249 return InternalInconsistencyStatus();
252 static void PutVarInt(LevelDBTransaction
* transaction
,
253 const StringPiece
& key
,
256 EncodeVarInt(value
, &buffer
);
257 transaction
->Put(key
, &buffer
);
260 template <typename DBOrTransaction
>
261 WARN_UNUSED_RESULT
static leveldb::Status
GetString(
263 const StringPiece
& key
,
264 base::string16
* found_string
,
268 leveldb::Status s
= db
->Get(key
, &result
, found
);
272 return leveldb::Status::OK();
273 StringPiece
slice(result
);
274 if (DecodeString(&slice
, found_string
) && slice
.empty())
276 return InternalInconsistencyStatus();
279 static void PutString(LevelDBTransaction
* transaction
,
280 const StringPiece
& key
,
281 const base::string16
& value
) {
283 EncodeString(value
, &buffer
);
284 transaction
->Put(key
, &buffer
);
287 static void PutIDBKeyPath(LevelDBTransaction
* transaction
,
288 const StringPiece
& key
,
289 const IndexedDBKeyPath
& value
) {
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 {
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;
321 GetInt(db
, SchemaVersionKey::Encode(), &db_schema_version
, &found
);
328 if (db_schema_version
> kLatestKnownSchemaVersion
) {
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
);
344 if (db_data_version
> latest_known_data_version
) {
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;
368 GetInt(transaction
.get(), schema_version_key
, &db_schema_version
, &found
);
370 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
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();
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;
399 int64 database_id
= 0;
401 s
= GetInt(transaction
.get(), it
->Key(), &database_id
, &found
);
403 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
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(),
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();
433 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
437 // All new values will be written using this serialization version.
439 s
= GetInt(transaction
.get(), data_version_key
, &db_data_version
, &found
);
441 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
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();
458 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA
);
462 template <typename DBOrTransaction
>
463 WARN_UNUSED_RESULT
static leveldb::Status
GetMaxObjectStoreId(
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(
475 const std::string
& max_object_store_id_key
,
476 int64
* max_object_store_id
) {
477 *max_object_store_id
= -1;
480 GetInt(db
, max_object_store_id_key
, max_object_store_id
, &found
);
484 *max_object_store_id
= 0;
486 DCHECK_GE(*max_object_store_id
, 0);
490 class DefaultLevelDBFactory
: public LevelDBFactory
{
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
);
504 DISALLOW_COPY_AND_ASSIGN(DefaultLevelDBFactory
);
507 static bool GetBlobKeyGeneratorCurrentNumber(
508 LevelDBTransaction
* leveldb_transaction
,
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
;
519 bool ok
= leveldb_transaction
->Get(key_gen_key
, &data
, &found
).ok();
521 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
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
);
532 *blob_key_generator_current_number
= cur_number
;
536 static bool UpdateBlobKeyGeneratorCurrentNumber(
537 LevelDBTransaction
* leveldb_transaction
,
539 int64 blob_key_generator_current_number
) {
542 if (!GetBlobKeyGeneratorCurrentNumber(
543 leveldb_transaction
, database_id
, &old_number
))
545 DCHECK_LT(old_number
, blob_key_generator_current_number
);
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
);
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
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
) {
570 leveldb::Status s
= transaction
->Get(key
, &data
, &found
);
572 INTERNAL_READ_ERROR(READ_BLOB_JOURNAL
);
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();
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
) {
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
) {
637 return leveldb::Status::OK();
638 BlobJournalType old_journal
;
639 leveldb::Status s
= GetBlobJournal(key
, transaction
, &old_journal
);
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(),
655 template <typename TransactionType
>
656 static leveldb::Status
AppendBlobsToLiveBlobJournal(
657 TransactionType
* transaction
,
658 const BlobJournalType
& journal
) {
659 return AppendBlobsToBlobJournal(transaction
, LiveBlobJournalKey::Encode(),
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
,
669 BlobJournalType journal
;
670 leveldb::Status s
= GetBlobJournal(key
, transaction
, &journal
);
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
,
682 return MergeDatabaseIntoBlobJournal(leveldb_transaction
,
683 BlobJournalKey::Encode(), database_id
);
686 static leveldb::Status
MergeDatabaseIntoLiveBlobJournal(
687 LevelDBDirectTransaction
* leveldb_transaction
,
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]
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
) {
703 for (const auto* info
: blob_info
) {
704 EncodeBool(info
->is_file(), &ret
);
705 EncodeVarInt(info
->key(), &ret
);
706 EncodeStringWithLength(info
->type(), &ret
);
708 EncodeStringWithLength(info
->file_name(), &ret
);
710 EncodeVarInt(info
->size(), &ret
);
715 static bool DecodeBlobData(const std::string
& data
,
716 std::vector
<IndexedDBBlobInfo
>* output
) {
717 std::vector
<IndexedDBBlobInfo
> ret
;
719 StringPiece
slice(data
);
720 while (!slice
.empty()) {
725 base::string16 file_name
;
727 if (!DecodeBool(&slice
, &is_file
))
729 if (!DecodeVarInt(&slice
, &key
) ||
730 !DatabaseMetaDataKey::IsValidBlobKey(key
))
732 if (!DecodeStringWithLength(&slice
, &type
))
735 if (!DecodeStringWithLength(&slice
, &file_name
))
737 ret
.push_back(IndexedDBBlobInfo(key
, type
, file_name
));
739 if (!DecodeVarInt(&slice
, &size
) || size
< 0)
741 ret
.push_back(IndexedDBBlobInfo(type
, static_cast<uint64
>(size
), key
));
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
),
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.
783 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
784 const std::string
& primary_key
,
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
,
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
,
826 base::SequencedTaskRunner
* task_runner
,
828 leveldb::Status
* status
) {
829 *data_loss
= blink::WebIDBDataLossNone
;
830 DefaultLevelDBFactory leveldb_factory
;
831 return IndexedDBBackingStore::Open(indexed_db_factory
,
844 static std::string
OriginToCustomHistogramSuffix(const GURL
& origin_url
) {
845 if (origin_url
.host() == "docs.google.com")
847 return std::string();
850 static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result
,
851 const GURL
& origin_url
) {
852 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.BackingStore.OpenStatus",
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());
873 DLOG(WARNING
) << "GetMaximumPathComponentLength returned -1";
874 // In limited testing, ChromeOS returns 143, other OSes 255.
875 #if defined(OS_CHROMEOS)
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.";
888 const int num_buckets
= 12;
889 UMA_HISTOGRAM_CUSTOM_COUNTS(
890 "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
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
))
918 const int64 max_json_len
= 4096;
920 if (!GetFileSize(info_path
, &file_size
) || file_size
> max_json_len
)
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
);
944 base::DeleteFile(info_path
, false);
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
))
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
);
967 int written
= file
.Write(0, output_js
.c_str(), output_js
.length());
968 return size_t(written
) == output_js
.length();
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
,
980 LevelDBFactory
* leveldb_factory
,
981 base::SequencedTaskRunner
* task_runner
,
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
,
998 if (!base::CreateDirectory(path_base
)) {
1000 leveldb::Status::IOError("Unable to create IndexedDB database path");
1001 LOG(ERROR
) << status
->ToString() << ": \"" << path_base
.AsUTF8Unsafe()
1003 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY
,
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
,
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;
1036 std::string corruption_message
;
1037 if (ReadCorruptionInfo(path_base
, origin_url
, &corruption_message
)) {
1038 LOG(ERROR
) << "IndexedDB recovering from a corrupted (and deleted) "
1040 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION
,
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 "
1049 HistogramOpenStatus(
1050 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA
,
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
,
1061 *data_loss
= blink::WebIDBDataLossTotal
;
1062 *data_loss_message
= "Unknown schema";
1066 DCHECK(status
->ok() || !is_schema_known
|| status
->IsIOError() ||
1067 status
->IsCorruption());
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
>();
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
,
1084 return scoped_refptr
<IndexedDBBackingStore
>();
1087 LOG(ERROR
) << "IndexedDB backing store cleanup succeeded, reopening";
1088 leveldb_factory
->OpenLevelDB(file_path
, comparator
.get(), &db
, NULL
);
1090 LOG(ERROR
) << "IndexedDB backing store reopen after recovery failed";
1091 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED
,
1093 return scoped_refptr
<IndexedDBBackingStore
>();
1095 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS
,
1099 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
1100 Create(indexed_db_factory
,
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
;
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
);
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());
1140 LOG(ERROR
) << "LevelDBDatabase::OpenInMemory failed.";
1141 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED
,
1143 return scoped_refptr
<IndexedDBBackingStore
>();
1145 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS
, origin_url
);
1147 return Create(NULL
/* indexed_db_factory */,
1150 NULL
/* request_context */,
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
,
1176 *status
= backing_store
->SetUpMetadata();
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;
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
) ||
1211 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES
);
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
);
1223 // Look up version by id.
1225 int64 database_version
= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
;
1226 *s
= GetVarInt(db_
.get(),
1227 DatabaseMetaDataKey::Encode(
1228 database_id
, DatabaseMetaDataKey::USER_INT_VERSION
),
1231 if (!s
->ok() || !found
) {
1232 INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES
);
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());
1242 INTERNAL_READ_ERROR(GET_DATABASE_NAMES
);
1247 leveldb::Status
IndexedDBBackingStore::GetIDBDatabaseMetaData(
1248 const base::string16
& name
,
1249 IndexedDBDatabaseMetadata
* metadata
,
1251 const std::string key
= DatabaseNameKey::Encode(origin_identifier_
, name
);
1254 leveldb::Status s
= GetInt(db_
.get(), key
, &metadata
->id
, found
);
1256 INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA
);
1260 return leveldb::Status::OK();
1262 s
= GetString(db_
.get(),
1263 DatabaseMetaDataKey::Encode(metadata
->id
,
1264 DatabaseMetaDataKey::USER_VERSION
),
1268 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
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
,
1282 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
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
);
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
;
1305 DatabaseMetaDataKey::Encode(
1306 metadata
->id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
),
1307 &blob_key_generator_current_number
,
1310 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1314 // This database predates blob support.
1316 } else if (!DatabaseMetaDataKey::IsValidBlobKey(
1317 blob_key_generator_current_number
)) {
1318 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1319 return InternalInconsistencyStatus();
1325 WARN_UNUSED_RESULT
static leveldb::Status
GetNewDatabaseId(
1326 LevelDBTransaction
* transaction
,
1329 int64 max_database_id
= -1;
1332 GetInt(transaction
, MaxDatabaseIdKey::Encode(), &max_database_id
, &found
);
1334 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID
);
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
,
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
);
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
),
1370 DatabaseMetaDataKey::Encode(*row_id
, DatabaseMetaDataKey::USER_VERSION
),
1372 PutVarInt(transaction
.get(),
1373 DatabaseMetaDataKey::Encode(*row_id
,
1374 DatabaseMetaDataKey::USER_INT_VERSION
),
1378 DatabaseMetaDataKey::Encode(
1379 *row_id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
),
1380 DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber
);
1382 s
= transaction
->Commit();
1384 INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA
);
1388 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
1389 IndexedDBBackingStore::Transaction
* transaction
,
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
),
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
,
1408 scoped_ptr
<LevelDBIterator
> it
= transaction
->CreateIterator();
1410 for (s
= it
->Seek(begin
); s
.ok() && it
->IsValid() &&
1411 (upper_open
? CompareKeys(it
->Key(), end
) < 0
1412 : CompareKeys(it
->Key(), end
) <= 0);
1414 transaction
->Remove(it
->Key());
1418 static leveldb::Status
DeleteBlobsInRange(
1419 IndexedDBBackingStore::Transaction
* transaction
,
1421 int64 object_store_id
,
1422 const std::string
& start_key
,
1423 const std::string
& end_key
,
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);
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
);
1444 static leveldb::Status
DeleteBlobsInObjectStore(
1445 IndexedDBBackingStore::Transaction
* transaction
,
1447 int64 object_store_id
) {
1448 std::string start_key
, stop_key
;
1450 BlobEntryKey::EncodeMinKeyForObjectStore(database_id
, object_store_id
);
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());
1465 IndexedDBDatabaseMetadata metadata
;
1466 bool success
= false;
1467 s
= GetIDBDatabaseMetaData(name
, &metadata
, &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;
1481 transaction
->Remove(it
->Key());
1483 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE
);
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
);
1497 s
= MergeDatabaseIntoPrimaryBlobJournal(transaction
.get(), metadata
.id
);
1500 need_cleanup
= true;
1503 s
= transaction
->Commit();
1505 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE
);
1509 // If another transaction is running, this will defer processing
1510 // the journal until completion.
1512 CleanPrimaryJournalIgnoreReturn();
1514 db_
->Compact(start_key
, stop_key
);
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)
1525 StringPiece
slice(it
->Key());
1526 ObjectStoreMetaDataKey meta_data_key
;
1528 ObjectStoreMetaDataKey::Decode(&slice
, &meta_data_key
) && slice
.empty();
1530 if (meta_data_key
.ObjectStoreId() != object_store_id
)
1532 if (meta_data_key
.MetaDataType() != meta_data_type
)
1537 // TODO(jsbell): This should do some error handling rather than
1538 // plowing ahead when bad data is encountered.
1539 leveldb::Status
IndexedDBBackingStore::GetObjectStores(
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
;
1558 ObjectStoreMetaDataKey::Decode(&slice
, &meta_data_key
) && slice
.empty();
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.
1569 int64 object_store_id
= meta_data_key
.ObjectStoreId();
1571 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
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
);
1583 if (!CheckObjectStoreAndMetaDataType(it
.get(),
1586 ObjectStoreMetaDataKey::KEY_PATH
)) {
1587 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
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
);
1600 if (!CheckObjectStoreAndMetaDataType(
1604 ObjectStoreMetaDataKey::AUTO_INCREMENT
)) {
1605 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
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.
1618 if (!CheckObjectStoreAndMetaDataType(it
.get(),
1621 ObjectStoreMetaDataKey::EVICTABLE
)) {
1622 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1626 s
= it
->Next(); // Last version.
1629 if (!CheckObjectStoreAndMetaDataType(
1633 ObjectStoreMetaDataKey::LAST_VERSION
)) {
1634 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1638 s
= it
->Next(); // Maximum index id allocated.
1641 if (!CheckObjectStoreAndMetaDataType(
1645 ObjectStoreMetaDataKey::MAX_INDEX_ID
)) {
1646 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
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)
1659 if (CheckObjectStoreAndMetaDataType(it
.get(),
1662 ObjectStoreMetaDataKey::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
);
1680 key_path
= IndexedDBKeyPath();
1686 int64 key_generator_current_number
= -1;
1687 if (CheckObjectStoreAndMetaDataType(
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
);
1705 IndexedDBObjectStoreMetadata
metadata(object_store_name
,
1710 s
= GetIndexes(database_id
, object_store_id
, &metadata
.indexes
);
1713 (*object_stores
)[object_store_id
] = metadata
;
1717 INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES
);
1722 WARN_UNUSED_RESULT
static leveldb::Status
SetMaxObjectStoreId(
1723 LevelDBTransaction
* transaction
,
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
);
1732 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID
);
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
);
1744 void IndexedDBBackingStore::Compact() { db_
->CompactAll(); }
1746 leveldb::Status
IndexedDBBackingStore::CreateObjectStore(
1747 IndexedDBBackingStore::Transaction
* transaction
,
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();
1758 SetMaxObjectStoreId(leveldb_transaction
, database_id
, object_store_id
);
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(
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
);
1797 leveldb::Status
IndexedDBBackingStore::DeleteObjectStore(
1798 IndexedDBBackingStore::Transaction
* transaction
,
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
;
1809 GetString(leveldb_transaction
,
1810 ObjectStoreMetaDataKey::Encode(
1811 database_id
, object_store_id
, ObjectStoreMetaDataKey::NAME
),
1815 INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1819 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1820 return InternalInconsistencyStatus();
1823 s
= DeleteBlobsInObjectStore(transaction
, database_id
, object_store_id
);
1825 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1829 s
= DeleteRangeBasic(
1830 leveldb_transaction
,
1831 ObjectStoreMetaDataKey::Encode(database_id
, object_store_id
, 0),
1832 ObjectStoreMetaDataKey::EncodeMaxKey(database_id
, object_store_id
),
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
),
1847 s
= DeleteRangeBasic(
1848 leveldb_transaction
,
1849 IndexMetaDataKey::Encode(database_id
, object_store_id
, 0, 0),
1850 IndexMetaDataKey::EncodeMaxKey(database_id
, object_store_id
),
1855 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1859 return ClearObjectStore(transaction
, database_id
, object_store_id
);
1862 leveldb::Status
IndexedDBBackingStore::GetRecord(
1863 IndexedDBBackingStore::Transaction
* transaction
,
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
);
1880 leveldb::Status s
= leveldb_transaction
->Get(leveldb_key
, &data
, &found
);
1882 INTERNAL_READ_ERROR(GET_RECORD
);
1888 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD
);
1889 return leveldb::Status::NotFound("Record contained no data");
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
,
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;
1915 GetInt(transaction
, last_version_key
, &last_version
, &found
);
1917 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER
);
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
;
1935 leveldb::Status
IndexedDBBackingStore::PutRecord(
1936 IndexedDBBackingStore::Transaction
* transaction
,
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();
1950 leveldb::Status s
= GetNewVersionNumber(
1951 leveldb_transaction
, database_id
, object_store_id
, &version
);
1954 DCHECK_GE(version
, 0);
1955 const std::string object_store_data_key
=
1956 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
1959 EncodeVarInt(version
, &v
);
1960 v
.append(value
->bits
);
1962 leveldb_transaction
->Put(object_store_data_key
, &v
);
1963 s
= transaction
->PutBlobInfoIfNeeded(database_id
,
1965 object_store_data_key
,
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
);
1984 leveldb::Status
IndexedDBBackingStore::ClearObjectStore(
1985 IndexedDBBackingStore::Transaction
* transaction
,
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();
1997 DeleteRangeBasic(transaction
->transaction(), start_key
, stop_key
, true);
1999 INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE
);
2002 return DeleteBlobsInObjectStore(transaction
, database_id
, object_store_id
);
2005 leveldb::Status
IndexedDBBackingStore::DeleteRecord(
2006 IndexedDBBackingStore::Transaction
* transaction
,
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
);
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
,
2032 int64 object_store_id
,
2033 const IndexedDBKeyRange
& key_range
) {
2035 scoped_ptr
<IndexedDBBackingStore::Cursor
> start_cursor
=
2036 OpenObjectStoreCursor(transaction
,
2040 blink::WebIDBCursorDirectionNext
,
2045 return leveldb::Status::OK(); // Empty range == delete success.
2047 scoped_ptr
<IndexedDBBackingStore::Cursor
> end_cursor
=
2048 OpenObjectStoreCursor(transaction
,
2052 blink::WebIDBCursorDirectionPrev
,
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
,
2076 start_blob_key
.Encode(),
2077 end_blob_key
.Encode(),
2081 s
= DeleteRangeBasic(transaction
->transaction(), start_key
, stop_key
, false);
2085 ExistsEntryKey::Encode(database_id
, object_store_id
, start_cursor
->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
,
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(
2105 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
2107 *key_generator_current_number
= -1;
2112 leveldb_transaction
->Get(key_generator_current_number_key
, &data
, &found
);
2114 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
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();
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;
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
;
2157 *key_generator_current_number
= max_numeric_key
+ 1;
2159 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2164 leveldb::Status
IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
2165 IndexedDBBackingStore::Transaction
* transaction
,
2167 int64 object_store_id
,
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
, ¤t_number
);
2179 if (new_number
<= current_number
)
2183 const std::string key_generator_current_number_key
=
2184 ObjectStoreMetaDataKey::Encode(
2187 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
2189 transaction
->transaction(), key_generator_current_number_key
, new_number
);
2190 return leveldb::Status::OK();
2193 leveldb::Status
IndexedDBBackingStore::KeyExistsInObjectStore(
2194 IndexedDBBackingStore::Transaction
* transaction
,
2196 int64 object_store_id
,
2197 const IndexedDBKey
& key
,
2198 RecordIdentifier
* found_record_identifier
,
2200 IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
2201 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2202 return InvalidDBKeyStatus();
2204 const std::string leveldb_key
=
2205 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
2209 transaction
->transaction()->Get(leveldb_key
, &data
, found
);
2211 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE
);
2215 return leveldb::Status::OK();
2217 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE
);
2218 return InternalInconsistencyStatus();
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
);
2232 class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
2233 : public IndexedDBBackingStore::Transaction::ChainedBlobWriter
{
2235 typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
2237 ChainedBlobWriterImpl(
2239 IndexedDBBackingStore
* backing_store
,
2240 WriteDescriptorVec
* blobs
,
2241 scoped_refptr
<IndexedDBBackingStore::BlobWriteCallback
> callback
)
2242 : waiting_for_callback_(false),
2243 database_id_(database_id
),
2244 backing_store_(backing_store
),
2245 callback_(callback
) {
2246 blobs_
.swap(*blobs
);
2247 iter_
= blobs_
.begin();
2248 backing_store
->task_runner()->PostTask(
2249 FROM_HERE
, base::Bind(&ChainedBlobWriterImpl::WriteNextFile
, this));
2252 void set_delegate(scoped_ptr
<FileWriterDelegate
> delegate
) override
{
2253 delegate_
.reset(delegate
.release());
2256 void ReportWriteCompletion(bool succeeded
, int64 bytes_written
) override
{
2257 DCHECK(waiting_for_callback_
);
2258 DCHECK(!succeeded
|| bytes_written
>= 0);
2259 waiting_for_callback_
= false;
2260 if (delegate_
.get()) // Only present for Blob, not File.
2261 content::BrowserThread::DeleteSoon(
2262 content::BrowserThread::IO
, FROM_HERE
, delegate_
.release());
2263 if (aborted_self_ref_
.get()) {
2264 aborted_self_ref_
= NULL
;
2267 if (iter_
->size() != -1 && iter_
->size() != bytes_written
)
2273 callback_
->Run(false);
2277 void Abort() override
{
2278 if (!waiting_for_callback_
)
2280 aborted_self_ref_
= this;
2284 ~ChainedBlobWriterImpl() override
{ DCHECK(!waiting_for_callback_
); }
2286 void WriteNextFile() {
2287 DCHECK(!waiting_for_callback_
);
2288 DCHECK(!aborted_self_ref_
.get());
2289 if (iter_
== blobs_
.end()) {
2290 callback_
->Run(true);
2293 waiting_for_callback_
= true;
2294 if (!backing_store_
->WriteBlobFile(database_id_
, *iter_
, this)) {
2295 waiting_for_callback_
= false;
2296 callback_
->Run(false);
2302 bool waiting_for_callback_
;
2303 scoped_refptr
<ChainedBlobWriterImpl
> aborted_self_ref_
;
2304 WriteDescriptorVec blobs_
;
2305 WriteDescriptorVec::const_iterator iter_
;
2307 IndexedDBBackingStore
* backing_store_
;
2308 scoped_refptr
<IndexedDBBackingStore::BlobWriteCallback
> callback_
;
2309 scoped_ptr
<FileWriterDelegate
> delegate_
;
2311 DISALLOW_COPY_AND_ASSIGN(ChainedBlobWriterImpl
);
2314 class LocalWriteClosure
: public FileWriterDelegate::DelegateWriteCallback
,
2315 public base::RefCountedThreadSafe
<LocalWriteClosure
> {
2317 LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter
*
2318 chained_blob_writer
,
2319 base::SequencedTaskRunner
* task_runner
)
2320 : chained_blob_writer_(chained_blob_writer
),
2321 task_runner_(task_runner
),
2322 bytes_written_(0) {}
2324 void Run(base::File::Error rv
,
2326 FileWriterDelegate::WriteProgressStatus write_status
) {
2327 DCHECK_GE(bytes
, 0);
2328 bytes_written_
+= bytes
;
2329 if (write_status
== FileWriterDelegate::SUCCESS_IO_PENDING
)
2330 return; // We don't care about progress events.
2331 if (rv
== base::File::FILE_OK
) {
2332 DCHECK_EQ(write_status
, FileWriterDelegate::SUCCESS_COMPLETED
);
2334 DCHECK(write_status
== FileWriterDelegate::ERROR_WRITE_STARTED
||
2335 write_status
== FileWriterDelegate::ERROR_WRITE_NOT_STARTED
);
2338 bool success
= write_status
== FileWriterDelegate::SUCCESS_COMPLETED
;
2339 if (success
&& !bytes_written_
) {
2340 // LocalFileStreamWriter only creates a file if data is actually written.
2341 // If none was then create one now.
2342 task_runner_
->PostTask(
2343 FROM_HERE
, base::Bind(&LocalWriteClosure::CreateEmptyFile
, this));
2344 } else if (success
&& !last_modified_
.is_null()) {
2345 task_runner_
->PostTask(
2346 FROM_HERE
, base::Bind(&LocalWriteClosure::UpdateTimeStamp
, this));
2348 task_runner_
->PostTask(
2350 base::Bind(&IndexedDBBackingStore::Transaction::ChainedBlobWriter::
2351 ReportWriteCompletion
,
2352 chained_blob_writer_
,
2358 void WriteBlobToFileOnIOThread(const FilePath
& file_path
,
2359 const GURL
& blob_url
,
2360 const base::Time
& last_modified
,
2361 net::URLRequestContext
* request_context
) {
2362 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
2363 scoped_ptr
<storage::FileStreamWriter
> writer(
2364 storage::FileStreamWriter::CreateForLocalFile(
2368 storage::FileStreamWriter::CREATE_NEW_FILE
));
2369 scoped_ptr
<FileWriterDelegate
> delegate(new FileWriterDelegate(
2370 writer
.Pass(), storage::FlushPolicy::FLUSH_ON_COMPLETION
));
2372 DCHECK(blob_url
.is_valid());
2373 scoped_ptr
<net::URLRequest
> blob_request(request_context
->CreateRequest(
2374 blob_url
, net::DEFAULT_PRIORITY
, delegate
.get()));
2376 this->file_path_
= file_path
;
2377 this->last_modified_
= last_modified
;
2379 delegate
->Start(blob_request
.Pass(),
2380 base::Bind(&LocalWriteClosure::Run
, this));
2381 chained_blob_writer_
->set_delegate(delegate
.Pass());
2385 virtual ~LocalWriteClosure() {
2386 // Make sure the last reference to a ChainedBlobWriter is released (and
2387 // deleted) on the IDB thread since it owns a transaction which has thread
2389 IndexedDBBackingStore::Transaction::ChainedBlobWriter
* raw_tmp
=
2390 chained_blob_writer_
.get();
2392 chained_blob_writer_
= NULL
;
2393 task_runner_
->ReleaseSoon(FROM_HERE
, raw_tmp
);
2395 friend class base::RefCountedThreadSafe
<LocalWriteClosure
>;
2397 // If necessary, update the timestamps on the file as a final
2398 // step before reporting success.
2399 void UpdateTimeStamp() {
2400 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
2401 if (!base::TouchFile(file_path_
, last_modified_
, last_modified_
)) {
2402 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2404 chained_blob_writer_
->ReportWriteCompletion(true, bytes_written_
);
2407 // Create an empty file.
2408 void CreateEmptyFile() {
2409 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
2410 base::File
file(file_path_
,
2411 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
2412 bool success
= file
.created();
2413 if (success
&& !last_modified_
.is_null() &&
2414 !file
.SetTimes(last_modified_
, last_modified_
)) {
2415 // TODO(cmumford): Complain quietly; timestamp's probably not vital.
2418 chained_blob_writer_
->ReportWriteCompletion(success
, bytes_written_
);
2421 scoped_refptr
<IndexedDBBackingStore::Transaction::ChainedBlobWriter
>
2422 chained_blob_writer_
;
2423 scoped_refptr
<base::SequencedTaskRunner
> task_runner_
;
2424 int64 bytes_written_
;
2426 base::FilePath file_path_
;
2427 base::Time last_modified_
;
2429 DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure
);
2432 bool IndexedDBBackingStore::WriteBlobFile(
2434 const Transaction::WriteDescriptor
& descriptor
,
2435 Transaction::ChainedBlobWriter
* chained_blob_writer
) {
2437 if (!MakeIDBBlobDirectory(blob_path_
, database_id
, descriptor
.key()))
2440 FilePath path
= GetBlobFileName(database_id
, descriptor
.key());
2442 if (descriptor
.is_file() && !descriptor
.file_path().empty()) {
2443 if (!base::CopyFile(descriptor
.file_path(), path
))
2446 base::File::Info info
;
2447 if (base::GetFileInfo(descriptor
.file_path(), &info
)) {
2448 if (descriptor
.size() != -1) {
2449 if (descriptor
.size() != info
.size
)
2451 // The round-trip can be lossy; round to nearest millisecond.
2452 int64 delta
= (descriptor
.last_modified() -
2453 info
.last_modified
).InMilliseconds();
2454 if (std::abs(delta
) > 1)
2457 if (!base::TouchFile(path
, info
.last_accessed
, info
.last_modified
)) {
2458 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2461 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2464 task_runner_
->PostTask(
2466 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion
,
2467 chained_blob_writer
,
2471 DCHECK(descriptor
.url().is_valid());
2472 scoped_refptr
<LocalWriteClosure
> write_closure(
2473 new LocalWriteClosure(chained_blob_writer
, task_runner_
.get()));
2474 content::BrowserThread::PostTask(
2475 content::BrowserThread::IO
,
2477 base::Bind(&LocalWriteClosure::WriteBlobToFileOnIOThread
,
2478 write_closure
.get(),
2481 descriptor
.last_modified(),
2487 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id
,
2489 DCHECK(KeyPrefix::IsValidDatabaseId(database_id
));
2490 bool all_blobs
= blob_key
== DatabaseMetaDataKey::kAllBlobsKey
;
2491 DCHECK(all_blobs
|| DatabaseMetaDataKey::IsValidBlobKey(blob_key
));
2492 scoped_refptr
<LevelDBTransaction
> transaction
=
2493 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
2495 BlobJournalType live_blob_journal
, primary_journal
;
2496 if (!GetLiveBlobJournal(transaction
.get(), &live_blob_journal
).ok())
2498 DCHECK(!live_blob_journal
.empty());
2499 if (!GetPrimaryBlobJournal(transaction
.get(), &primary_journal
).ok())
2502 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2503 // remove all entries with database_id from the live_blob journal and add only
2504 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2505 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2506 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2507 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2508 // matching (database_id, blob_key) tuple, we should move it to the primary
2510 BlobJournalType new_live_blob_journal
;
2511 for (BlobJournalType::iterator journal_iter
= live_blob_journal
.begin();
2512 journal_iter
!= live_blob_journal
.end();
2514 int64 current_database_id
= journal_iter
->first
;
2515 int64 current_blob_key
= journal_iter
->second
;
2516 bool current_all_blobs
=
2517 current_blob_key
== DatabaseMetaDataKey::kAllBlobsKey
;
2518 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id
) ||
2520 if (current_database_id
== database_id
&&
2521 (all_blobs
|| current_all_blobs
|| blob_key
== current_blob_key
)) {
2523 primary_journal
.push_back(
2524 std::make_pair(database_id
, current_blob_key
));
2525 if (current_all_blobs
)
2526 new_live_blob_journal
.push_back(*journal_iter
);
2527 new_live_blob_journal
.insert(new_live_blob_journal
.end(),
2529 live_blob_journal
.end()); // All the rest.
2533 new_live_blob_journal
.push_back(*journal_iter
);
2537 primary_journal
.push_back(
2538 std::make_pair(database_id
, DatabaseMetaDataKey::kAllBlobsKey
));
2540 UpdatePrimaryBlobJournal(transaction
.get(), primary_journal
);
2541 UpdateLiveBlobJournal(transaction
.get(), new_live_blob_journal
);
2542 transaction
->Commit();
2543 // We could just do the deletions/cleaning here, but if there are a lot of
2544 // blobs about to be garbage collected, it'd be better to wait and do them all
2546 StartJournalCleaningTimer();
2549 // The this reference is a raw pointer that's declared Unretained inside the
2550 // timer code, so this won't confuse IndexedDBFactory's check for
2551 // HasLastBackingStoreReference. It's safe because if the backing store is
2552 // deleted, the timer will automatically be canceled on destruction.
2553 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2554 journal_cleaning_timer_
.Start(
2556 base::TimeDelta::FromSeconds(5),
2558 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn
);
2561 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
2562 FilePath
IndexedDBBackingStore::GetBlobFileName(int64 database_id
,
2564 return GetBlobFileNameForKey(blob_path_
, database_id
, key
);
2567 static bool CheckIndexAndMetaDataKey(const LevelDBIterator
* it
,
2568 const std::string
& stop_key
,
2570 unsigned char meta_data_type
) {
2571 if (!it
->IsValid() || CompareKeys(it
->Key(), stop_key
) >= 0)
2574 StringPiece
slice(it
->Key());
2575 IndexMetaDataKey meta_data_key
;
2576 bool ok
= IndexMetaDataKey::Decode(&slice
, &meta_data_key
);
2578 if (meta_data_key
.IndexId() != index_id
)
2580 if (meta_data_key
.meta_data_type() != meta_data_type
)
2585 // TODO(jsbell): This should do some error handling rather than plowing ahead
2586 // when bad data is encountered.
2587 leveldb::Status
IndexedDBBackingStore::GetIndexes(
2589 int64 object_store_id
,
2590 IndexedDBObjectStoreMetadata::IndexMap
* indexes
) {
2591 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2592 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2593 return InvalidDBKeyStatus();
2594 const std::string start_key
=
2595 IndexMetaDataKey::Encode(database_id
, object_store_id
, 0, 0);
2596 const std::string stop_key
=
2597 IndexMetaDataKey::Encode(database_id
, object_store_id
+ 1, 0, 0);
2599 DCHECK(indexes
->empty());
2601 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
2602 leveldb::Status s
= it
->Seek(start_key
);
2603 while (s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0) {
2604 StringPiece
slice(it
->Key());
2605 IndexMetaDataKey meta_data_key
;
2606 bool ok
= IndexMetaDataKey::Decode(&slice
, &meta_data_key
);
2608 if (meta_data_key
.meta_data_type() != IndexMetaDataKey::NAME
) {
2609 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2610 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2618 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2620 int64 index_id
= meta_data_key
.IndexId();
2621 base::string16 index_name
;
2623 StringPiece
slice(it
->Value());
2624 if (!DecodeString(&slice
, &index_name
) || !slice
.empty())
2625 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2628 s
= it
->Next(); // unique flag
2631 if (!CheckIndexAndMetaDataKey(
2632 it
.get(), stop_key
, index_id
, IndexMetaDataKey::UNIQUE
)) {
2633 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2638 StringPiece
slice(it
->Value());
2639 if (!DecodeBool(&slice
, &index_unique
) || !slice
.empty())
2640 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2643 s
= it
->Next(); // key_path
2646 if (!CheckIndexAndMetaDataKey(
2647 it
.get(), stop_key
, index_id
, IndexMetaDataKey::KEY_PATH
)) {
2648 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2651 IndexedDBKeyPath key_path
;
2653 StringPiece
slice(it
->Value());
2654 if (!DecodeIDBKeyPath(&slice
, &key_path
) || !slice
.empty())
2655 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2658 s
= it
->Next(); // [optional] multi_entry flag
2661 bool index_multi_entry
= false;
2662 if (CheckIndexAndMetaDataKey(
2663 it
.get(), stop_key
, index_id
, IndexMetaDataKey::MULTI_ENTRY
)) {
2664 StringPiece
slice(it
->Value());
2665 if (!DecodeBool(&slice
, &index_multi_entry
) || !slice
.empty())
2666 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2673 (*indexes
)[index_id
] = IndexedDBIndexMetadata(
2674 index_name
, index_id
, key_path
, index_unique
, index_multi_entry
);
2678 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES
);
2683 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id
, int64 key
) const {
2684 FilePath path
= GetBlobFileName(database_id
, key
);
2685 return base::DeleteFile(path
, false);
2688 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id
) const {
2689 FilePath path
= GetBlobDirectoryName(blob_path_
, database_id
);
2690 return base::DeleteFile(path
, true);
2693 leveldb::Status
IndexedDBBackingStore::CleanUpBlobJournalEntries(
2694 const BlobJournalType
& journal
) const {
2695 if (journal
.empty())
2696 return leveldb::Status::OK();
2697 for (const auto& entry
: journal
) {
2698 int64 database_id
= entry
.first
;
2699 int64 blob_key
= entry
.second
;
2700 DCHECK(KeyPrefix::IsValidDatabaseId(database_id
));
2701 if (blob_key
== DatabaseMetaDataKey::kAllBlobsKey
) {
2702 if (!RemoveBlobDirectory(database_id
))
2703 return IOErrorStatus();
2705 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key
));
2706 if (!RemoveBlobFile(database_id
, blob_key
))
2707 return IOErrorStatus();
2710 return leveldb::Status::OK();
2713 leveldb::Status
IndexedDBBackingStore::CleanUpBlobJournal(
2714 const std::string
& level_db_key
) const {
2715 DCHECK(!committing_transaction_count_
);
2717 scoped_refptr
<LevelDBTransaction
> journal_transaction
=
2718 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
2719 BlobJournalType journal
;
2721 s
= GetBlobJournal(level_db_key
, journal_transaction
.get(), &journal
);
2724 if (journal
.empty())
2725 return leveldb::Status::OK();
2726 s
= CleanUpBlobJournalEntries(journal
);
2729 ClearBlobJournal(journal_transaction
.get(), level_db_key
);
2730 return journal_transaction
->Commit();
2733 leveldb::Status
IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
2735 const std::string
& object_store_data_key
,
2736 IndexedDBValue
* value
) {
2737 BlobChangeRecord
* change_record
= NULL
;
2738 BlobChangeMap::const_iterator blob_iter
=
2739 blob_change_map_
.find(object_store_data_key
);
2740 if (blob_iter
!= blob_change_map_
.end()) {
2741 change_record
= blob_iter
->second
;
2743 blob_iter
= incognito_blob_map_
.find(object_store_data_key
);
2744 if (blob_iter
!= incognito_blob_map_
.end())
2745 change_record
= blob_iter
->second
;
2747 if (change_record
) {
2748 // Either we haven't written the blob to disk yet or we're in incognito
2749 // mode, so we have to send back the one they sent us. This change record
2750 // includes the original UUID.
2751 value
->blob_info
= change_record
->blob_info();
2752 return leveldb::Status::OK();
2755 BlobEntryKey blob_entry_key
;
2756 StringPiece
leveldb_key_piece(object_store_data_key
);
2757 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece
,
2760 return InternalInconsistencyStatus();
2762 std::string encoded_key
= blob_entry_key
.Encode();
2764 std::string encoded_value
;
2765 leveldb::Status s
= transaction()->Get(encoded_key
, &encoded_value
, &found
);
2769 if (!DecodeBlobData(encoded_value
, &value
->blob_info
)) {
2770 INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD
);
2771 return InternalInconsistencyStatus();
2773 for (auto& entry
: value
->blob_info
) {
2774 entry
.set_file_path(
2775 backing_store_
->GetBlobFileName(database_id
, entry
.key()));
2776 entry
.set_mark_used_callback(
2777 backing_store_
->active_blob_registry()->GetAddBlobRefCallback(
2778 database_id
, entry
.key()));
2779 entry
.set_release_callback(
2780 backing_store_
->active_blob_registry()->GetFinalReleaseCallback(
2781 database_id
, entry
.key()));
2782 if (entry
.is_file() && !entry
.file_path().empty()) {
2783 base::File::Info info
;
2784 if (base::GetFileInfo(entry
.file_path(), &info
)) {
2785 // This should always work, but it isn't fatal if it doesn't; it just
2786 // means a potential slow synchronous call from the renderer later.
2787 entry
.set_last_modified(info
.last_modified
);
2788 entry
.set_size(info
.size
);
2793 return leveldb::Status::OK();
2796 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2797 // While a transaction is busy it is not safe to clean the journal.
2798 if (committing_transaction_count_
> 0)
2799 StartJournalCleaningTimer();
2801 CleanUpBlobJournal(BlobJournalKey::Encode());
2804 WARN_UNUSED_RESULT
static leveldb::Status
SetMaxIndexId(
2805 LevelDBTransaction
* transaction
,
2807 int64 object_store_id
,
2809 int64 max_index_id
= -1;
2810 const std::string max_index_id_key
= ObjectStoreMetaDataKey::Encode(
2811 database_id
, object_store_id
, ObjectStoreMetaDataKey::MAX_INDEX_ID
);
2814 GetInt(transaction
, max_index_id_key
, &max_index_id
, &found
);
2816 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID
);
2820 max_index_id
= kMinimumIndexId
;
2822 if (index_id
<= max_index_id
) {
2823 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID
);
2824 return InternalInconsistencyStatus();
2827 PutInt(transaction
, max_index_id_key
, index_id
);
2831 leveldb::Status
IndexedDBBackingStore::CreateIndex(
2832 IndexedDBBackingStore::Transaction
* transaction
,
2834 int64 object_store_id
,
2836 const base::string16
& name
,
2837 const IndexedDBKeyPath
& key_path
,
2839 bool is_multi_entry
) {
2840 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2841 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2842 return InvalidDBKeyStatus();
2843 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2844 leveldb::Status s
= SetMaxIndexId(
2845 leveldb_transaction
, database_id
, object_store_id
, index_id
);
2850 const std::string name_key
= IndexMetaDataKey::Encode(
2851 database_id
, object_store_id
, index_id
, IndexMetaDataKey::NAME
);
2852 const std::string unique_key
= IndexMetaDataKey::Encode(
2853 database_id
, object_store_id
, index_id
, IndexMetaDataKey::UNIQUE
);
2854 const std::string key_path_key
= IndexMetaDataKey::Encode(
2855 database_id
, object_store_id
, index_id
, IndexMetaDataKey::KEY_PATH
);
2856 const std::string multi_entry_key
= IndexMetaDataKey::Encode(
2857 database_id
, object_store_id
, index_id
, IndexMetaDataKey::MULTI_ENTRY
);
2859 PutString(leveldb_transaction
, name_key
, name
);
2860 PutBool(leveldb_transaction
, unique_key
, is_unique
);
2861 PutIDBKeyPath(leveldb_transaction
, key_path_key
, key_path
);
2862 PutBool(leveldb_transaction
, multi_entry_key
, is_multi_entry
);
2866 leveldb::Status
IndexedDBBackingStore::DeleteIndex(
2867 IndexedDBBackingStore::Transaction
* transaction
,
2869 int64 object_store_id
,
2871 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2872 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2873 return InvalidDBKeyStatus();
2874 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2876 const std::string index_meta_data_start
=
2877 IndexMetaDataKey::Encode(database_id
, object_store_id
, index_id
, 0);
2878 const std::string index_meta_data_end
=
2879 IndexMetaDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
2880 leveldb::Status s
= DeleteRangeBasic(
2881 leveldb_transaction
, index_meta_data_start
, index_meta_data_end
, true);
2884 const std::string index_data_start
=
2885 IndexDataKey::EncodeMinKey(database_id
, object_store_id
, index_id
);
2886 const std::string index_data_end
=
2887 IndexDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
2888 s
= DeleteRangeBasic(
2889 leveldb_transaction
, index_data_start
, index_data_end
, true);
2893 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX
);
2898 leveldb::Status
IndexedDBBackingStore::PutIndexDataForRecord(
2899 IndexedDBBackingStore::Transaction
* transaction
,
2901 int64 object_store_id
,
2903 const IndexedDBKey
& key
,
2904 const RecordIdentifier
& record_identifier
) {
2905 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2906 DCHECK(key
.IsValid());
2907 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2908 return InvalidDBKeyStatus();
2910 std::string encoded_key
;
2911 EncodeIDBKey(key
, &encoded_key
);
2913 const std::string index_data_key
=
2914 IndexDataKey::Encode(database_id
,
2918 record_identifier
.primary_key(),
2922 EncodeVarInt(record_identifier
.version(), &data
);
2923 data
.append(record_identifier
.primary_key());
2925 transaction
->transaction()->Put(index_data_key
, &data
);
2926 return leveldb::Status::OK();
2929 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction
* transaction
,
2930 const std::string
& target
,
2931 std::string
* found_key
,
2932 leveldb::Status
* s
) {
2933 scoped_ptr
<LevelDBIterator
> it
= transaction
->CreateIterator();
2934 *s
= it
->Seek(target
);
2938 if (!it
->IsValid()) {
2939 *s
= it
->SeekToLast();
2940 if (!s
->ok() || !it
->IsValid())
2944 while (CompareIndexKeys(it
->Key(), target
) > 0) {
2946 if (!s
->ok() || !it
->IsValid())
2951 *found_key
= it
->Key().as_string();
2953 // There can be several index keys that compare equal. We want the last one.
2955 } while (s
->ok() && it
->IsValid() && !CompareIndexKeys(it
->Key(), target
));
2960 static leveldb::Status
VersionExists(LevelDBTransaction
* transaction
,
2962 int64 object_store_id
,
2964 const std::string
& encoded_primary_key
,
2966 const std::string key
=
2967 ExistsEntryKey::Encode(database_id
, object_store_id
, encoded_primary_key
);
2970 leveldb::Status s
= transaction
->Get(key
, &data
, exists
);
2972 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS
);
2978 StringPiece
slice(data
);
2980 if (!DecodeInt(&slice
, &decoded
) || !slice
.empty())
2981 return InternalInconsistencyStatus();
2982 *exists
= (decoded
== version
);
2986 leveldb::Status
IndexedDBBackingStore::FindKeyInIndex(
2987 IndexedDBBackingStore::Transaction
* transaction
,
2989 int64 object_store_id
,
2991 const IndexedDBKey
& key
,
2992 std::string
* found_encoded_primary_key
,
2994 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
2995 DCHECK(KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
));
2997 DCHECK(found_encoded_primary_key
->empty());
3000 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3001 const std::string leveldb_key
=
3002 IndexDataKey::Encode(database_id
, object_store_id
, index_id
, key
);
3003 scoped_ptr
<LevelDBIterator
> it
= leveldb_transaction
->CreateIterator();
3004 leveldb::Status s
= it
->Seek(leveldb_key
);
3006 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX
);
3012 return leveldb::Status::OK();
3013 if (CompareIndexKeys(it
->Key(), leveldb_key
) > 0)
3014 return leveldb::Status::OK();
3016 StringPiece
slice(it
->Value());
3019 if (!DecodeVarInt(&slice
, &version
)) {
3020 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX
);
3021 return InternalInconsistencyStatus();
3023 *found_encoded_primary_key
= slice
.as_string();
3025 bool exists
= false;
3026 s
= VersionExists(leveldb_transaction
,
3030 *found_encoded_primary_key
,
3035 // Delete stale index data entry and continue.
3036 leveldb_transaction
->Remove(it
->Key());
3045 leveldb::Status
IndexedDBBackingStore::GetPrimaryKeyViaIndex(
3046 IndexedDBBackingStore::Transaction
* transaction
,
3048 int64 object_store_id
,
3050 const IndexedDBKey
& key
,
3051 scoped_ptr
<IndexedDBKey
>* primary_key
) {
3052 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
3053 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
3054 return InvalidDBKeyStatus();
3057 std::string found_encoded_primary_key
;
3058 leveldb::Status s
= FindKeyInIndex(transaction
,
3063 &found_encoded_primary_key
,
3066 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX
);
3071 if (!found_encoded_primary_key
.size()) {
3072 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX
);
3073 return InvalidDBKeyStatus();
3076 StringPiece
slice(found_encoded_primary_key
);
3077 if (DecodeIDBKey(&slice
, primary_key
) && slice
.empty())
3080 return InvalidDBKeyStatus();
3083 leveldb::Status
IndexedDBBackingStore::KeyExistsInIndex(
3084 IndexedDBBackingStore::Transaction
* transaction
,
3086 int64 object_store_id
,
3088 const IndexedDBKey
& index_key
,
3089 scoped_ptr
<IndexedDBKey
>* found_primary_key
,
3091 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
3092 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
3093 return InvalidDBKeyStatus();
3096 std::string found_encoded_primary_key
;
3097 leveldb::Status s
= FindKeyInIndex(transaction
,
3102 &found_encoded_primary_key
,
3105 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX
);
3109 return leveldb::Status::OK();
3110 if (found_encoded_primary_key
.empty()) {
3111 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX
);
3112 return InvalidDBKeyStatus();
3115 StringPiece
slice(found_encoded_primary_key
);
3116 if (DecodeIDBKey(&slice
, found_primary_key
) && slice
.empty())
3119 return InvalidDBKeyStatus();
3122 IndexedDBBackingStore::Cursor::Cursor(
3123 const IndexedDBBackingStore::Cursor
* other
)
3124 : backing_store_(other
->backing_store_
),
3125 transaction_(other
->transaction_
),
3126 database_id_(other
->database_id_
),
3127 cursor_options_(other
->cursor_options_
),
3128 current_key_(new IndexedDBKey(*other
->current_key_
)) {
3129 if (other
->iterator_
) {
3130 iterator_
= transaction_
->transaction()->CreateIterator();
3132 if (other
->iterator_
->IsValid()) {
3133 leveldb::Status s
= iterator_
->Seek(other
->iterator_
->Key());
3134 // TODO(cmumford): Handle this error (crbug.com/363397)
3135 DCHECK(iterator_
->IsValid());
3140 IndexedDBBackingStore::Cursor::Cursor(
3141 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3142 IndexedDBBackingStore::Transaction
* transaction
,
3144 const CursorOptions
& cursor_options
)
3145 : backing_store_(backing_store
.get()),
3146 transaction_(transaction
),
3147 database_id_(database_id
),
3148 cursor_options_(cursor_options
) {
3150 IndexedDBBackingStore::Cursor::~Cursor() {}
3152 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status
* s
) {
3153 iterator_
= transaction_
->transaction()->CreateIterator();
3154 if (cursor_options_
.forward
)
3155 *s
= iterator_
->Seek(cursor_options_
.low_key
);
3157 *s
= iterator_
->Seek(cursor_options_
.high_key
);
3161 return Continue(0, READY
, s
);
3164 bool IndexedDBBackingStore::Cursor::Advance(uint32 count
, leveldb::Status
* s
) {
3165 *s
= leveldb::Status::OK();
3173 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey
* key
,
3174 const IndexedDBKey
* primary_key
,
3175 IteratorState next_state
,
3176 leveldb::Status
* s
) {
3177 DCHECK(!key
|| key
->IsValid());
3178 DCHECK(!primary_key
|| primary_key
->IsValid());
3179 *s
= leveldb::Status::OK();
3181 // TODO(alecflett): avoid a copy here?
3182 IndexedDBKey previous_key
= current_key_
? *current_key_
: IndexedDBKey();
3184 // When iterating with PrevNoDuplicate, spec requires that the
3185 // value we yield for each key is the first duplicate in forwards
3187 IndexedDBKey last_duplicate_key
;
3189 bool forward
= cursor_options_
.forward
;
3190 bool first_iteration_forward
= forward
;
3191 bool flipped
= false;
3194 if (next_state
== SEEK
) {
3195 // TODO(jsbell): Optimize seeking for reverse cursors as well.
3196 if (first_iteration_forward
&& key
) {
3197 first_iteration_forward
= false;
3198 std::string leveldb_key
;
3200 leveldb_key
= EncodeKey(*key
, *primary_key
);
3202 leveldb_key
= EncodeKey(*key
);
3204 *s
= iterator_
->Seek(leveldb_key
);
3205 } else if (forward
) {
3206 *s
= iterator_
->Next();
3208 *s
= iterator_
->Prev();
3213 next_state
= SEEK
; // for subsequent iterations
3216 if (!iterator_
->IsValid()) {
3217 if (!forward
&& last_duplicate_key
.IsValid()) {
3218 // We need to walk forward because we hit the end of
3228 if (IsPastBounds()) {
3229 if (!forward
&& last_duplicate_key
.IsValid()) {
3230 // We need to walk forward because now we're beyond the
3231 // bounds defined by the cursor.
3240 if (!HaveEnteredRange())
3243 // The row may not load because there's a stale entry in the
3244 // index. This is not fatal.
3245 if (!LoadCurrentRow())
3250 if (primary_key
&& current_key_
->Equals(*key
) &&
3251 this->primary_key().IsLessThan(*primary_key
))
3253 if (!flipped
&& current_key_
->IsLessThan(*key
))
3256 if (primary_key
&& key
->Equals(*current_key_
) &&
3257 primary_key
->IsLessThan(this->primary_key()))
3259 if (key
->IsLessThan(*current_key_
))
3264 if (cursor_options_
.unique
) {
3265 if (previous_key
.IsValid() && current_key_
->Equals(previous_key
)) {
3266 // We should never be able to walk forward all the way
3267 // to the previous key.
3268 DCHECK(!last_duplicate_key
.IsValid());
3273 if (!last_duplicate_key
.IsValid()) {
3274 last_duplicate_key
= *current_key_
;
3278 // We need to walk forward because we hit the boundary
3279 // between key ranges.
3280 if (!last_duplicate_key
.Equals(*current_key_
)) {
3292 DCHECK(!last_duplicate_key
.IsValid() ||
3293 (forward
&& last_duplicate_key
.Equals(*current_key_
)));
3297 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3298 if (cursor_options_
.forward
) {
3299 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.low_key
);
3300 if (cursor_options_
.low_open
) {
3303 return compare
>= 0;
3305 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.high_key
);
3306 if (cursor_options_
.high_open
) {
3309 return compare
<= 0;
3312 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
3313 if (cursor_options_
.forward
) {
3314 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.high_key
);
3315 if (cursor_options_
.high_open
) {
3316 return compare
>= 0;
3320 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.low_key
);
3321 if (cursor_options_
.low_open
) {
3322 return compare
<= 0;
3327 const IndexedDBKey
& IndexedDBBackingStore::Cursor::primary_key() const {
3328 return *current_key_
;
3331 const IndexedDBBackingStore::RecordIdentifier
&
3332 IndexedDBBackingStore::Cursor::record_identifier() const {
3333 return record_identifier_
;
3336 class ObjectStoreKeyCursorImpl
: public IndexedDBBackingStore::Cursor
{
3338 ObjectStoreKeyCursorImpl(
3339 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3340 IndexedDBBackingStore::Transaction
* transaction
,
3342 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3343 : IndexedDBBackingStore::Cursor(backing_store
,
3348 Cursor
* Clone() override
{ return new ObjectStoreKeyCursorImpl(this); }
3350 // IndexedDBBackingStore::Cursor
3351 IndexedDBValue
* value() override
{
3355 bool LoadCurrentRow() override
;
3358 std::string
EncodeKey(const IndexedDBKey
& key
) override
{
3359 return ObjectStoreDataKey::Encode(
3360 cursor_options_
.database_id
, cursor_options_
.object_store_id
, key
);
3362 std::string
EncodeKey(const IndexedDBKey
& key
,
3363 const IndexedDBKey
& primary_key
) override
{
3365 return std::string();
3369 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl
* other
)
3370 : IndexedDBBackingStore::Cursor(other
) {}
3372 DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl
);
3375 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
3376 StringPiece
slice(iterator_
->Key());
3377 ObjectStoreDataKey object_store_data_key
;
3378 if (!ObjectStoreDataKey::Decode(&slice
, &object_store_data_key
)) {
3379 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3383 current_key_
= object_store_data_key
.user_key();
3386 slice
= StringPiece(iterator_
->Value());
3387 if (!DecodeVarInt(&slice
, &version
)) {
3388 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3392 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3393 std::string encoded_key
;
3394 EncodeIDBKey(*current_key_
, &encoded_key
);
3395 record_identifier_
.Reset(encoded_key
, version
);
3400 class ObjectStoreCursorImpl
: public IndexedDBBackingStore::Cursor
{
3402 ObjectStoreCursorImpl(
3403 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3404 IndexedDBBackingStore::Transaction
* transaction
,
3406 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3407 : IndexedDBBackingStore::Cursor(backing_store
,
3412 Cursor
* Clone() override
{ return new ObjectStoreCursorImpl(this); }
3414 // IndexedDBBackingStore::Cursor
3415 IndexedDBValue
* value() override
{ return ¤t_value_
; }
3416 bool LoadCurrentRow() override
;
3419 std::string
EncodeKey(const IndexedDBKey
& key
) override
{
3420 return ObjectStoreDataKey::Encode(
3421 cursor_options_
.database_id
, cursor_options_
.object_store_id
, key
);
3423 std::string
EncodeKey(const IndexedDBKey
& key
,
3424 const IndexedDBKey
& primary_key
) override
{
3426 return std::string();
3430 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl
* other
)
3431 : IndexedDBBackingStore::Cursor(other
),
3432 current_value_(other
->current_value_
) {}
3434 IndexedDBValue current_value_
;
3436 DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl
);
3439 bool ObjectStoreCursorImpl::LoadCurrentRow() {
3440 StringPiece
key_slice(iterator_
->Key());
3441 ObjectStoreDataKey object_store_data_key
;
3442 if (!ObjectStoreDataKey::Decode(&key_slice
, &object_store_data_key
)) {
3443 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3447 current_key_
= object_store_data_key
.user_key();
3450 StringPiece value_slice
= StringPiece(iterator_
->Value());
3451 if (!DecodeVarInt(&value_slice
, &version
)) {
3452 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3456 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3457 std::string encoded_key
;
3458 EncodeIDBKey(*current_key_
, &encoded_key
);
3459 record_identifier_
.Reset(encoded_key
, version
);
3461 if (!transaction_
->GetBlobInfoForRecord(database_id_
,
3462 iterator_
->Key().as_string(),
3463 ¤t_value_
).ok()) {
3466 current_value_
.bits
= value_slice
.as_string();
3470 class IndexKeyCursorImpl
: public IndexedDBBackingStore::Cursor
{
3473 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3474 IndexedDBBackingStore::Transaction
* transaction
,
3476 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3477 : IndexedDBBackingStore::Cursor(backing_store
,
3482 Cursor
* Clone() override
{ return new IndexKeyCursorImpl(this); }
3484 // IndexedDBBackingStore::Cursor
3485 IndexedDBValue
* value() override
{
3489 const IndexedDBKey
& primary_key() const override
{ return *primary_key_
; }
3490 const IndexedDBBackingStore::RecordIdentifier
& record_identifier()
3493 return record_identifier_
;
3495 bool LoadCurrentRow() override
;
3498 std::string
EncodeKey(const IndexedDBKey
& key
) override
{
3499 return IndexDataKey::Encode(cursor_options_
.database_id
,
3500 cursor_options_
.object_store_id
,
3501 cursor_options_
.index_id
,
3504 std::string
EncodeKey(const IndexedDBKey
& key
,
3505 const IndexedDBKey
& primary_key
) override
{
3506 return IndexDataKey::Encode(cursor_options_
.database_id
,
3507 cursor_options_
.object_store_id
,
3508 cursor_options_
.index_id
,
3514 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl
* other
)
3515 : IndexedDBBackingStore::Cursor(other
),
3516 primary_key_(new IndexedDBKey(*other
->primary_key_
)) {}
3518 scoped_ptr
<IndexedDBKey
> primary_key_
;
3520 DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl
);
3523 bool IndexKeyCursorImpl::LoadCurrentRow() {
3524 StringPiece
slice(iterator_
->Key());
3525 IndexDataKey index_data_key
;
3526 if (!IndexDataKey::Decode(&slice
, &index_data_key
)) {
3527 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3531 current_key_
= index_data_key
.user_key();
3532 DCHECK(current_key_
);
3534 slice
= StringPiece(iterator_
->Value());
3535 int64 index_data_version
;
3536 if (!DecodeVarInt(&slice
, &index_data_version
)) {
3537 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3541 if (!DecodeIDBKey(&slice
, &primary_key_
) || !slice
.empty()) {
3542 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3546 std::string primary_leveldb_key
=
3547 ObjectStoreDataKey::Encode(index_data_key
.DatabaseId(),
3548 index_data_key
.ObjectStoreId(),
3554 transaction_
->transaction()->Get(primary_leveldb_key
, &result
, &found
);
3556 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3560 transaction_
->transaction()->Remove(iterator_
->Key());
3563 if (!result
.size()) {
3564 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3568 int64 object_store_data_version
;
3569 slice
= StringPiece(result
);
3570 if (!DecodeVarInt(&slice
, &object_store_data_version
)) {
3571 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3575 if (object_store_data_version
!= index_data_version
) {
3576 transaction_
->transaction()->Remove(iterator_
->Key());
3583 class IndexCursorImpl
: public IndexedDBBackingStore::Cursor
{
3586 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3587 IndexedDBBackingStore::Transaction
* transaction
,
3589 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3590 : IndexedDBBackingStore::Cursor(backing_store
,
3595 Cursor
* Clone() override
{ return new IndexCursorImpl(this); }
3597 // IndexedDBBackingStore::Cursor
3598 IndexedDBValue
* value() override
{ return ¤t_value_
; }
3599 const IndexedDBKey
& primary_key() const override
{ return *primary_key_
; }
3600 const IndexedDBBackingStore::RecordIdentifier
& record_identifier()
3603 return record_identifier_
;
3605 bool LoadCurrentRow() override
;
3608 std::string
EncodeKey(const IndexedDBKey
& key
) override
{
3609 return IndexDataKey::Encode(cursor_options_
.database_id
,
3610 cursor_options_
.object_store_id
,
3611 cursor_options_
.index_id
,
3614 std::string
EncodeKey(const IndexedDBKey
& key
,
3615 const IndexedDBKey
& primary_key
) override
{
3616 return IndexDataKey::Encode(cursor_options_
.database_id
,
3617 cursor_options_
.object_store_id
,
3618 cursor_options_
.index_id
,
3624 explicit IndexCursorImpl(const IndexCursorImpl
* other
)
3625 : IndexedDBBackingStore::Cursor(other
),
3626 primary_key_(new IndexedDBKey(*other
->primary_key_
)),
3627 current_value_(other
->current_value_
),
3628 primary_leveldb_key_(other
->primary_leveldb_key_
) {}
3630 scoped_ptr
<IndexedDBKey
> primary_key_
;
3631 IndexedDBValue current_value_
;
3632 std::string primary_leveldb_key_
;
3634 DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl
);
3637 bool IndexCursorImpl::LoadCurrentRow() {
3638 StringPiece
slice(iterator_
->Key());
3639 IndexDataKey index_data_key
;
3640 if (!IndexDataKey::Decode(&slice
, &index_data_key
)) {
3641 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3645 current_key_
= index_data_key
.user_key();
3646 DCHECK(current_key_
);
3648 slice
= StringPiece(iterator_
->Value());
3649 int64 index_data_version
;
3650 if (!DecodeVarInt(&slice
, &index_data_version
)) {
3651 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3654 if (!DecodeIDBKey(&slice
, &primary_key_
)) {
3655 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3659 DCHECK_EQ(index_data_key
.DatabaseId(), database_id_
);
3660 primary_leveldb_key_
=
3661 ObjectStoreDataKey::Encode(index_data_key
.DatabaseId(),
3662 index_data_key
.ObjectStoreId(),
3668 transaction_
->transaction()->Get(primary_leveldb_key_
, &result
, &found
);
3670 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3674 transaction_
->transaction()->Remove(iterator_
->Key());
3677 if (!result
.size()) {
3678 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3682 int64 object_store_data_version
;
3683 slice
= StringPiece(result
);
3684 if (!DecodeVarInt(&slice
, &object_store_data_version
)) {
3685 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3689 if (object_store_data_version
!= index_data_version
) {
3690 transaction_
->transaction()->Remove(iterator_
->Key());
3694 current_value_
.bits
= slice
.as_string();
3695 return transaction_
->GetBlobInfoForRecord(database_id_
,
3696 primary_leveldb_key_
,
3697 ¤t_value_
).ok();
3700 bool ObjectStoreCursorOptions(
3701 LevelDBTransaction
* transaction
,
3703 int64 object_store_id
,
3704 const IndexedDBKeyRange
& range
,
3705 blink::WebIDBCursorDirection direction
,
3706 IndexedDBBackingStore::Cursor::CursorOptions
* cursor_options
) {
3707 cursor_options
->database_id
= database_id
;
3708 cursor_options
->object_store_id
= object_store_id
;
3710 bool lower_bound
= range
.lower().IsValid();
3711 bool upper_bound
= range
.upper().IsValid();
3712 cursor_options
->forward
=
3713 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3714 direction
== blink::WebIDBCursorDirectionNext
);
3715 cursor_options
->unique
=
3716 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3717 direction
== blink::WebIDBCursorDirectionPrevNoDuplicate
);
3720 cursor_options
->low_key
=
3721 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MinIDBKey());
3722 cursor_options
->low_open
= true; // Not included.
3724 cursor_options
->low_key
=
3725 ObjectStoreDataKey::Encode(database_id
, object_store_id
, range
.lower());
3726 cursor_options
->low_open
= range
.lower_open();
3732 cursor_options
->high_key
=
3733 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MaxIDBKey());
3735 if (cursor_options
->forward
) {
3736 cursor_options
->high_open
= true; // Not included.
3738 // We need a key that exists.
3739 // TODO(cmumford): Handle this error (crbug.com/363397)
3740 if (!FindGreatestKeyLessThanOrEqual(transaction
,
3741 cursor_options
->high_key
,
3742 &cursor_options
->high_key
,
3745 cursor_options
->high_open
= false;
3748 cursor_options
->high_key
=
3749 ObjectStoreDataKey::Encode(database_id
, object_store_id
, range
.upper());
3750 cursor_options
->high_open
= range
.upper_open();
3752 if (!cursor_options
->forward
) {
3753 // For reverse cursors, we need a key that exists.
3754 std::string found_high_key
;
3755 // TODO(cmumford): Handle this error (crbug.com/363397)
3756 if (!FindGreatestKeyLessThanOrEqual(
3757 transaction
, cursor_options
->high_key
, &found_high_key
, &s
))
3760 // If the target key should not be included, but we end up with a smaller
3761 // key, we should include that.
3762 if (cursor_options
->high_open
&&
3763 CompareIndexKeys(found_high_key
, cursor_options
->high_key
) < 0)
3764 cursor_options
->high_open
= false;
3766 cursor_options
->high_key
= found_high_key
;
3773 bool IndexCursorOptions(
3774 LevelDBTransaction
* transaction
,
3776 int64 object_store_id
,
3778 const IndexedDBKeyRange
& range
,
3779 blink::WebIDBCursorDirection direction
,
3780 IndexedDBBackingStore::Cursor::CursorOptions
* cursor_options
) {
3781 DCHECK(transaction
);
3782 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
3785 cursor_options
->database_id
= database_id
;
3786 cursor_options
->object_store_id
= object_store_id
;
3787 cursor_options
->index_id
= index_id
;
3789 bool lower_bound
= range
.lower().IsValid();
3790 bool upper_bound
= range
.upper().IsValid();
3791 cursor_options
->forward
=
3792 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3793 direction
== blink::WebIDBCursorDirectionNext
);
3794 cursor_options
->unique
=
3795 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3796 direction
== blink::WebIDBCursorDirectionPrevNoDuplicate
);
3799 cursor_options
->low_key
=
3800 IndexDataKey::EncodeMinKey(database_id
, object_store_id
, index_id
);
3801 cursor_options
->low_open
= false; // Included.
3803 cursor_options
->low_key
= IndexDataKey::Encode(
3804 database_id
, object_store_id
, index_id
, range
.lower());
3805 cursor_options
->low_open
= range
.lower_open();
3811 cursor_options
->high_key
=
3812 IndexDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
3813 cursor_options
->high_open
= false; // Included.
3815 if (!cursor_options
->forward
) { // We need a key that exists.
3816 if (!FindGreatestKeyLessThanOrEqual(transaction
,
3817 cursor_options
->high_key
,
3818 &cursor_options
->high_key
,
3821 cursor_options
->high_open
= false;
3824 cursor_options
->high_key
= IndexDataKey::Encode(
3825 database_id
, object_store_id
, index_id
, range
.upper());
3826 cursor_options
->high_open
= range
.upper_open();
3828 std::string found_high_key
;
3829 // Seek to the *last* key in the set of non-unique keys
3830 // TODO(cmumford): Handle this error (crbug.com/363397)
3831 if (!FindGreatestKeyLessThanOrEqual(
3832 transaction
, cursor_options
->high_key
, &found_high_key
, &s
))
3835 // If the target key should not be included, but we end up with a smaller
3836 // key, we should include that.
3837 if (cursor_options
->high_open
&&
3838 CompareIndexKeys(found_high_key
, cursor_options
->high_key
) < 0)
3839 cursor_options
->high_open
= false;
3841 cursor_options
->high_key
= found_high_key
;
3847 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3848 IndexedDBBackingStore::OpenObjectStoreCursor(
3849 IndexedDBBackingStore::Transaction
* transaction
,
3851 int64 object_store_id
,
3852 const IndexedDBKeyRange
& range
,
3853 blink::WebIDBCursorDirection direction
,
3854 leveldb::Status
* s
) {
3855 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3856 *s
= leveldb::Status::OK();
3857 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3858 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3859 if (!ObjectStoreCursorOptions(leveldb_transaction
,
3865 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3866 scoped_ptr
<ObjectStoreCursorImpl
> cursor(new ObjectStoreCursorImpl(
3867 this, transaction
, database_id
, cursor_options
));
3868 if (!cursor
->FirstSeek(s
))
3869 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3871 return cursor
.Pass();
3874 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3875 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3876 IndexedDBBackingStore::Transaction
* transaction
,
3878 int64 object_store_id
,
3879 const IndexedDBKeyRange
& range
,
3880 blink::WebIDBCursorDirection direction
,
3881 leveldb::Status
* s
) {
3882 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3883 *s
= leveldb::Status::OK();
3884 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3885 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3886 if (!ObjectStoreCursorOptions(leveldb_transaction
,
3892 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3893 scoped_ptr
<ObjectStoreKeyCursorImpl
> cursor(new ObjectStoreKeyCursorImpl(
3894 this, transaction
, database_id
, cursor_options
));
3895 if (!cursor
->FirstSeek(s
))
3896 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3898 return cursor
.Pass();
3901 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3902 IndexedDBBackingStore::OpenIndexKeyCursor(
3903 IndexedDBBackingStore::Transaction
* transaction
,
3905 int64 object_store_id
,
3907 const IndexedDBKeyRange
& range
,
3908 blink::WebIDBCursorDirection direction
,
3909 leveldb::Status
* s
) {
3910 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3911 *s
= leveldb::Status::OK();
3912 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3913 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3914 if (!IndexCursorOptions(leveldb_transaction
,
3921 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3922 scoped_ptr
<IndexKeyCursorImpl
> cursor(
3923 new IndexKeyCursorImpl(this, transaction
, database_id
, cursor_options
));
3924 if (!cursor
->FirstSeek(s
))
3925 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3927 return cursor
.Pass();
3930 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3931 IndexedDBBackingStore::OpenIndexCursor(
3932 IndexedDBBackingStore::Transaction
* transaction
,
3934 int64 object_store_id
,
3936 const IndexedDBKeyRange
& range
,
3937 blink::WebIDBCursorDirection direction
,
3938 leveldb::Status
* s
) {
3939 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
3940 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3941 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3942 if (!IndexCursorOptions(leveldb_transaction
,
3949 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3950 scoped_ptr
<IndexCursorImpl
> cursor(
3951 new IndexCursorImpl(this, transaction
, database_id
, cursor_options
));
3952 if (!cursor
->FirstSeek(s
))
3953 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3955 return cursor
.Pass();
3958 IndexedDBBackingStore::Transaction::Transaction(
3959 IndexedDBBackingStore
* backing_store
)
3960 : backing_store_(backing_store
), database_id_(-1), committing_(false) {
3963 IndexedDBBackingStore::Transaction::~Transaction() {
3964 STLDeleteContainerPairSecondPointers(
3965 blob_change_map_
.begin(), blob_change_map_
.end());
3966 STLDeleteContainerPairSecondPointers(incognito_blob_map_
.begin(),
3967 incognito_blob_map_
.end());
3968 DCHECK(!committing_
);
3971 void IndexedDBBackingStore::Transaction::Begin() {
3972 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
3973 DCHECK(!transaction_
.get());
3974 transaction_
= IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3975 backing_store_
->db_
.get());
3977 // If incognito, this snapshots blobs just as the above transaction_
3978 // constructor snapshots the leveldb.
3979 for (const auto& iter
: backing_store_
->incognito_blob_map_
)
3980 incognito_blob_map_
[iter
.first
] = iter
.second
->Clone().release();
3983 static GURL
GetURLFromUUID(const string
& uuid
) {
3984 return GURL("blob:uuid/" + uuid
);
3987 leveldb::Status
IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
3988 BlobEntryKeyValuePairVec
* new_blob_entries
,
3989 WriteDescriptorVec
* new_files_to_write
) {
3990 if (backing_store_
->is_incognito())
3991 return leveldb::Status::OK();
3993 DCHECK(new_blob_entries
->empty());
3994 DCHECK(new_files_to_write
->empty());
3995 DCHECK(blobs_to_write_
.empty());
3997 if (blob_change_map_
.empty())
3998 return leveldb::Status::OK();
4000 // Create LevelDBTransaction for the name generator seed and add-journal.
4001 scoped_refptr
<LevelDBTransaction
> pre_transaction
=
4002 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4003 backing_store_
->db_
.get());
4005 for (auto& iter
: blob_change_map_
) {
4006 std::vector
<IndexedDBBlobInfo
*> new_blob_keys
;
4007 for (auto& entry
: iter
.second
->mutable_blob_info()) {
4008 int64 next_blob_key
= -1;
4009 bool result
= GetBlobKeyGeneratorCurrentNumber(
4010 pre_transaction
.get(), database_id_
, &next_blob_key
);
4011 if (!result
|| next_blob_key
< 0)
4012 return InternalInconsistencyStatus();
4013 blobs_to_write_
.push_back(std::make_pair(database_id_
, next_blob_key
));
4014 if (entry
.is_file() && !entry
.file_path().empty()) {
4015 new_files_to_write
->push_back(
4016 WriteDescriptor(entry
.file_path(), next_blob_key
, entry
.size(),
4017 entry
.last_modified()));
4019 new_files_to_write
->push_back(
4020 WriteDescriptor(GetURLFromUUID(entry
.uuid()), next_blob_key
,
4021 entry
.size(), entry
.last_modified()));
4023 entry
.set_key(next_blob_key
);
4024 new_blob_keys
.push_back(&entry
);
4025 result
= UpdateBlobKeyGeneratorCurrentNumber(
4026 pre_transaction
.get(), database_id_
, next_blob_key
+ 1);
4028 return InternalInconsistencyStatus();
4030 BlobEntryKey blob_entry_key
;
4031 StringPiece
key_piece(iter
.second
->key());
4032 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece
, &blob_entry_key
)) {
4034 return InternalInconsistencyStatus();
4036 new_blob_entries
->push_back(
4037 std::make_pair(blob_entry_key
, EncodeBlobData(new_blob_keys
)));
4040 AppendBlobsToPrimaryBlobJournal(pre_transaction
.get(), blobs_to_write_
);
4041 leveldb::Status s
= pre_transaction
->Commit();
4043 return InternalInconsistencyStatus();
4045 return leveldb::Status::OK();
4048 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
4049 if (backing_store_
->is_incognito())
4052 // Look up all old files to remove as part of the transaction, store their
4053 // names in blobs_to_remove_, and remove their old blob data entries.
4054 for (const auto& iter
: blob_change_map_
) {
4055 BlobEntryKey blob_entry_key
;
4056 StringPiece
key_piece(iter
.second
->key());
4057 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece
, &blob_entry_key
)) {
4059 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4060 transaction_
= NULL
;
4063 if (database_id_
< 0)
4064 database_id_
= blob_entry_key
.database_id();
4066 DCHECK_EQ(database_id_
, blob_entry_key
.database_id());
4067 std::string blob_entry_key_bytes
= blob_entry_key
.Encode();
4069 std::string blob_entry_value_bytes
;
4070 leveldb::Status s
= transaction_
->Get(
4071 blob_entry_key_bytes
, &blob_entry_value_bytes
, &found
);
4072 if (s
.ok() && found
) {
4073 std::vector
<IndexedDBBlobInfo
> blob_info
;
4074 if (!DecodeBlobData(blob_entry_value_bytes
, &blob_info
)) {
4075 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4076 transaction_
= NULL
;
4079 for (const auto& blob
: blob_info
) {
4080 blobs_to_remove_
.push_back(std::make_pair(database_id_
, blob
.key()));
4081 transaction_
->Remove(blob_entry_key_bytes
);
4088 void IndexedDBBackingStore::Transaction::PartitionBlobsToRemove(
4089 BlobJournalType
* dead_blobs
,
4090 BlobJournalType
* live_blobs
) const {
4091 IndexedDBActiveBlobRegistry
* registry
=
4092 backing_store_
->active_blob_registry();
4093 for (const auto& iter
: blobs_to_remove_
) {
4094 if (registry
->MarkDeletedCheckIfUsed(iter
.first
, iter
.second
))
4095 live_blobs
->push_back(iter
);
4097 dead_blobs
->push_back(iter
);
4101 leveldb::Status
IndexedDBBackingStore::Transaction::CommitPhaseOne(
4102 scoped_refptr
<BlobWriteCallback
> callback
) {
4103 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
4104 DCHECK(transaction_
.get());
4105 DCHECK(backing_store_
->task_runner()->RunsTasksOnCurrentThread());
4109 BlobEntryKeyValuePairVec new_blob_entries
;
4110 WriteDescriptorVec new_files_to_write
;
4111 s
= HandleBlobPreTransaction(&new_blob_entries
, &new_files_to_write
);
4113 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4114 transaction_
= NULL
;
4118 DCHECK(!new_files_to_write
.size() ||
4119 KeyPrefix::IsValidDatabaseId(database_id_
));
4120 if (!CollectBlobFilesToRemove()) {
4121 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4122 transaction_
= NULL
;
4123 return InternalInconsistencyStatus();
4127 ++backing_store_
->committing_transaction_count_
;
4129 if (!new_files_to_write
.empty()) {
4130 // This kicks off the writes of the new blobs, if any.
4131 // This call will zero out new_blob_entries and new_files_to_write.
4132 WriteNewBlobs(&new_blob_entries
, &new_files_to_write
, callback
);
4134 callback
->Run(true);
4137 return leveldb::Status::OK();
4140 leveldb::Status
IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
4141 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
4144 DCHECK(committing_
);
4145 committing_
= false;
4146 DCHECK_GT(backing_store_
->committing_transaction_count_
, 0UL);
4147 --backing_store_
->committing_transaction_count_
;
4149 BlobJournalType primary_journal
, live_journal
, saved_primary_journal
,
4151 if (!blob_change_map_
.empty()) {
4152 // Read the persisted states of the primary/live blob journals,
4153 // so that they can be updated correctly by the transaction.
4154 scoped_refptr
<LevelDBTransaction
> journal_transaction
=
4155 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4156 backing_store_
->db_
.get());
4157 s
= GetPrimaryBlobJournal(journal_transaction
.get(), &primary_journal
);
4160 s
= GetLiveBlobJournal(journal_transaction
.get(), &live_journal
);
4164 // Remove newly added blobs from the journal - they will be accounted
4165 // for in blob entry tables in the transaction.
4166 std::sort(primary_journal
.begin(), primary_journal
.end());
4167 std::sort(blobs_to_write_
.begin(), blobs_to_write_
.end());
4168 BlobJournalType new_journal
= base::STLSetDifference
<BlobJournalType
>(
4169 primary_journal
, blobs_to_write_
);
4170 primary_journal
.swap(new_journal
);
4172 // Append newly deleted blobs to appropriate primary/live journals.
4173 saved_primary_journal
= primary_journal
;
4174 BlobJournalType live_blobs
;
4175 if (!blobs_to_remove_
.empty()) {
4176 DCHECK(!backing_store_
->is_incognito());
4177 PartitionBlobsToRemove(&dead_blobs
, &live_blobs
);
4179 primary_journal
.insert(primary_journal
.end(), dead_blobs
.begin(),
4181 live_journal
.insert(live_journal
.end(), live_blobs
.begin(),
4183 UpdatePrimaryBlobJournal(transaction_
.get(), primary_journal
);
4184 UpdateLiveBlobJournal(transaction_
.get(), live_journal
);
4187 // Actually commit. If this succeeds, the journals will appropriately
4188 // reflect pending blob work - dead files that should be deleted
4189 // immediately, and live files to monitor.
4190 s
= transaction_
->Commit();
4191 transaction_
= NULL
;
4194 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD
);
4198 if (backing_store_
->is_incognito()) {
4199 if (!blob_change_map_
.empty()) {
4200 BlobChangeMap
& target_map
= backing_store_
->incognito_blob_map_
;
4201 for (auto& iter
: blob_change_map_
) {
4202 BlobChangeMap::iterator target_record
= target_map
.find(iter
.first
);
4203 if (target_record
!= target_map
.end()) {
4204 delete target_record
->second
;
4205 target_map
.erase(target_record
);
4208 target_map
[iter
.first
] = iter
.second
;
4213 return leveldb::Status::OK();
4216 // Actually delete dead blob files, then remove those entries
4217 // from the persisted primary journal.
4218 if (dead_blobs
.empty())
4219 return leveldb::Status::OK();
4221 DCHECK(!blob_change_map_
.empty());
4223 s
= backing_store_
->CleanUpBlobJournalEntries(dead_blobs
);
4225 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4229 scoped_refptr
<LevelDBTransaction
> update_journal_transaction
=
4230 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4231 backing_store_
->db_
.get());
4232 UpdatePrimaryBlobJournal(update_journal_transaction
.get(),
4233 saved_primary_journal
);
4234 s
= update_journal_transaction
->Commit();
4239 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
4240 : public IndexedDBBackingStore::BlobWriteCallback
{
4242 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction
* transaction
,
4243 scoped_refptr
<BlobWriteCallback
> callback
)
4244 : transaction_(transaction
), callback_(callback
) {}
4245 void Run(bool succeeded
) override
{
4246 callback_
->Run(succeeded
);
4247 if (succeeded
) // Else it's already been deleted during rollback.
4248 transaction_
->chained_blob_writer_
= NULL
;
4252 ~BlobWriteCallbackWrapper() override
{}
4253 friend class base::RefCounted
<IndexedDBBackingStore::BlobWriteCallback
>;
4255 IndexedDBBackingStore::Transaction
* transaction_
;
4256 scoped_refptr
<BlobWriteCallback
> callback_
;
4258 DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper
);
4261 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
4262 BlobEntryKeyValuePairVec
* new_blob_entries
,
4263 WriteDescriptorVec
* new_files_to_write
,
4264 scoped_refptr
<BlobWriteCallback
> callback
) {
4265 DCHECK_GT(new_files_to_write
->size(), 0UL);
4266 DCHECK_GT(database_id_
, 0);
4267 for (auto& blob_entry_iter
: *new_blob_entries
) {
4268 // Add the new blob-table entry for each blob to the main transaction, or
4269 // remove any entry that may exist if there's no new one.
4270 if (blob_entry_iter
.second
.empty())
4271 transaction_
->Remove(blob_entry_iter
.first
.Encode());
4273 transaction_
->Put(blob_entry_iter
.first
.Encode(),
4274 &blob_entry_iter
.second
);
4276 // Creating the writer will start it going asynchronously.
4277 chained_blob_writer_
=
4278 new ChainedBlobWriterImpl(database_id_
,
4281 new BlobWriteCallbackWrapper(this, callback
));
4284 void IndexedDBBackingStore::Transaction::Rollback() {
4285 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
4287 committing_
= false;
4288 DCHECK_GT(backing_store_
->committing_transaction_count_
, 0UL);
4289 --backing_store_
->committing_transaction_count_
;
4292 if (chained_blob_writer_
.get()) {
4293 chained_blob_writer_
->Abort();
4294 chained_blob_writer_
= NULL
;
4296 if (transaction_
.get() == NULL
)
4298 transaction_
->Rollback();
4299 transaction_
= NULL
;
4302 IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
4303 const std::string
& key
,
4304 int64 object_store_id
)
4305 : key_(key
), object_store_id_(object_store_id
) {
4308 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
4311 void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
4312 std::vector
<IndexedDBBlobInfo
>* blob_info
) {
4315 blob_info_
.swap(*blob_info
);
4318 void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
4319 ScopedVector
<storage::BlobDataHandle
>* handles
) {
4322 handles_
.swap(*handles
);
4325 scoped_ptr
<IndexedDBBackingStore::BlobChangeRecord
>
4326 IndexedDBBackingStore::BlobChangeRecord::Clone() const {
4327 scoped_ptr
<IndexedDBBackingStore::BlobChangeRecord
> record(
4328 new BlobChangeRecord(key_
, object_store_id_
));
4329 record
->blob_info_
= blob_info_
;
4331 for (const auto* handle
: handles_
)
4332 record
->handles_
.push_back(new storage::BlobDataHandle(*handle
));
4333 return record
.Pass();
4336 leveldb::Status
IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
4338 int64 object_store_id
,
4339 const std::string
& object_store_data_key
,
4340 std::vector
<IndexedDBBlobInfo
>* blob_info
,
4341 ScopedVector
<storage::BlobDataHandle
>* handles
) {
4342 if (!blob_info
|| blob_info
->empty()) {
4343 blob_change_map_
.erase(object_store_data_key
);
4344 incognito_blob_map_
.erase(object_store_data_key
);
4346 BlobEntryKey blob_entry_key
;
4347 StringPiece
leveldb_key_piece(object_store_data_key
);
4348 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece
,
4351 return InternalInconsistencyStatus();
4356 transaction()->Get(blob_entry_key
.Encode(), &value
, &found
);
4360 return leveldb::Status::OK();
4363 database_id
, object_store_id
, object_store_data_key
, blob_info
, handles
);
4364 return leveldb::Status::OK();
4367 // This is storing an info, even if empty, even if the previous key had no blob
4368 // info that we know of. It duplicates a bunch of information stored in the
4369 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
4370 // changes to exists or index keys here.
4371 void IndexedDBBackingStore::Transaction::PutBlobInfo(
4373 int64 object_store_id
,
4374 const std::string
& object_store_data_key
,
4375 std::vector
<IndexedDBBlobInfo
>* blob_info
,
4376 ScopedVector
<storage::BlobDataHandle
>* handles
) {
4377 DCHECK_GT(object_store_data_key
.size(), 0UL);
4378 if (database_id_
< 0)
4379 database_id_
= database_id
;
4380 DCHECK_EQ(database_id_
, database_id
);
4382 BlobChangeMap::iterator it
= blob_change_map_
.find(object_store_data_key
);
4383 BlobChangeRecord
* record
= NULL
;
4384 if (it
== blob_change_map_
.end()) {
4385 record
= new BlobChangeRecord(object_store_data_key
, object_store_id
);
4386 blob_change_map_
[object_store_data_key
] = record
;
4388 record
= it
->second
;
4390 DCHECK_EQ(record
->object_store_id(), object_store_id
);
4391 record
->SetBlobInfo(blob_info
);
4392 record
->SetHandles(handles
);
4393 DCHECK(!handles
|| !handles
->size());
4396 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4400 base::Time last_modified
)
4405 last_modified_(last_modified
) {
4408 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4409 const FilePath
& file_path
,
4412 base::Time last_modified
)
4414 file_path_(file_path
),
4417 last_modified_(last_modified
) {
4420 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4421 const WriteDescriptor
& other
) = default;
4422 IndexedDBBackingStore::Transaction::WriteDescriptor::~WriteDescriptor() =
4424 IndexedDBBackingStore::Transaction::WriteDescriptor
&
4425 IndexedDBBackingStore::Transaction::WriteDescriptor::
4426 operator=(const WriteDescriptor
& other
) = default;
4428 } // namespace content