1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/indexed_db/indexed_db_backing_store.h"
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.h"
9 #include "base/format_macros.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "content/browser/child_process_security_policy_impl.h"
18 #include "content/browser/indexed_db/indexed_db_blob_info.h"
19 #include "content/browser/indexed_db/indexed_db_class_factory.h"
20 #include "content/browser/indexed_db/indexed_db_database_error.h"
21 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
22 #include "content/browser/indexed_db/indexed_db_metadata.h"
23 #include "content/browser/indexed_db/indexed_db_tracing.h"
24 #include "content/browser/indexed_db/indexed_db_value.h"
25 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
26 #include "content/browser/indexed_db/leveldb/leveldb_database.h"
27 #include "content/browser/indexed_db/leveldb/leveldb_factory.h"
28 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
29 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
30 #include "content/common/indexed_db/indexed_db_key.h"
31 #include "content/common/indexed_db/indexed_db_key_path.h"
32 #include "content/common/indexed_db/indexed_db_key_range.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "net/url_request/url_request_context.h"
35 #include "storage/browser/blob/blob_data_handle.h"
36 #include "storage/browser/fileapi/file_stream_writer.h"
37 #include "storage/browser/fileapi/file_writer_delegate.h"
38 #include "storage/browser/fileapi/local_file_stream_writer.h"
39 #include "storage/common/database/database_identifier.h"
40 #include "third_party/WebKit/public/platform/WebIDBTypes.h"
41 #include "third_party/WebKit/public/web/WebSerializedScriptValueVersion.h"
42 #include "third_party/leveldatabase/env_chromium.h"
45 using base::StringPiece
;
46 using storage::FileWriterDelegate
;
52 FilePath
GetBlobDirectoryName(const FilePath
& pathBase
, int64 database_id
) {
53 return pathBase
.AppendASCII(base::StringPrintf("%" PRIx64
, database_id
));
56 FilePath
GetBlobDirectoryNameForKey(const FilePath
& pathBase
,
59 FilePath path
= GetBlobDirectoryName(pathBase
, database_id
);
60 path
= path
.AppendASCII(base::StringPrintf(
61 "%02x", static_cast<int>(key
& 0x000000000000ff00) >> 8));
65 FilePath
GetBlobFileNameForKey(const FilePath
& pathBase
,
68 FilePath path
= GetBlobDirectoryNameForKey(pathBase
, database_id
, key
);
69 path
= path
.AppendASCII(base::StringPrintf("%" PRIx64
, key
));
73 bool MakeIDBBlobDirectory(const FilePath
& pathBase
,
76 FilePath path
= GetBlobDirectoryNameForKey(pathBase
, database_id
, key
);
77 return base::CreateDirectory(path
);
80 static std::string
ComputeOriginIdentifier(const GURL
& origin_url
) {
81 return storage::GetIdentifierFromOrigin(origin_url
) + "@1";
84 static base::FilePath
ComputeFileName(const GURL
& origin_url
) {
85 return base::FilePath()
86 .AppendASCII(storage::GetIdentifierFromOrigin(origin_url
))
87 .AddExtension(FILE_PATH_LITERAL(".indexeddb.leveldb"));
90 static base::FilePath
ComputeBlobPath(const GURL
& origin_url
) {
91 return base::FilePath()
92 .AppendASCII(storage::GetIdentifierFromOrigin(origin_url
))
93 .AddExtension(FILE_PATH_LITERAL(".indexeddb.blob"));
96 static base::FilePath
ComputeCorruptionFileName(const GURL
& origin_url
) {
97 return ComputeFileName(origin_url
)
98 .Append(FILE_PATH_LITERAL("corruption_info.json"));
103 static const int64 kKeyGeneratorInitialNumber
=
104 1; // From the IndexedDB specification.
106 enum IndexedDBBackingStoreErrorSource
{
107 // 0 - 2 are no longer used.
108 FIND_KEY_IN_INDEX
= 3,
109 GET_IDBDATABASE_METADATA
,
111 GET_KEY_GENERATOR_CURRENT_NUMBER
,
114 KEY_EXISTS_IN_OBJECT_STORE
,
117 GET_PRIMARY_KEY_VIA_INDEX
,
121 SET_MAX_OBJECT_STORE_ID
,
124 GET_NEW_VERSION_NUMBER
,
125 CREATE_IDBDATABASE_METADATA
,
127 TRANSACTION_COMMIT_METHOD
, // TRANSACTION_COMMIT is a WinNT.h macro
133 GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER
,
134 GET_BLOB_INFO_FOR_RECORD
,
138 static void RecordInternalError(const char* type
,
139 IndexedDBBackingStoreErrorSource location
) {
141 name
.append("WebCore.IndexedDB.BackingStore.").append(type
).append("Error");
142 base::Histogram::FactoryGet(name
,
145 INTERNAL_ERROR_MAX
+ 1,
146 base::HistogramBase::kUmaTargetedHistogramFlag
)
150 // Use to signal conditions caused by data corruption.
151 // A macro is used instead of an inline function so that the assert and log
152 // report the line number.
153 #define REPORT_ERROR(type, location) \
155 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
156 RecordInternalError(type, location); \
159 #define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location)
160 #define INTERNAL_CONSISTENCY_ERROR(location) \
161 REPORT_ERROR("Consistency", location)
162 #define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
164 // Use to signal conditions that usually indicate developer error, but
165 // could be caused by data corruption. A macro is used instead of an
166 // inline function so that the assert and log report the line number.
167 // TODO(cmumford): Improve test coverage so that all error conditions are
168 // "tested" and then delete this macro.
169 #define REPORT_ERROR_UNTESTED(type, location) \
171 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
173 RecordInternalError(type, location); \
176 #define INTERNAL_READ_ERROR_UNTESTED(location) \
177 REPORT_ERROR_UNTESTED("Read", location)
178 #define INTERNAL_CONSISTENCY_ERROR_UNTESTED(location) \
179 REPORT_ERROR_UNTESTED("Consistency", location)
180 #define INTERNAL_WRITE_ERROR_UNTESTED(location) \
181 REPORT_ERROR_UNTESTED("Write", location)
183 static void PutBool(LevelDBTransaction
* transaction
,
184 const StringPiece
& key
,
187 EncodeBool(value
, &buffer
);
188 transaction
->Put(key
, &buffer
);
191 // Was able to use LevelDB to read the data w/o error, but the data read was not
192 // in the expected format.
193 static leveldb::Status
InternalInconsistencyStatus() {
194 return leveldb::Status::Corruption("Internal inconsistency");
197 static leveldb::Status
InvalidDBKeyStatus() {
198 return leveldb::Status::InvalidArgument("Invalid database key ID");
201 static leveldb::Status
IOErrorStatus() {
202 return leveldb::Status::IOError("IO Error");
205 template <typename DBOrTransaction
>
206 static leveldb::Status
GetInt(DBOrTransaction
* db
,
207 const StringPiece
& key
,
211 leveldb::Status s
= db
->Get(key
, &result
, found
);
215 return leveldb::Status::OK();
216 StringPiece
slice(result
);
217 if (DecodeInt(&slice
, found_int
) && slice
.empty())
219 return InternalInconsistencyStatus();
222 static void PutInt(LevelDBTransaction
* transaction
,
223 const StringPiece
& key
,
227 EncodeInt(value
, &buffer
);
228 transaction
->Put(key
, &buffer
);
231 template <typename DBOrTransaction
>
232 WARN_UNUSED_RESULT
static leveldb::Status
GetVarInt(DBOrTransaction
* db
,
233 const StringPiece
& key
,
237 leveldb::Status s
= db
->Get(key
, &result
, found
);
241 return leveldb::Status::OK();
242 StringPiece
slice(result
);
243 if (DecodeVarInt(&slice
, found_int
) && slice
.empty())
245 return InternalInconsistencyStatus();
248 static void PutVarInt(LevelDBTransaction
* transaction
,
249 const StringPiece
& key
,
252 EncodeVarInt(value
, &buffer
);
253 transaction
->Put(key
, &buffer
);
256 template <typename DBOrTransaction
>
257 WARN_UNUSED_RESULT
static leveldb::Status
GetString(
259 const StringPiece
& key
,
260 base::string16
* found_string
,
264 leveldb::Status s
= db
->Get(key
, &result
, found
);
268 return leveldb::Status::OK();
269 StringPiece
slice(result
);
270 if (DecodeString(&slice
, found_string
) && slice
.empty())
272 return InternalInconsistencyStatus();
275 static void PutString(LevelDBTransaction
* transaction
,
276 const StringPiece
& key
,
277 const base::string16
& value
) {
279 EncodeString(value
, &buffer
);
280 transaction
->Put(key
, &buffer
);
283 static void PutIDBKeyPath(LevelDBTransaction
* transaction
,
284 const StringPiece
& key
,
285 const IndexedDBKeyPath
& value
) {
287 EncodeIDBKeyPath(value
, &buffer
);
288 transaction
->Put(key
, &buffer
);
291 static int CompareKeys(const StringPiece
& a
, const StringPiece
& b
) {
292 return Compare(a
, b
, false /*index_keys*/);
295 static int CompareIndexKeys(const StringPiece
& a
, const StringPiece
& b
) {
296 return Compare(a
, b
, true /*index_keys*/);
299 int IndexedDBBackingStore::Comparator::Compare(const StringPiece
& a
,
300 const StringPiece
& b
) const {
301 return content::Compare(a
, b
, false /*index_keys*/);
304 const char* IndexedDBBackingStore::Comparator::Name() const {
308 // 0 - Initial version.
309 // 1 - Adds UserIntVersion to DatabaseMetaData.
310 // 2 - Adds DataVersion to to global metadata.
311 // 3 - Adds metadata needed for blob support.
312 static const int64 kLatestKnownSchemaVersion
= 3;
313 WARN_UNUSED_RESULT
static bool IsSchemaKnown(LevelDBDatabase
* db
, bool* known
) {
314 int64 db_schema_version
= 0;
317 GetInt(db
, SchemaVersionKey::Encode(), &db_schema_version
, &found
);
324 if (db_schema_version
> kLatestKnownSchemaVersion
) {
329 const uint32 latest_known_data_version
=
330 blink::kSerializedScriptValueVersion
;
331 int64 db_data_version
= 0;
332 s
= GetInt(db
, DataVersionKey::Encode(), &db_data_version
, &found
);
340 if (db_data_version
> latest_known_data_version
) {
349 // TODO(ericu): Move this down into the member section of this file. I'm
350 // leaving it here for this CL as it's easier to see the diffs in place.
351 WARN_UNUSED_RESULT
leveldb::Status
IndexedDBBackingStore::SetUpMetadata() {
352 const uint32 latest_known_data_version
=
353 blink::kSerializedScriptValueVersion
;
354 const std::string schema_version_key
= SchemaVersionKey::Encode();
355 const std::string data_version_key
= DataVersionKey::Encode();
357 scoped_refptr
<LevelDBTransaction
> transaction
=
358 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
360 int64 db_schema_version
= 0;
361 int64 db_data_version
= 0;
364 GetInt(transaction
.get(), schema_version_key
, &db_schema_version
, &found
);
366 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
370 // Initialize new backing store.
371 db_schema_version
= kLatestKnownSchemaVersion
;
372 PutInt(transaction
.get(), schema_version_key
, db_schema_version
);
373 db_data_version
= latest_known_data_version
;
374 PutInt(transaction
.get(), data_version_key
, db_data_version
);
375 // If a blob directory already exists for this database, blow it away. It's
376 // leftover from a partially-purged previous generation of data.
377 if (!base::DeleteFile(blob_path_
, true)) {
378 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA
);
379 return IOErrorStatus();
382 // Upgrade old backing store.
383 DCHECK_LE(db_schema_version
, kLatestKnownSchemaVersion
);
384 if (db_schema_version
< 1) {
385 db_schema_version
= 1;
386 PutInt(transaction
.get(), schema_version_key
, db_schema_version
);
387 const std::string start_key
=
388 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_
);
389 const std::string stop_key
=
390 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_
);
391 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
392 for (s
= it
->Seek(start_key
);
393 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
395 int64 database_id
= 0;
397 s
= GetInt(transaction
.get(), it
->Key(), &database_id
, &found
);
399 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
403 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA
);
404 return InternalInconsistencyStatus();
406 std::string int_version_key
= DatabaseMetaDataKey::Encode(
407 database_id
, DatabaseMetaDataKey::USER_INT_VERSION
);
408 PutVarInt(transaction
.get(),
410 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
413 if (s
.ok() && db_schema_version
< 2) {
414 db_schema_version
= 2;
415 PutInt(transaction
.get(), schema_version_key
, db_schema_version
);
416 db_data_version
= blink::kSerializedScriptValueVersion
;
417 PutInt(transaction
.get(), data_version_key
, db_data_version
);
419 if (db_schema_version
< 3) {
420 db_schema_version
= 3;
421 if (!base::DeleteFile(blob_path_
, true)) {
422 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA
);
423 return IOErrorStatus();
429 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
433 // All new values will be written using this serialization version.
435 s
= GetInt(transaction
.get(), data_version_key
, &db_data_version
, &found
);
437 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
441 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA
);
442 return InternalInconsistencyStatus();
444 if (db_data_version
< latest_known_data_version
) {
445 db_data_version
= latest_known_data_version
;
446 PutInt(transaction
.get(), data_version_key
, db_data_version
);
449 DCHECK_EQ(db_schema_version
, kLatestKnownSchemaVersion
);
450 DCHECK_EQ(db_data_version
, latest_known_data_version
);
452 s
= transaction
->Commit();
454 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA
);
458 template <typename DBOrTransaction
>
459 WARN_UNUSED_RESULT
static leveldb::Status
GetMaxObjectStoreId(
462 int64
* max_object_store_id
) {
463 const std::string max_object_store_id_key
= DatabaseMetaDataKey::Encode(
464 database_id
, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID
);
465 return GetMaxObjectStoreId(db
, max_object_store_id_key
, max_object_store_id
);
468 template <typename DBOrTransaction
>
469 WARN_UNUSED_RESULT
static leveldb::Status
GetMaxObjectStoreId(
471 const std::string
& max_object_store_id_key
,
472 int64
* max_object_store_id
) {
473 *max_object_store_id
= -1;
476 GetInt(db
, max_object_store_id_key
, max_object_store_id
, &found
);
480 *max_object_store_id
= 0;
482 DCHECK_GE(*max_object_store_id
, 0);
486 class DefaultLevelDBFactory
: public LevelDBFactory
{
488 DefaultLevelDBFactory() {}
489 leveldb::Status
OpenLevelDB(const base::FilePath
& file_name
,
490 const LevelDBComparator
* comparator
,
491 scoped_ptr
<LevelDBDatabase
>* db
,
492 bool* is_disk_full
) override
{
493 return LevelDBDatabase::Open(file_name
, comparator
, db
, is_disk_full
);
495 leveldb::Status
DestroyLevelDB(const base::FilePath
& file_name
) override
{
496 return LevelDBDatabase::Destroy(file_name
);
500 DISALLOW_COPY_AND_ASSIGN(DefaultLevelDBFactory
);
503 static bool GetBlobKeyGeneratorCurrentNumber(
504 LevelDBTransaction
* leveldb_transaction
,
506 int64
* blob_key_generator_current_number
) {
507 const std::string key_gen_key
= DatabaseMetaDataKey::Encode(
508 database_id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
510 // Default to initial number if not found.
511 int64 cur_number
= DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber
;
515 bool ok
= leveldb_transaction
->Get(key_gen_key
, &data
, &found
).ok();
517 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
521 StringPiece
slice(data
);
522 if (!DecodeVarInt(&slice
, &cur_number
) || !slice
.empty() ||
523 !DatabaseMetaDataKey::IsValidBlobKey(cur_number
)) {
524 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
528 *blob_key_generator_current_number
= cur_number
;
532 static bool UpdateBlobKeyGeneratorCurrentNumber(
533 LevelDBTransaction
* leveldb_transaction
,
535 int64 blob_key_generator_current_number
) {
538 if (!GetBlobKeyGeneratorCurrentNumber(
539 leveldb_transaction
, database_id
, &old_number
))
541 DCHECK_LT(old_number
, blob_key_generator_current_number
);
544 DatabaseMetaDataKey::IsValidBlobKey(blob_key_generator_current_number
));
545 const std::string key
= DatabaseMetaDataKey::Encode(
546 database_id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
548 PutVarInt(leveldb_transaction
, key
, blob_key_generator_current_number
);
552 // TODO(ericu): Error recovery. If we persistently can't read the
553 // blob journal, the safe thing to do is to clear it and leak the blobs,
554 // though that may be costly. Still, database/directory deletion should always
555 // clean things up, and we can write an fsck that will do a full correction if
557 template <typename T
>
558 static leveldb::Status
GetBlobJournal(const StringPiece
& leveldb_key
,
559 T
* leveldb_transaction
,
560 BlobJournalType
* journal
) {
563 leveldb::Status s
= leveldb_transaction
->Get(leveldb_key
, &data
, &found
);
565 INTERNAL_READ_ERROR(READ_BLOB_JOURNAL
);
569 if (!found
|| !data
.size())
570 return leveldb::Status::OK();
571 StringPiece
slice(data
);
572 if (!DecodeBlobJournal(&slice
, journal
)) {
573 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DECODE_BLOB_JOURNAL
);
574 s
= InternalInconsistencyStatus();
579 static void ClearBlobJournal(LevelDBTransaction
* leveldb_transaction
,
580 const std::string
& level_db_key
) {
581 leveldb_transaction
->Remove(level_db_key
);
584 static void UpdatePrimaryJournalWithBlobList(
585 LevelDBTransaction
* leveldb_transaction
,
586 const BlobJournalType
& journal
) {
587 const std::string leveldb_key
= BlobJournalKey::Encode();
589 EncodeBlobJournal(journal
, &data
);
590 leveldb_transaction
->Put(leveldb_key
, &data
);
593 static void UpdateLiveBlobJournalWithBlobList(
594 LevelDBTransaction
* leveldb_transaction
,
595 const BlobJournalType
& journal
) {
596 const std::string leveldb_key
= LiveBlobJournalKey::Encode();
598 EncodeBlobJournal(journal
, &data
);
599 leveldb_transaction
->Put(leveldb_key
, &data
);
602 static leveldb::Status
MergeBlobsIntoLiveBlobJournal(
603 LevelDBTransaction
* leveldb_transaction
,
604 const BlobJournalType
& journal
) {
605 BlobJournalType old_journal
;
606 const std::string key
= LiveBlobJournalKey::Encode();
607 leveldb::Status s
= GetBlobJournal(key
, leveldb_transaction
, &old_journal
);
611 old_journal
.insert(old_journal
.end(), journal
.begin(), journal
.end());
613 UpdateLiveBlobJournalWithBlobList(leveldb_transaction
, old_journal
);
614 return leveldb::Status::OK();
617 static void UpdateBlobJournalWithDatabase(
618 LevelDBDirectTransaction
* leveldb_transaction
,
620 BlobJournalType journal
;
622 std::make_pair(database_id
, DatabaseMetaDataKey::kAllBlobsKey
));
623 const std::string key
= BlobJournalKey::Encode();
625 EncodeBlobJournal(journal
, &data
);
626 leveldb_transaction
->Put(key
, &data
);
629 static leveldb::Status
MergeDatabaseIntoLiveBlobJournal(
630 LevelDBDirectTransaction
* leveldb_transaction
,
632 BlobJournalType journal
;
633 const std::string key
= LiveBlobJournalKey::Encode();
634 leveldb::Status s
= GetBlobJournal(key
, leveldb_transaction
, &journal
);
638 std::make_pair(database_id
, DatabaseMetaDataKey::kAllBlobsKey
));
640 EncodeBlobJournal(journal
, &data
);
641 leveldb_transaction
->Put(key
, &data
);
642 return leveldb::Status::OK();
645 // Blob Data is encoded as a series of:
646 // { is_file [bool], key [int64 as varInt],
647 // type [string-with-length, may be empty],
648 // (for Blobs only) size [int64 as varInt]
649 // (for Files only) fileName [string-with-length]
651 // There is no length field; just read until you run out of data.
652 static std::string
EncodeBlobData(
653 const std::vector
<IndexedDBBlobInfo
*>& blob_info
) {
655 for (const auto* info
: blob_info
) {
656 EncodeBool(info
->is_file(), &ret
);
657 EncodeVarInt(info
->key(), &ret
);
658 EncodeStringWithLength(info
->type(), &ret
);
660 EncodeStringWithLength(info
->file_name(), &ret
);
662 EncodeVarInt(info
->size(), &ret
);
667 static bool DecodeBlobData(const std::string
& data
,
668 std::vector
<IndexedDBBlobInfo
>* output
) {
669 std::vector
<IndexedDBBlobInfo
> ret
;
671 StringPiece
slice(data
);
672 while (!slice
.empty()) {
677 base::string16 file_name
;
679 if (!DecodeBool(&slice
, &is_file
))
681 if (!DecodeVarInt(&slice
, &key
) ||
682 !DatabaseMetaDataKey::IsValidBlobKey(key
))
684 if (!DecodeStringWithLength(&slice
, &type
))
687 if (!DecodeStringWithLength(&slice
, &file_name
))
689 ret
.push_back(IndexedDBBlobInfo(key
, type
, file_name
));
691 if (!DecodeVarInt(&slice
, &size
) || size
< 0)
693 ret
.push_back(IndexedDBBlobInfo(type
, static_cast<uint64
>(size
), key
));
701 IndexedDBBackingStore::IndexedDBBackingStore(
702 IndexedDBFactory
* indexed_db_factory
,
703 const GURL
& origin_url
,
704 const base::FilePath
& blob_path
,
705 net::URLRequestContext
* request_context
,
706 scoped_ptr
<LevelDBDatabase
> db
,
707 scoped_ptr
<LevelDBComparator
> comparator
,
708 base::SequencedTaskRunner
* task_runner
)
709 : indexed_db_factory_(indexed_db_factory
),
710 origin_url_(origin_url
),
711 blob_path_(blob_path
),
712 origin_identifier_(ComputeOriginIdentifier(origin_url
)),
713 request_context_(request_context
),
714 task_runner_(task_runner
),
716 comparator_(comparator
.Pass()),
717 active_blob_registry_(this) {
720 IndexedDBBackingStore::~IndexedDBBackingStore() {
721 if (!blob_path_
.empty() && !child_process_ids_granted_
.empty()) {
722 ChildProcessSecurityPolicyImpl
* policy
=
723 ChildProcessSecurityPolicyImpl::GetInstance();
724 for (const auto& pid
: child_process_ids_granted_
)
725 policy
->RevokeAllPermissionsForFile(pid
, blob_path_
);
727 STLDeleteContainerPairSecondPointers(incognito_blob_map_
.begin(),
728 incognito_blob_map_
.end());
729 // db_'s destructor uses comparator_. The order of destruction is important.
734 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
735 const std::string
& primary_key
,
737 : primary_key_(primary_key
), version_(version
) {
738 DCHECK(!primary_key
.empty());
740 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
741 : primary_key_(), version_(-1) {}
742 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
744 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
745 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
747 enum IndexedDBBackingStoreOpenResult
{
748 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS
,
749 INDEXED_DB_BACKING_STORE_OPEN_SUCCESS
,
750 INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY
,
751 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA
,
752 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED
,
753 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED
,
754 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS
,
755 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA
,
756 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR
,
757 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED
,
758 INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII
,
759 INDEXED_DB_BACKING_STORE_OPEN_DISK_FULL_DEPRECATED
,
760 INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG
,
761 INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY
,
762 INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION
,
763 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR
,
764 INDEXED_DB_BACKING_STORE_OPEN_MAX
,
768 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::Open(
769 IndexedDBFactory
* indexed_db_factory
,
770 const GURL
& origin_url
,
771 const base::FilePath
& path_base
,
772 net::URLRequestContext
* request_context
,
773 blink::WebIDBDataLoss
* data_loss
,
774 std::string
* data_loss_message
,
776 base::SequencedTaskRunner
* task_runner
,
778 leveldb::Status
* status
) {
779 *data_loss
= blink::WebIDBDataLossNone
;
780 DefaultLevelDBFactory leveldb_factory
;
781 return IndexedDBBackingStore::Open(indexed_db_factory
,
794 static std::string
OriginToCustomHistogramSuffix(const GURL
& origin_url
) {
795 if (origin_url
.host() == "docs.google.com")
797 return std::string();
800 static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result
,
801 const GURL
& origin_url
) {
802 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.BackingStore.OpenStatus",
804 INDEXED_DB_BACKING_STORE_OPEN_MAX
);
805 const std::string suffix
= OriginToCustomHistogramSuffix(origin_url
);
806 // Data from the WebCore.IndexedDB.BackingStore.OpenStatus histogram is used
807 // to generate a graph. So as not to alter the meaning of that graph,
808 // continue to collect all stats there (above) but also now collect docs stats
809 // separately (below).
810 if (!suffix
.empty()) {
811 base::LinearHistogram::FactoryGet(
812 "WebCore.IndexedDB.BackingStore.OpenStatus" + suffix
,
814 INDEXED_DB_BACKING_STORE_OPEN_MAX
,
815 INDEXED_DB_BACKING_STORE_OPEN_MAX
+ 1,
816 base::HistogramBase::kUmaTargetedHistogramFlag
)->Add(result
);
820 static bool IsPathTooLong(const base::FilePath
& leveldb_dir
) {
821 int limit
= base::GetMaximumPathComponentLength(leveldb_dir
.DirName());
823 DLOG(WARNING
) << "GetMaximumPathComponentLength returned -1";
824 // In limited testing, ChromeOS returns 143, other OSes 255.
825 #if defined(OS_CHROMEOS)
831 size_t component_length
= leveldb_dir
.BaseName().value().length();
832 if (component_length
> static_cast<uint32_t>(limit
)) {
833 DLOG(WARNING
) << "Path component length (" << component_length
834 << ") exceeds maximum (" << limit
835 << ") allowed by this filesystem.";
838 const int num_buckets
= 12;
839 UMA_HISTOGRAM_CUSTOM_COUNTS(
840 "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
850 leveldb::Status
IndexedDBBackingStore::DestroyBackingStore(
851 const base::FilePath
& path_base
,
852 const GURL
& origin_url
) {
853 const base::FilePath file_path
=
854 path_base
.Append(ComputeFileName(origin_url
));
855 DefaultLevelDBFactory leveldb_factory
;
856 return leveldb_factory
.DestroyLevelDB(file_path
);
859 bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath
& path_base
,
860 const GURL
& origin_url
,
861 std::string
* message
) {
862 const base::FilePath info_path
=
863 path_base
.Append(ComputeCorruptionFileName(origin_url
));
865 if (IsPathTooLong(info_path
))
868 const int64 max_json_len
= 4096;
870 if (!GetFileSize(info_path
, &file_size
) || file_size
> max_json_len
)
877 base::File
file(info_path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
878 bool success
= false;
879 if (file
.IsValid()) {
880 std::vector
<char> bytes(file_size
);
881 if (file_size
== file
.Read(0, &bytes
[0], file_size
)) {
882 std::string
input_js(&bytes
[0], file_size
);
883 base::JSONReader reader
;
884 scoped_ptr
<base::Value
> val(reader
.ReadToValue(input_js
));
885 if (val
&& val
->GetType() == base::Value::TYPE_DICTIONARY
) {
886 base::DictionaryValue
* dict_val
=
887 static_cast<base::DictionaryValue
*>(val
.get());
888 success
= dict_val
->GetString("message", message
);
894 base::DeleteFile(info_path
, false);
899 bool IndexedDBBackingStore::RecordCorruptionInfo(
900 const base::FilePath
& path_base
,
901 const GURL
& origin_url
,
902 const std::string
& message
) {
903 const base::FilePath info_path
=
904 path_base
.Append(ComputeCorruptionFileName(origin_url
));
905 if (IsPathTooLong(info_path
))
908 base::DictionaryValue root_dict
;
909 root_dict
.SetString("message", message
);
910 std::string output_js
;
911 base::JSONWriter::Write(&root_dict
, &output_js
);
913 base::File
file(info_path
,
914 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
917 int written
= file
.Write(0, output_js
.c_str(), output_js
.length());
918 return size_t(written
) == output_js
.length();
922 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::Open(
923 IndexedDBFactory
* indexed_db_factory
,
924 const GURL
& origin_url
,
925 const base::FilePath
& path_base
,
926 net::URLRequestContext
* request_context
,
927 blink::WebIDBDataLoss
* data_loss
,
928 std::string
* data_loss_message
,
930 LevelDBFactory
* leveldb_factory
,
931 base::SequencedTaskRunner
* task_runner
,
933 leveldb::Status
* status
) {
934 IDB_TRACE("IndexedDBBackingStore::Open");
935 DCHECK(!path_base
.empty());
936 *data_loss
= blink::WebIDBDataLossNone
;
937 *data_loss_message
= "";
938 *is_disk_full
= false;
940 *status
= leveldb::Status::OK();
942 scoped_ptr
<LevelDBComparator
> comparator(new Comparator());
944 if (!base::IsStringASCII(path_base
.AsUTF8Unsafe())) {
945 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII
,
948 if (!base::CreateDirectory(path_base
)) {
950 leveldb::Status::IOError("Unable to create IndexedDB database path");
951 LOG(ERROR
) << status
->ToString() << ": \"" << path_base
.AsUTF8Unsafe()
953 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY
,
955 return scoped_refptr
<IndexedDBBackingStore
>();
958 const base::FilePath file_path
=
959 path_base
.Append(ComputeFileName(origin_url
));
960 const base::FilePath blob_path
=
961 path_base
.Append(ComputeBlobPath(origin_url
));
963 if (IsPathTooLong(file_path
)) {
964 *status
= leveldb::Status::IOError("File path too long");
965 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG
,
967 return scoped_refptr
<IndexedDBBackingStore
>();
970 scoped_ptr
<LevelDBDatabase
> db
;
971 *status
= leveldb_factory
->OpenLevelDB(
972 file_path
, comparator
.get(), &db
, is_disk_full
);
974 DCHECK(!db
== !status
->ok());
976 if (leveldb_env::IndicatesDiskFull(*status
)) {
977 *is_disk_full
= true;
978 } else if (status
->IsCorruption()) {
979 *data_loss
= blink::WebIDBDataLossTotal
;
980 *data_loss_message
= leveldb_env::GetCorruptionMessage(*status
);
984 bool is_schema_known
= false;
986 std::string corruption_message
;
987 if (ReadCorruptionInfo(path_base
, origin_url
, &corruption_message
)) {
988 LOG(ERROR
) << "IndexedDB recovering from a corrupted (and deleted) "
990 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION
,
993 *data_loss
= blink::WebIDBDataLossTotal
;
995 "IndexedDB (database was corrupt): " + corruption_message
;
996 } else if (!IsSchemaKnown(db
.get(), &is_schema_known
)) {
997 LOG(ERROR
) << "IndexedDB had IO error checking schema, treating it as "
1000 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA
,
1003 *data_loss
= blink::WebIDBDataLossTotal
;
1004 *data_loss_message
= "I/O error checking schema";
1005 } else if (!is_schema_known
) {
1006 LOG(ERROR
) << "IndexedDB backing store had unknown schema, treating it "
1007 "as failure to open";
1008 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA
,
1011 *data_loss
= blink::WebIDBDataLossTotal
;
1012 *data_loss_message
= "Unknown schema";
1016 DCHECK(status
->ok() || !is_schema_known
|| leveldb_env::IsIOError(*status
) ||
1017 status
->IsCorruption());
1020 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_SUCCESS
, origin_url
);
1021 } else if (leveldb_env::IsIOError(*status
)) {
1022 LOG(ERROR
) << "Unable to open backing store, not trying to recover - "
1023 << status
->ToString();
1024 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY
, origin_url
);
1025 return scoped_refptr
<IndexedDBBackingStore
>();
1027 DCHECK(!is_schema_known
|| status
->IsCorruption());
1028 LOG(ERROR
) << "IndexedDB backing store open failed, attempting cleanup";
1029 *status
= leveldb_factory
->DestroyLevelDB(file_path
);
1030 if (!status
->ok()) {
1031 LOG(ERROR
) << "IndexedDB backing store cleanup failed";
1032 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED
,
1034 return scoped_refptr
<IndexedDBBackingStore
>();
1037 LOG(ERROR
) << "IndexedDB backing store cleanup succeeded, reopening";
1038 leveldb_factory
->OpenLevelDB(file_path
, comparator
.get(), &db
, NULL
);
1040 LOG(ERROR
) << "IndexedDB backing store reopen after recovery failed";
1041 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED
,
1043 return scoped_refptr
<IndexedDBBackingStore
>();
1045 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS
,
1051 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR
,
1053 return scoped_refptr
<IndexedDBBackingStore
>();
1056 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
1057 Create(indexed_db_factory
,
1066 if (clean_journal
&& backing_store
.get() &&
1067 !backing_store
->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
1068 HistogramOpenStatus(
1069 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR
, origin_url
);
1070 return scoped_refptr
<IndexedDBBackingStore
>();
1072 return backing_store
;
1076 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::OpenInMemory(
1077 const GURL
& origin_url
,
1078 base::SequencedTaskRunner
* task_runner
,
1079 leveldb::Status
* status
) {
1080 DefaultLevelDBFactory leveldb_factory
;
1081 return IndexedDBBackingStore::OpenInMemory(
1082 origin_url
, &leveldb_factory
, task_runner
, status
);
1086 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::OpenInMemory(
1087 const GURL
& origin_url
,
1088 LevelDBFactory
* leveldb_factory
,
1089 base::SequencedTaskRunner
* task_runner
,
1090 leveldb::Status
* status
) {
1091 IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
1093 scoped_ptr
<LevelDBComparator
> comparator(new Comparator());
1094 scoped_ptr
<LevelDBDatabase
> db
=
1095 LevelDBDatabase::OpenInMemory(comparator
.get());
1097 LOG(ERROR
) << "LevelDBDatabase::OpenInMemory failed.";
1098 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED
,
1100 return scoped_refptr
<IndexedDBBackingStore
>();
1102 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS
, origin_url
);
1104 return Create(NULL
/* indexed_db_factory */,
1107 NULL
/* request_context */,
1115 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::Create(
1116 IndexedDBFactory
* indexed_db_factory
,
1117 const GURL
& origin_url
,
1118 const base::FilePath
& blob_path
,
1119 net::URLRequestContext
* request_context
,
1120 scoped_ptr
<LevelDBDatabase
> db
,
1121 scoped_ptr
<LevelDBComparator
> comparator
,
1122 base::SequencedTaskRunner
* task_runner
,
1123 leveldb::Status
* status
) {
1124 // TODO(jsbell): Handle comparator name changes.
1125 scoped_refptr
<IndexedDBBackingStore
> backing_store(
1126 new IndexedDBBackingStore(indexed_db_factory
,
1133 *status
= backing_store
->SetUpMetadata();
1135 return scoped_refptr
<IndexedDBBackingStore
>();
1137 return backing_store
;
1140 void IndexedDBBackingStore::GrantChildProcessPermissions(int child_process_id
) {
1141 if (!child_process_ids_granted_
.count(child_process_id
)) {
1142 child_process_ids_granted_
.insert(child_process_id
);
1143 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
1144 child_process_id
, blob_path_
);
1148 std::vector
<base::string16
> IndexedDBBackingStore::GetDatabaseNames(
1149 leveldb::Status
* s
) {
1150 *s
= leveldb::Status::OK();
1151 std::vector
<base::string16
> found_names
;
1152 const std::string start_key
=
1153 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_
);
1154 const std::string stop_key
=
1155 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_
);
1157 DCHECK(found_names
.empty());
1159 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
1160 for (*s
= it
->Seek(start_key
);
1161 s
->ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
1163 // Decode database name (in iterator key).
1164 StringPiece
slice(it
->Key());
1165 DatabaseNameKey database_name_key
;
1166 if (!DatabaseNameKey::Decode(&slice
, &database_name_key
) ||
1168 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES
);
1172 // Decode database id (in iterator value).
1173 int64 database_id
= 0;
1174 StringPiece
valueSlice(it
->Value());
1175 if (!DecodeInt(&valueSlice
, &database_id
) || !valueSlice
.empty()) {
1176 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES
);
1180 // Look up version by id.
1182 int64 database_version
= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
;
1183 *s
= GetVarInt(db_
.get(),
1184 DatabaseMetaDataKey::Encode(
1185 database_id
, DatabaseMetaDataKey::USER_INT_VERSION
),
1188 if (!s
->ok() || !found
) {
1189 INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES
);
1193 // Ignore stale metadata from failed initial opens.
1194 if (database_version
!= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
)
1195 found_names
.push_back(database_name_key
.database_name());
1199 INTERNAL_READ_ERROR(GET_DATABASE_NAMES
);
1204 leveldb::Status
IndexedDBBackingStore::GetIDBDatabaseMetaData(
1205 const base::string16
& name
,
1206 IndexedDBDatabaseMetadata
* metadata
,
1208 const std::string key
= DatabaseNameKey::Encode(origin_identifier_
, name
);
1211 leveldb::Status s
= GetInt(db_
.get(), key
, &metadata
->id
, found
);
1213 INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA
);
1217 return leveldb::Status::OK();
1219 s
= GetString(db_
.get(),
1220 DatabaseMetaDataKey::Encode(metadata
->id
,
1221 DatabaseMetaDataKey::USER_VERSION
),
1225 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1229 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1230 return InternalInconsistencyStatus();
1233 s
= GetVarInt(db_
.get(),
1234 DatabaseMetaDataKey::Encode(
1235 metadata
->id
, DatabaseMetaDataKey::USER_INT_VERSION
),
1236 &metadata
->int_version
,
1239 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1243 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1244 return InternalInconsistencyStatus();
1247 if (metadata
->int_version
== IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
)
1248 metadata
->int_version
= IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1250 s
= GetMaxObjectStoreId(
1251 db_
.get(), metadata
->id
, &metadata
->max_object_store_id
);
1253 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1256 // We don't cache this, we just check it if it's there.
1257 int64 blob_key_generator_current_number
=
1258 DatabaseMetaDataKey::kInvalidBlobKey
;
1262 DatabaseMetaDataKey::Encode(
1263 metadata
->id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
),
1264 &blob_key_generator_current_number
,
1267 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1271 // This database predates blob support.
1273 } else if (!DatabaseMetaDataKey::IsValidBlobKey(
1274 blob_key_generator_current_number
)) {
1275 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1276 return InternalInconsistencyStatus();
1282 WARN_UNUSED_RESULT
static leveldb::Status
GetNewDatabaseId(
1283 LevelDBTransaction
* transaction
,
1286 int64 max_database_id
= -1;
1289 GetInt(transaction
, MaxDatabaseIdKey::Encode(), &max_database_id
, &found
);
1291 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID
);
1295 max_database_id
= 0;
1297 DCHECK_GE(max_database_id
, 0);
1299 int64 database_id
= max_database_id
+ 1;
1300 PutInt(transaction
, MaxDatabaseIdKey::Encode(), database_id
);
1301 *new_id
= database_id
;
1302 return leveldb::Status::OK();
1305 leveldb::Status
IndexedDBBackingStore::CreateIDBDatabaseMetaData(
1306 const base::string16
& name
,
1307 const base::string16
& version
,
1310 // TODO(jsbell): Don't persist metadata if open fails. http://crbug.com/395472
1311 scoped_refptr
<LevelDBTransaction
> transaction
=
1312 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
1314 leveldb::Status s
= GetNewDatabaseId(transaction
.get(), row_id
);
1317 DCHECK_GE(*row_id
, 0);
1319 if (int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
)
1320 int_version
= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
;
1322 PutInt(transaction
.get(),
1323 DatabaseNameKey::Encode(origin_identifier_
, name
),
1327 DatabaseMetaDataKey::Encode(*row_id
, DatabaseMetaDataKey::USER_VERSION
),
1329 PutVarInt(transaction
.get(),
1330 DatabaseMetaDataKey::Encode(*row_id
,
1331 DatabaseMetaDataKey::USER_INT_VERSION
),
1335 DatabaseMetaDataKey::Encode(
1336 *row_id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
),
1337 DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber
);
1339 s
= transaction
->Commit();
1341 INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA
);
1345 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
1346 IndexedDBBackingStore::Transaction
* transaction
,
1348 int64 int_version
) {
1349 if (int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
)
1350 int_version
= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
;
1351 DCHECK_GE(int_version
, 0) << "int_version was " << int_version
;
1352 PutVarInt(transaction
->transaction(),
1353 DatabaseMetaDataKey::Encode(row_id
,
1354 DatabaseMetaDataKey::USER_INT_VERSION
),
1359 // If you're deleting a range that contains user keys that have blob info, this
1360 // won't clean up the blobs.
1361 static leveldb::Status
DeleteRangeBasic(LevelDBTransaction
* transaction
,
1362 const std::string
& begin
,
1363 const std::string
& end
,
1365 scoped_ptr
<LevelDBIterator
> it
= transaction
->CreateIterator();
1367 for (s
= it
->Seek(begin
); s
.ok() && it
->IsValid() &&
1368 (upper_open
? CompareKeys(it
->Key(), end
) < 0
1369 : CompareKeys(it
->Key(), end
) <= 0);
1371 transaction
->Remove(it
->Key());
1375 static leveldb::Status
DeleteBlobsInRange(
1376 IndexedDBBackingStore::Transaction
* transaction
,
1378 int64 object_store_id
,
1379 const std::string
& start_key
,
1380 const std::string
& end_key
,
1382 scoped_ptr
<LevelDBIterator
> it
= transaction
->transaction()->CreateIterator();
1383 leveldb::Status s
= it
->Seek(start_key
);
1384 for (; s
.ok() && it
->IsValid() &&
1385 (upper_open
? CompareKeys(it
->Key(), end_key
) < 0
1386 : CompareKeys(it
->Key(), end_key
) <= 0);
1388 StringPiece
key_piece(it
->Key());
1389 std::string user_key
=
1390 BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece
);
1391 if (!user_key
.size()) {
1392 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1393 return InternalInconsistencyStatus();
1395 transaction
->PutBlobInfo(
1396 database_id
, object_store_id
, user_key
, NULL
, NULL
);
1401 static leveldb::Status
DeleteBlobsInObjectStore(
1402 IndexedDBBackingStore::Transaction
* transaction
,
1404 int64 object_store_id
) {
1405 std::string start_key
, stop_key
;
1407 BlobEntryKey::EncodeMinKeyForObjectStore(database_id
, object_store_id
);
1409 BlobEntryKey::EncodeStopKeyForObjectStore(database_id
, object_store_id
);
1410 return DeleteBlobsInRange(
1411 transaction
, database_id
, object_store_id
, start_key
, stop_key
, true);
1414 leveldb::Status
IndexedDBBackingStore::DeleteDatabase(
1415 const base::string16
& name
) {
1416 IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
1417 scoped_ptr
<LevelDBDirectTransaction
> transaction
=
1418 LevelDBDirectTransaction::Create(db_
.get());
1421 s
= CleanUpBlobJournal(BlobJournalKey::Encode());
1425 IndexedDBDatabaseMetadata metadata
;
1426 bool success
= false;
1427 s
= GetIDBDatabaseMetaData(name
, &metadata
, &success
);
1431 return leveldb::Status::OK();
1433 const std::string start_key
= DatabaseMetaDataKey::Encode(
1434 metadata
.id
, DatabaseMetaDataKey::ORIGIN_NAME
);
1435 const std::string stop_key
= DatabaseMetaDataKey::Encode(
1436 metadata
.id
+ 1, DatabaseMetaDataKey::ORIGIN_NAME
);
1437 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
1438 for (s
= it
->Seek(start_key
);
1439 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
1441 transaction
->Remove(it
->Key());
1443 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE
);
1447 const std::string key
= DatabaseNameKey::Encode(origin_identifier_
, name
);
1448 transaction
->Remove(key
);
1450 bool need_cleanup
= false;
1451 if (active_blob_registry()->MarkDeletedCheckIfUsed(
1452 metadata
.id
, DatabaseMetaDataKey::kAllBlobsKey
)) {
1453 s
= MergeDatabaseIntoLiveBlobJournal(transaction
.get(), metadata
.id
);
1457 UpdateBlobJournalWithDatabase(transaction
.get(), metadata
.id
);
1458 need_cleanup
= true;
1461 s
= transaction
->Commit();
1463 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE
);
1468 CleanUpBlobJournal(BlobJournalKey::Encode());
1470 db_
->Compact(start_key
, stop_key
);
1474 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator
* it
,
1475 const std::string
& stop_key
,
1476 int64 object_store_id
,
1477 int64 meta_data_type
) {
1478 if (!it
->IsValid() || CompareKeys(it
->Key(), stop_key
) >= 0)
1481 StringPiece
slice(it
->Key());
1482 ObjectStoreMetaDataKey meta_data_key
;
1484 ObjectStoreMetaDataKey::Decode(&slice
, &meta_data_key
) && slice
.empty();
1486 if (meta_data_key
.ObjectStoreId() != object_store_id
)
1488 if (meta_data_key
.MetaDataType() != meta_data_type
)
1493 // TODO(jsbell): This should do some error handling rather than
1494 // plowing ahead when bad data is encountered.
1495 leveldb::Status
IndexedDBBackingStore::GetObjectStores(
1497 IndexedDBDatabaseMetadata::ObjectStoreMap
* object_stores
) {
1498 IDB_TRACE("IndexedDBBackingStore::GetObjectStores");
1499 if (!KeyPrefix::IsValidDatabaseId(database_id
))
1500 return InvalidDBKeyStatus();
1501 const std::string start_key
=
1502 ObjectStoreMetaDataKey::Encode(database_id
, 1, 0);
1503 const std::string stop_key
=
1504 ObjectStoreMetaDataKey::EncodeMaxKey(database_id
);
1506 DCHECK(object_stores
->empty());
1508 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
1509 leveldb::Status s
= it
->Seek(start_key
);
1510 while (s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0) {
1511 StringPiece
slice(it
->Key());
1512 ObjectStoreMetaDataKey meta_data_key
;
1514 ObjectStoreMetaDataKey::Decode(&slice
, &meta_data_key
) && slice
.empty();
1516 if (!ok
|| meta_data_key
.MetaDataType() != ObjectStoreMetaDataKey::NAME
) {
1517 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1518 // Possible stale metadata, but don't fail the load.
1525 int64 object_store_id
= meta_data_key
.ObjectStoreId();
1527 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
1529 base::string16 object_store_name
;
1531 StringPiece
slice(it
->Value());
1532 if (!DecodeString(&slice
, &object_store_name
) || !slice
.empty())
1533 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1539 if (!CheckObjectStoreAndMetaDataType(it
.get(),
1542 ObjectStoreMetaDataKey::KEY_PATH
)) {
1543 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1546 IndexedDBKeyPath key_path
;
1548 StringPiece
slice(it
->Value());
1549 if (!DecodeIDBKeyPath(&slice
, &key_path
) || !slice
.empty())
1550 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1556 if (!CheckObjectStoreAndMetaDataType(
1560 ObjectStoreMetaDataKey::AUTO_INCREMENT
)) {
1561 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1564 bool auto_increment
;
1566 StringPiece
slice(it
->Value());
1567 if (!DecodeBool(&slice
, &auto_increment
) || !slice
.empty())
1568 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1571 s
= it
->Next(); // Is evictable.
1574 if (!CheckObjectStoreAndMetaDataType(it
.get(),
1577 ObjectStoreMetaDataKey::EVICTABLE
)) {
1578 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1582 s
= it
->Next(); // Last version.
1585 if (!CheckObjectStoreAndMetaDataType(
1589 ObjectStoreMetaDataKey::LAST_VERSION
)) {
1590 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1594 s
= it
->Next(); // Maximum index id allocated.
1597 if (!CheckObjectStoreAndMetaDataType(
1601 ObjectStoreMetaDataKey::MAX_INDEX_ID
)) {
1602 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1607 StringPiece
slice(it
->Value());
1608 if (!DecodeInt(&slice
, &max_index_id
) || !slice
.empty())
1609 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1612 s
= it
->Next(); // [optional] has key path (is not null)
1615 if (CheckObjectStoreAndMetaDataType(it
.get(),
1618 ObjectStoreMetaDataKey::HAS_KEY_PATH
)) {
1621 StringPiece
slice(it
->Value());
1622 if (!DecodeBool(&slice
, &has_key_path
))
1623 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1625 // This check accounts for two layers of legacy coding:
1626 // (1) Initially, has_key_path was added to distinguish null vs. string.
1627 // (2) Later, null vs. string vs. array was stored in the key_path itself.
1628 // So this check is only relevant for string-type key_paths.
1629 if (!has_key_path
&&
1630 (key_path
.type() == blink::WebIDBKeyPathTypeString
&&
1631 !key_path
.string().empty())) {
1632 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1636 key_path
= IndexedDBKeyPath();
1642 int64 key_generator_current_number
= -1;
1643 if (CheckObjectStoreAndMetaDataType(
1647 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
)) {
1648 StringPiece
slice(it
->Value());
1649 if (!DecodeInt(&slice
, &key_generator_current_number
) || !slice
.empty())
1650 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1652 // TODO(jsbell): Return key_generator_current_number, cache in
1653 // object store, and write lazily to backing store. For now,
1654 // just assert that if it was written it was valid.
1655 DCHECK_GE(key_generator_current_number
, kKeyGeneratorInitialNumber
);
1661 IndexedDBObjectStoreMetadata
metadata(object_store_name
,
1666 s
= GetIndexes(database_id
, object_store_id
, &metadata
.indexes
);
1669 (*object_stores
)[object_store_id
] = metadata
;
1673 INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES
);
1678 WARN_UNUSED_RESULT
static leveldb::Status
SetMaxObjectStoreId(
1679 LevelDBTransaction
* transaction
,
1681 int64 object_store_id
) {
1682 const std::string max_object_store_id_key
= DatabaseMetaDataKey::Encode(
1683 database_id
, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID
);
1684 int64 max_object_store_id
= -1;
1685 leveldb::Status s
= GetMaxObjectStoreId(
1686 transaction
, max_object_store_id_key
, &max_object_store_id
);
1688 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID
);
1692 if (object_store_id
<= max_object_store_id
) {
1693 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID
);
1694 return InternalInconsistencyStatus();
1696 PutInt(transaction
, max_object_store_id_key
, object_store_id
);
1700 void IndexedDBBackingStore::Compact() { db_
->CompactAll(); }
1702 leveldb::Status
IndexedDBBackingStore::CreateObjectStore(
1703 IndexedDBBackingStore::Transaction
* transaction
,
1705 int64 object_store_id
,
1706 const base::string16
& name
,
1707 const IndexedDBKeyPath
& key_path
,
1708 bool auto_increment
) {
1709 IDB_TRACE("IndexedDBBackingStore::CreateObjectStore");
1710 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1711 return InvalidDBKeyStatus();
1712 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1714 SetMaxObjectStoreId(leveldb_transaction
, database_id
, object_store_id
);
1718 const std::string name_key
= ObjectStoreMetaDataKey::Encode(
1719 database_id
, object_store_id
, ObjectStoreMetaDataKey::NAME
);
1720 const std::string key_path_key
= ObjectStoreMetaDataKey::Encode(
1721 database_id
, object_store_id
, ObjectStoreMetaDataKey::KEY_PATH
);
1722 const std::string auto_increment_key
= ObjectStoreMetaDataKey::Encode(
1723 database_id
, object_store_id
, ObjectStoreMetaDataKey::AUTO_INCREMENT
);
1724 const std::string evictable_key
= ObjectStoreMetaDataKey::Encode(
1725 database_id
, object_store_id
, ObjectStoreMetaDataKey::EVICTABLE
);
1726 const std::string last_version_key
= ObjectStoreMetaDataKey::Encode(
1727 database_id
, object_store_id
, ObjectStoreMetaDataKey::LAST_VERSION
);
1728 const std::string max_index_id_key
= ObjectStoreMetaDataKey::Encode(
1729 database_id
, object_store_id
, ObjectStoreMetaDataKey::MAX_INDEX_ID
);
1730 const std::string has_key_path_key
= ObjectStoreMetaDataKey::Encode(
1731 database_id
, object_store_id
, ObjectStoreMetaDataKey::HAS_KEY_PATH
);
1732 const std::string key_generator_current_number_key
=
1733 ObjectStoreMetaDataKey::Encode(
1736 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
1737 const std::string names_key
= ObjectStoreNamesKey::Encode(database_id
, name
);
1739 PutString(leveldb_transaction
, name_key
, name
);
1740 PutIDBKeyPath(leveldb_transaction
, key_path_key
, key_path
);
1741 PutInt(leveldb_transaction
, auto_increment_key
, auto_increment
);
1742 PutInt(leveldb_transaction
, evictable_key
, false);
1743 PutInt(leveldb_transaction
, last_version_key
, 1);
1744 PutInt(leveldb_transaction
, max_index_id_key
, kMinimumIndexId
);
1745 PutBool(leveldb_transaction
, has_key_path_key
, !key_path
.IsNull());
1746 PutInt(leveldb_transaction
,
1747 key_generator_current_number_key
,
1748 kKeyGeneratorInitialNumber
);
1749 PutInt(leveldb_transaction
, names_key
, object_store_id
);
1753 leveldb::Status
IndexedDBBackingStore::DeleteObjectStore(
1754 IndexedDBBackingStore::Transaction
* transaction
,
1756 int64 object_store_id
) {
1757 IDB_TRACE("IndexedDBBackingStore::DeleteObjectStore");
1758 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1759 return InvalidDBKeyStatus();
1760 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1762 base::string16 object_store_name
;
1765 GetString(leveldb_transaction
,
1766 ObjectStoreMetaDataKey::Encode(
1767 database_id
, object_store_id
, ObjectStoreMetaDataKey::NAME
),
1771 INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1775 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1776 return InternalInconsistencyStatus();
1779 s
= DeleteBlobsInObjectStore(transaction
, database_id
, object_store_id
);
1781 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1785 s
= DeleteRangeBasic(
1786 leveldb_transaction
,
1787 ObjectStoreMetaDataKey::Encode(database_id
, object_store_id
, 0),
1788 ObjectStoreMetaDataKey::EncodeMaxKey(database_id
, object_store_id
),
1792 leveldb_transaction
->Remove(
1793 ObjectStoreNamesKey::Encode(database_id
, object_store_name
));
1795 s
= DeleteRangeBasic(
1796 leveldb_transaction
,
1797 IndexFreeListKey::Encode(database_id
, object_store_id
, 0),
1798 IndexFreeListKey::EncodeMaxKey(database_id
, object_store_id
),
1803 s
= DeleteRangeBasic(
1804 leveldb_transaction
,
1805 IndexMetaDataKey::Encode(database_id
, object_store_id
, 0, 0),
1806 IndexMetaDataKey::EncodeMaxKey(database_id
, object_store_id
),
1811 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1815 return ClearObjectStore(transaction
, database_id
, object_store_id
);
1818 leveldb::Status
IndexedDBBackingStore::GetRecord(
1819 IndexedDBBackingStore::Transaction
* transaction
,
1821 int64 object_store_id
,
1822 const IndexedDBKey
& key
,
1823 IndexedDBValue
* record
) {
1824 IDB_TRACE("IndexedDBBackingStore::GetRecord");
1825 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1826 return InvalidDBKeyStatus();
1827 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1829 const std::string leveldb_key
=
1830 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
1836 leveldb::Status s
= leveldb_transaction
->Get(leveldb_key
, &data
, &found
);
1838 INTERNAL_READ_ERROR(GET_RECORD
);
1844 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD
);
1845 return leveldb::Status::NotFound("Record contained no data");
1849 StringPiece
slice(data
);
1850 if (!DecodeVarInt(&slice
, &version
)) {
1851 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD
);
1852 return InternalInconsistencyStatus();
1855 record
->bits
= slice
.as_string();
1856 return transaction
->GetBlobInfoForRecord(database_id
, leveldb_key
, record
);
1859 WARN_UNUSED_RESULT
static leveldb::Status
GetNewVersionNumber(
1860 LevelDBTransaction
* transaction
,
1862 int64 object_store_id
,
1863 int64
* new_version_number
) {
1864 const std::string last_version_key
= ObjectStoreMetaDataKey::Encode(
1865 database_id
, object_store_id
, ObjectStoreMetaDataKey::LAST_VERSION
);
1867 *new_version_number
= -1;
1868 int64 last_version
= -1;
1871 GetInt(transaction
, last_version_key
, &last_version
, &found
);
1873 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER
);
1879 DCHECK_GE(last_version
, 0);
1881 int64 version
= last_version
+ 1;
1882 PutInt(transaction
, last_version_key
, version
);
1884 // TODO(jsbell): Think about how we want to handle the overflow scenario.
1885 DCHECK(version
> last_version
);
1887 *new_version_number
= version
;
1891 leveldb::Status
IndexedDBBackingStore::PutRecord(
1892 IndexedDBBackingStore::Transaction
* transaction
,
1894 int64 object_store_id
,
1895 const IndexedDBKey
& key
,
1896 IndexedDBValue
* value
,
1897 ScopedVector
<storage::BlobDataHandle
>* handles
,
1898 RecordIdentifier
* record_identifier
) {
1899 IDB_TRACE("IndexedDBBackingStore::PutRecord");
1900 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1901 return InvalidDBKeyStatus();
1902 DCHECK(key
.IsValid());
1904 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1906 leveldb::Status s
= GetNewVersionNumber(
1907 leveldb_transaction
, database_id
, object_store_id
, &version
);
1910 DCHECK_GE(version
, 0);
1911 const std::string object_store_data_key
=
1912 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
1915 EncodeVarInt(version
, &v
);
1916 v
.append(value
->bits
);
1918 leveldb_transaction
->Put(object_store_data_key
, &v
);
1919 s
= transaction
->PutBlobInfoIfNeeded(database_id
,
1921 object_store_data_key
,
1926 DCHECK(!handles
->size());
1928 const std::string exists_entry_key
=
1929 ExistsEntryKey::Encode(database_id
, object_store_id
, key
);
1930 std::string version_encoded
;
1931 EncodeInt(version
, &version_encoded
);
1932 leveldb_transaction
->Put(exists_entry_key
, &version_encoded
);
1934 std::string key_encoded
;
1935 EncodeIDBKey(key
, &key_encoded
);
1936 record_identifier
->Reset(key_encoded
, version
);
1940 leveldb::Status
IndexedDBBackingStore::ClearObjectStore(
1941 IndexedDBBackingStore::Transaction
* transaction
,
1943 int64 object_store_id
) {
1944 IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
1945 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1946 return InvalidDBKeyStatus();
1947 const std::string start_key
=
1948 KeyPrefix(database_id
, object_store_id
).Encode();
1949 const std::string stop_key
=
1950 KeyPrefix(database_id
, object_store_id
+ 1).Encode();
1953 DeleteRangeBasic(transaction
->transaction(), start_key
, stop_key
, true);
1955 INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE
);
1958 return DeleteBlobsInObjectStore(transaction
, database_id
, object_store_id
);
1961 leveldb::Status
IndexedDBBackingStore::DeleteRecord(
1962 IndexedDBBackingStore::Transaction
* transaction
,
1964 int64 object_store_id
,
1965 const RecordIdentifier
& record_identifier
) {
1966 IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
1967 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1968 return InvalidDBKeyStatus();
1969 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1971 const std::string object_store_data_key
= ObjectStoreDataKey::Encode(
1972 database_id
, object_store_id
, record_identifier
.primary_key());
1973 leveldb_transaction
->Remove(object_store_data_key
);
1974 leveldb::Status s
= transaction
->PutBlobInfoIfNeeded(
1975 database_id
, object_store_id
, object_store_data_key
, NULL
, NULL
);
1979 const std::string exists_entry_key
= ExistsEntryKey::Encode(
1980 database_id
, object_store_id
, record_identifier
.primary_key());
1981 leveldb_transaction
->Remove(exists_entry_key
);
1982 return leveldb::Status::OK();
1985 leveldb::Status
IndexedDBBackingStore::DeleteRange(
1986 IndexedDBBackingStore::Transaction
* transaction
,
1988 int64 object_store_id
,
1989 const IndexedDBKeyRange
& key_range
) {
1991 scoped_ptr
<IndexedDBBackingStore::Cursor
> start_cursor
=
1992 OpenObjectStoreCursor(transaction
,
1996 blink::WebIDBCursorDirectionNext
,
2001 return leveldb::Status::OK(); // Empty range == delete success.
2003 scoped_ptr
<IndexedDBBackingStore::Cursor
> end_cursor
=
2004 OpenObjectStoreCursor(transaction
,
2008 blink::WebIDBCursorDirectionPrev
,
2014 return leveldb::Status::OK(); // Empty range == delete success.
2016 BlobEntryKey start_blob_key
, end_blob_key
;
2018 std::string start_key
= ObjectStoreDataKey::Encode(
2019 database_id
, object_store_id
, start_cursor
->key());
2020 base::StringPiece
start_key_piece(start_key
);
2021 if (!BlobEntryKey::FromObjectStoreDataKey(&start_key_piece
, &start_blob_key
))
2022 return InternalInconsistencyStatus();
2023 std::string stop_key
= ObjectStoreDataKey::Encode(
2024 database_id
, object_store_id
, end_cursor
->key());
2025 base::StringPiece
stop_key_piece(stop_key
);
2026 if (!BlobEntryKey::FromObjectStoreDataKey(&stop_key_piece
, &end_blob_key
))
2027 return InternalInconsistencyStatus();
2029 s
= DeleteBlobsInRange(transaction
,
2032 start_blob_key
.Encode(),
2033 end_blob_key
.Encode(),
2037 s
= DeleteRangeBasic(transaction
->transaction(), start_key
, stop_key
, false);
2041 ExistsEntryKey::Encode(database_id
, object_store_id
, start_cursor
->key());
2043 ExistsEntryKey::Encode(database_id
, object_store_id
, end_cursor
->key());
2044 return DeleteRangeBasic(
2045 transaction
->transaction(), start_key
, stop_key
, false);
2048 leveldb::Status
IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
2049 IndexedDBBackingStore::Transaction
* transaction
,
2051 int64 object_store_id
,
2052 int64
* key_generator_current_number
) {
2053 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2054 return InvalidDBKeyStatus();
2055 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2057 const std::string key_generator_current_number_key
=
2058 ObjectStoreMetaDataKey::Encode(
2061 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
2063 *key_generator_current_number
= -1;
2068 leveldb_transaction
->Get(key_generator_current_number_key
, &data
, &found
);
2070 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2073 if (found
&& !data
.empty()) {
2074 StringPiece
slice(data
);
2075 if (!DecodeInt(&slice
, key_generator_current_number
) || !slice
.empty()) {
2076 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2077 return InternalInconsistencyStatus();
2082 // Previously, the key generator state was not stored explicitly
2083 // but derived from the maximum numeric key present in existing
2084 // data. This violates the spec as the data may be cleared but the
2085 // key generator state must be preserved.
2086 // TODO(jsbell): Fix this for all stores on database open?
2087 const std::string start_key
=
2088 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MinIDBKey());
2089 const std::string stop_key
=
2090 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MaxIDBKey());
2092 scoped_ptr
<LevelDBIterator
> it
= leveldb_transaction
->CreateIterator();
2093 int64 max_numeric_key
= 0;
2095 for (s
= it
->Seek(start_key
);
2096 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
2098 StringPiece
slice(it
->Key());
2099 ObjectStoreDataKey data_key
;
2100 if (!ObjectStoreDataKey::Decode(&slice
, &data_key
) || !slice
.empty()) {
2101 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2102 return InternalInconsistencyStatus();
2104 scoped_ptr
<IndexedDBKey
> user_key
= data_key
.user_key();
2105 if (user_key
->type() == blink::WebIDBKeyTypeNumber
) {
2106 int64 n
= static_cast<int64
>(user_key
->number());
2107 if (n
> max_numeric_key
)
2108 max_numeric_key
= n
;
2113 *key_generator_current_number
= max_numeric_key
+ 1;
2115 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2120 leveldb::Status
IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
2121 IndexedDBBackingStore::Transaction
* transaction
,
2123 int64 object_store_id
,
2125 bool check_current
) {
2126 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2127 return InvalidDBKeyStatus();
2129 if (check_current
) {
2130 int64 current_number
;
2131 leveldb::Status s
= GetKeyGeneratorCurrentNumber(
2132 transaction
, database_id
, object_store_id
, ¤t_number
);
2135 if (new_number
<= current_number
)
2139 const std::string key_generator_current_number_key
=
2140 ObjectStoreMetaDataKey::Encode(
2143 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
2145 transaction
->transaction(), key_generator_current_number_key
, new_number
);
2146 return leveldb::Status::OK();
2149 leveldb::Status
IndexedDBBackingStore::KeyExistsInObjectStore(
2150 IndexedDBBackingStore::Transaction
* transaction
,
2152 int64 object_store_id
,
2153 const IndexedDBKey
& key
,
2154 RecordIdentifier
* found_record_identifier
,
2156 IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
2157 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2158 return InvalidDBKeyStatus();
2160 const std::string leveldb_key
=
2161 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
2165 transaction
->transaction()->Get(leveldb_key
, &data
, found
);
2167 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE
);
2171 return leveldb::Status::OK();
2173 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE
);
2174 return InternalInconsistencyStatus();
2178 StringPiece
slice(data
);
2179 if (!DecodeVarInt(&slice
, &version
))
2180 return InternalInconsistencyStatus();
2182 std::string encoded_key
;
2183 EncodeIDBKey(key
, &encoded_key
);
2184 found_record_identifier
->Reset(encoded_key
, version
);
2188 class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
2189 : public IndexedDBBackingStore::Transaction::ChainedBlobWriter
{
2191 typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
2193 ChainedBlobWriterImpl(
2195 IndexedDBBackingStore
* backing_store
,
2196 WriteDescriptorVec
* blobs
,
2197 scoped_refptr
<IndexedDBBackingStore::BlobWriteCallback
> callback
)
2198 : waiting_for_callback_(false),
2199 database_id_(database_id
),
2200 backing_store_(backing_store
),
2201 callback_(callback
),
2203 blobs_
.swap(*blobs
);
2204 iter_
= blobs_
.begin();
2205 backing_store
->task_runner()->PostTask(
2206 FROM_HERE
, base::Bind(&ChainedBlobWriterImpl::WriteNextFile
, this));
2209 void set_delegate(scoped_ptr
<FileWriterDelegate
> delegate
) override
{
2210 delegate_
.reset(delegate
.release());
2213 void ReportWriteCompletion(bool succeeded
, int64 bytes_written
) override
{
2214 DCHECK(waiting_for_callback_
);
2215 DCHECK(!succeeded
|| bytes_written
>= 0);
2216 waiting_for_callback_
= false;
2217 if (delegate_
.get()) // Only present for Blob, not File.
2218 content::BrowserThread::DeleteSoon(
2219 content::BrowserThread::IO
, FROM_HERE
, delegate_
.release());
2224 if (iter_
->size() != -1 && iter_
->size() != bytes_written
)
2230 callback_
->Run(false);
2234 void Abort() override
{
2235 if (!waiting_for_callback_
)
2242 ~ChainedBlobWriterImpl() override
{}
2244 void WriteNextFile() {
2245 DCHECK(!waiting_for_callback_
);
2247 if (iter_
== blobs_
.end()) {
2248 DCHECK(!self_ref_
.get());
2249 callback_
->Run(true);
2252 if (!backing_store_
->WriteBlobFile(database_id_
, *iter_
, this)) {
2253 callback_
->Run(false);
2256 waiting_for_callback_
= true;
2260 bool waiting_for_callback_
;
2261 scoped_refptr
<ChainedBlobWriterImpl
> self_ref_
;
2262 WriteDescriptorVec blobs_
;
2263 WriteDescriptorVec::const_iterator iter_
;
2265 IndexedDBBackingStore
* backing_store_
;
2266 scoped_refptr
<IndexedDBBackingStore::BlobWriteCallback
> callback_
;
2267 scoped_ptr
<FileWriterDelegate
> delegate_
;
2270 DISALLOW_COPY_AND_ASSIGN(ChainedBlobWriterImpl
);
2273 class LocalWriteClosure
: public FileWriterDelegate::DelegateWriteCallback
,
2274 public base::RefCountedThreadSafe
<LocalWriteClosure
> {
2276 LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter
*
2277 chained_blob_writer
,
2278 base::SequencedTaskRunner
* task_runner
)
2279 : chained_blob_writer_(chained_blob_writer
),
2280 task_runner_(task_runner
),
2281 bytes_written_(0) {}
2283 void Run(base::File::Error rv
,
2285 FileWriterDelegate::WriteProgressStatus write_status
) {
2286 DCHECK_GE(bytes
, 0);
2287 bytes_written_
+= bytes
;
2288 if (write_status
== FileWriterDelegate::SUCCESS_IO_PENDING
)
2289 return; // We don't care about progress events.
2290 if (rv
== base::File::FILE_OK
) {
2291 DCHECK_EQ(write_status
, FileWriterDelegate::SUCCESS_COMPLETED
);
2293 DCHECK(write_status
== FileWriterDelegate::ERROR_WRITE_STARTED
||
2294 write_status
== FileWriterDelegate::ERROR_WRITE_NOT_STARTED
);
2296 task_runner_
->PostTask(
2298 base::Bind(&IndexedDBBackingStore::Transaction::ChainedBlobWriter::
2299 ReportWriteCompletion
,
2300 chained_blob_writer_
,
2301 write_status
== FileWriterDelegate::SUCCESS_COMPLETED
,
2305 void writeBlobToFileOnIOThread(const FilePath
& file_path
,
2306 const GURL
& blob_url
,
2307 net::URLRequestContext
* request_context
) {
2308 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
2309 scoped_ptr
<storage::FileStreamWriter
> writer(
2310 storage::FileStreamWriter::CreateForLocalFile(
2314 storage::FileStreamWriter::CREATE_NEW_FILE
));
2315 scoped_ptr
<FileWriterDelegate
> delegate(
2316 new FileWriterDelegate(writer
.Pass(),
2317 FileWriterDelegate::FLUSH_ON_COMPLETION
));
2319 DCHECK(blob_url
.is_valid());
2320 scoped_ptr
<net::URLRequest
> blob_request(request_context
->CreateRequest(
2321 blob_url
, net::DEFAULT_PRIORITY
, delegate
.get(), NULL
));
2323 delegate
->Start(blob_request
.Pass(),
2324 base::Bind(&LocalWriteClosure::Run
, this));
2325 chained_blob_writer_
->set_delegate(delegate
.Pass());
2329 virtual ~LocalWriteClosure() {
2330 // Make sure the last reference to a ChainedBlobWriter is released (and
2331 // deleted) on the IDB thread since it owns a transaction which has thread
2333 IndexedDBBackingStore::Transaction::ChainedBlobWriter
* raw_tmp
=
2334 chained_blob_writer_
.get();
2336 chained_blob_writer_
= NULL
;
2337 task_runner_
->ReleaseSoon(FROM_HERE
, raw_tmp
);
2339 friend class base::RefCountedThreadSafe
<LocalWriteClosure
>;
2341 scoped_refptr
<IndexedDBBackingStore::Transaction::ChainedBlobWriter
>
2342 chained_blob_writer_
;
2343 scoped_refptr
<base::SequencedTaskRunner
> task_runner_
;
2344 int64 bytes_written_
;
2346 DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure
);
2349 bool IndexedDBBackingStore::WriteBlobFile(
2351 const Transaction::WriteDescriptor
& descriptor
,
2352 Transaction::ChainedBlobWriter
* chained_blob_writer
) {
2354 if (!MakeIDBBlobDirectory(blob_path_
, database_id
, descriptor
.key()))
2357 FilePath path
= GetBlobFileName(database_id
, descriptor
.key());
2359 if (descriptor
.is_file()) {
2360 DCHECK(!descriptor
.file_path().empty());
2361 if (!base::CopyFile(descriptor
.file_path(), path
))
2364 base::File::Info info
;
2365 if (base::GetFileInfo(descriptor
.file_path(), &info
)) {
2366 if (descriptor
.size() != -1) {
2367 if (descriptor
.size() != info
.size
)
2369 // The round-trip can be lossy; round to nearest millisecond.
2370 int64 delta
= (descriptor
.last_modified() -
2371 info
.last_modified
).InMilliseconds();
2372 if (std::abs(delta
) > 1)
2375 if (!base::TouchFile(path
, info
.last_accessed
, info
.last_modified
)) {
2376 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2379 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2382 task_runner_
->PostTask(
2384 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion
,
2385 chained_blob_writer
,
2389 DCHECK(descriptor
.url().is_valid());
2390 scoped_refptr
<LocalWriteClosure
> write_closure(
2391 new LocalWriteClosure(chained_blob_writer
, task_runner_
.get()));
2392 content::BrowserThread::PostTask(
2393 content::BrowserThread::IO
,
2395 base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread
,
2396 write_closure
.get(),
2404 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id
,
2406 DCHECK(KeyPrefix::IsValidDatabaseId(database_id
));
2407 bool all_blobs
= blob_key
== DatabaseMetaDataKey::kAllBlobsKey
;
2408 DCHECK(all_blobs
|| DatabaseMetaDataKey::IsValidBlobKey(blob_key
));
2409 scoped_refptr
<LevelDBTransaction
> transaction
=
2410 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
2412 std::string live_blob_key
= LiveBlobJournalKey::Encode();
2413 BlobJournalType live_blob_journal
;
2414 if (!GetBlobJournal(live_blob_key
, transaction
.get(), &live_blob_journal
)
2417 DCHECK(live_blob_journal
.size());
2419 std::string primary_key
= BlobJournalKey::Encode();
2420 BlobJournalType primary_journal
;
2421 if (!GetBlobJournal(primary_key
, transaction
.get(), &primary_journal
).ok())
2424 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2425 // remove all entries with database_id from the live_blob journal and add only
2426 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2427 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2428 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2429 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2430 // matching (database_id, blob_key) tuple, we should move it to the primary
2432 BlobJournalType new_live_blob_journal
;
2433 for (BlobJournalType::iterator journal_iter
= live_blob_journal
.begin();
2434 journal_iter
!= live_blob_journal
.end();
2436 int64 current_database_id
= journal_iter
->first
;
2437 int64 current_blob_key
= journal_iter
->second
;
2438 bool current_all_blobs
=
2439 current_blob_key
== DatabaseMetaDataKey::kAllBlobsKey
;
2440 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id
) ||
2442 if (current_database_id
== database_id
&&
2443 (all_blobs
|| current_all_blobs
|| blob_key
== current_blob_key
)) {
2445 primary_journal
.push_back(
2446 std::make_pair(database_id
, current_blob_key
));
2447 if (current_all_blobs
)
2448 new_live_blob_journal
.push_back(*journal_iter
);
2449 new_live_blob_journal
.insert(new_live_blob_journal
.end(),
2451 live_blob_journal
.end()); // All the rest.
2455 new_live_blob_journal
.push_back(*journal_iter
);
2459 primary_journal
.push_back(
2460 std::make_pair(database_id
, DatabaseMetaDataKey::kAllBlobsKey
));
2462 UpdatePrimaryJournalWithBlobList(transaction
.get(), primary_journal
);
2463 UpdateLiveBlobJournalWithBlobList(transaction
.get(), new_live_blob_journal
);
2464 transaction
->Commit();
2465 // We could just do the deletions/cleaning here, but if there are a lot of
2466 // blobs about to be garbage collected, it'd be better to wait and do them all
2468 StartJournalCleaningTimer();
2471 // The this reference is a raw pointer that's declared Unretained inside the
2472 // timer code, so this won't confuse IndexedDBFactory's check for
2473 // HasLastBackingStoreReference. It's safe because if the backing store is
2474 // deleted, the timer will automatically be canceled on destruction.
2475 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2476 journal_cleaning_timer_
.Start(
2478 base::TimeDelta::FromSeconds(5),
2480 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn
);
2483 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
2484 FilePath
IndexedDBBackingStore::GetBlobFileName(int64 database_id
, int64 key
) {
2485 return GetBlobFileNameForKey(blob_path_
, database_id
, key
);
2488 static bool CheckIndexAndMetaDataKey(const LevelDBIterator
* it
,
2489 const std::string
& stop_key
,
2491 unsigned char meta_data_type
) {
2492 if (!it
->IsValid() || CompareKeys(it
->Key(), stop_key
) >= 0)
2495 StringPiece
slice(it
->Key());
2496 IndexMetaDataKey meta_data_key
;
2497 bool ok
= IndexMetaDataKey::Decode(&slice
, &meta_data_key
);
2499 if (meta_data_key
.IndexId() != index_id
)
2501 if (meta_data_key
.meta_data_type() != meta_data_type
)
2506 // TODO(jsbell): This should do some error handling rather than plowing ahead
2507 // when bad data is encountered.
2508 leveldb::Status
IndexedDBBackingStore::GetIndexes(
2510 int64 object_store_id
,
2511 IndexedDBObjectStoreMetadata::IndexMap
* indexes
) {
2512 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2513 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2514 return InvalidDBKeyStatus();
2515 const std::string start_key
=
2516 IndexMetaDataKey::Encode(database_id
, object_store_id
, 0, 0);
2517 const std::string stop_key
=
2518 IndexMetaDataKey::Encode(database_id
, object_store_id
+ 1, 0, 0);
2520 DCHECK(indexes
->empty());
2522 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
2523 leveldb::Status s
= it
->Seek(start_key
);
2524 while (s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0) {
2525 StringPiece
slice(it
->Key());
2526 IndexMetaDataKey meta_data_key
;
2527 bool ok
= IndexMetaDataKey::Decode(&slice
, &meta_data_key
);
2529 if (meta_data_key
.meta_data_type() != IndexMetaDataKey::NAME
) {
2530 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2531 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2539 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2541 int64 index_id
= meta_data_key
.IndexId();
2542 base::string16 index_name
;
2544 StringPiece
slice(it
->Value());
2545 if (!DecodeString(&slice
, &index_name
) || !slice
.empty())
2546 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2549 s
= it
->Next(); // unique flag
2552 if (!CheckIndexAndMetaDataKey(
2553 it
.get(), stop_key
, index_id
, IndexMetaDataKey::UNIQUE
)) {
2554 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2559 StringPiece
slice(it
->Value());
2560 if (!DecodeBool(&slice
, &index_unique
) || !slice
.empty())
2561 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2564 s
= it
->Next(); // key_path
2567 if (!CheckIndexAndMetaDataKey(
2568 it
.get(), stop_key
, index_id
, IndexMetaDataKey::KEY_PATH
)) {
2569 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2572 IndexedDBKeyPath key_path
;
2574 StringPiece
slice(it
->Value());
2575 if (!DecodeIDBKeyPath(&slice
, &key_path
) || !slice
.empty())
2576 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2579 s
= it
->Next(); // [optional] multi_entry flag
2582 bool index_multi_entry
= false;
2583 if (CheckIndexAndMetaDataKey(
2584 it
.get(), stop_key
, index_id
, IndexMetaDataKey::MULTI_ENTRY
)) {
2585 StringPiece
slice(it
->Value());
2586 if (!DecodeBool(&slice
, &index_multi_entry
) || !slice
.empty())
2587 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2594 (*indexes
)[index_id
] = IndexedDBIndexMetadata(
2595 index_name
, index_id
, key_path
, index_unique
, index_multi_entry
);
2599 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES
);
2604 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id
, int64 key
) {
2605 FilePath fileName
= GetBlobFileName(database_id
, key
);
2606 return base::DeleteFile(fileName
, false);
2609 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id
) {
2610 FilePath dirName
= GetBlobDirectoryName(blob_path_
, database_id
);
2611 return base::DeleteFile(dirName
, true);
2614 leveldb::Status
IndexedDBBackingStore::CleanUpBlobJournal(
2615 const std::string
& level_db_key
) {
2616 scoped_refptr
<LevelDBTransaction
> journal_transaction
=
2617 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
2618 BlobJournalType journal
;
2620 GetBlobJournal(level_db_key
, journal_transaction
.get(), &journal
);
2623 if (!journal
.size())
2624 return leveldb::Status::OK();
2625 for (const auto& entry
: journal
) {
2626 int64 database_id
= entry
.first
;
2627 int64 blob_key
= entry
.second
;
2628 DCHECK(KeyPrefix::IsValidDatabaseId(database_id
));
2629 if (blob_key
== DatabaseMetaDataKey::kAllBlobsKey
) {
2630 if (!RemoveBlobDirectory(database_id
))
2631 return IOErrorStatus();
2633 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key
));
2634 if (!RemoveBlobFile(database_id
, blob_key
))
2635 return IOErrorStatus();
2638 ClearBlobJournal(journal_transaction
.get(), level_db_key
);
2639 return journal_transaction
->Commit();
2642 leveldb::Status
IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
2644 const std::string
& object_store_data_key
,
2645 IndexedDBValue
* value
) {
2646 BlobChangeRecord
* change_record
= NULL
;
2647 BlobChangeMap::const_iterator blob_iter
=
2648 blob_change_map_
.find(object_store_data_key
);
2649 if (blob_iter
!= blob_change_map_
.end()) {
2650 change_record
= blob_iter
->second
;
2652 blob_iter
= incognito_blob_map_
.find(object_store_data_key
);
2653 if (blob_iter
!= incognito_blob_map_
.end())
2654 change_record
= blob_iter
->second
;
2656 if (change_record
) {
2657 // Either we haven't written the blob to disk yet or we're in incognito
2658 // mode, so we have to send back the one they sent us. This change record
2659 // includes the original UUID.
2660 value
->blob_info
= change_record
->blob_info();
2661 return leveldb::Status::OK();
2664 BlobEntryKey blob_entry_key
;
2665 StringPiece
leveldb_key_piece(object_store_data_key
);
2666 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece
,
2669 return InternalInconsistencyStatus();
2671 std::string encoded_key
= blob_entry_key
.Encode();
2673 std::string encoded_value
;
2674 leveldb::Status s
= transaction()->Get(encoded_key
, &encoded_value
, &found
);
2678 if (!DecodeBlobData(encoded_value
, &value
->blob_info
)) {
2679 INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD
);
2680 return InternalInconsistencyStatus();
2682 for (auto& entry
: value
->blob_info
) {
2683 entry
.set_file_path(
2684 backing_store_
->GetBlobFileName(database_id
, entry
.key()));
2685 entry
.set_mark_used_callback(
2686 backing_store_
->active_blob_registry()->GetAddBlobRefCallback(
2687 database_id
, entry
.key()));
2688 entry
.set_release_callback(
2689 backing_store_
->active_blob_registry()->GetFinalReleaseCallback(
2690 database_id
, entry
.key()));
2691 if (entry
.is_file()) {
2692 base::File::Info info
;
2693 if (base::GetFileInfo(entry
.file_path(), &info
)) {
2694 // This should always work, but it isn't fatal if it doesn't; it just
2695 // means a potential slow synchronous call from the renderer later.
2696 entry
.set_last_modified(info
.last_modified
);
2697 entry
.set_size(info
.size
);
2702 return leveldb::Status::OK();
2705 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2706 CleanUpBlobJournal(BlobJournalKey::Encode());
2709 WARN_UNUSED_RESULT
static leveldb::Status
SetMaxIndexId(
2710 LevelDBTransaction
* transaction
,
2712 int64 object_store_id
,
2714 int64 max_index_id
= -1;
2715 const std::string max_index_id_key
= ObjectStoreMetaDataKey::Encode(
2716 database_id
, object_store_id
, ObjectStoreMetaDataKey::MAX_INDEX_ID
);
2719 GetInt(transaction
, max_index_id_key
, &max_index_id
, &found
);
2721 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID
);
2725 max_index_id
= kMinimumIndexId
;
2727 if (index_id
<= max_index_id
) {
2728 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID
);
2729 return InternalInconsistencyStatus();
2732 PutInt(transaction
, max_index_id_key
, index_id
);
2736 leveldb::Status
IndexedDBBackingStore::CreateIndex(
2737 IndexedDBBackingStore::Transaction
* transaction
,
2739 int64 object_store_id
,
2741 const base::string16
& name
,
2742 const IndexedDBKeyPath
& key_path
,
2744 bool is_multi_entry
) {
2745 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2746 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2747 return InvalidDBKeyStatus();
2748 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2749 leveldb::Status s
= SetMaxIndexId(
2750 leveldb_transaction
, database_id
, object_store_id
, index_id
);
2755 const std::string name_key
= IndexMetaDataKey::Encode(
2756 database_id
, object_store_id
, index_id
, IndexMetaDataKey::NAME
);
2757 const std::string unique_key
= IndexMetaDataKey::Encode(
2758 database_id
, object_store_id
, index_id
, IndexMetaDataKey::UNIQUE
);
2759 const std::string key_path_key
= IndexMetaDataKey::Encode(
2760 database_id
, object_store_id
, index_id
, IndexMetaDataKey::KEY_PATH
);
2761 const std::string multi_entry_key
= IndexMetaDataKey::Encode(
2762 database_id
, object_store_id
, index_id
, IndexMetaDataKey::MULTI_ENTRY
);
2764 PutString(leveldb_transaction
, name_key
, name
);
2765 PutBool(leveldb_transaction
, unique_key
, is_unique
);
2766 PutIDBKeyPath(leveldb_transaction
, key_path_key
, key_path
);
2767 PutBool(leveldb_transaction
, multi_entry_key
, is_multi_entry
);
2771 leveldb::Status
IndexedDBBackingStore::DeleteIndex(
2772 IndexedDBBackingStore::Transaction
* transaction
,
2774 int64 object_store_id
,
2776 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2777 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2778 return InvalidDBKeyStatus();
2779 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2781 const std::string index_meta_data_start
=
2782 IndexMetaDataKey::Encode(database_id
, object_store_id
, index_id
, 0);
2783 const std::string index_meta_data_end
=
2784 IndexMetaDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
2785 leveldb::Status s
= DeleteRangeBasic(
2786 leveldb_transaction
, index_meta_data_start
, index_meta_data_end
, true);
2789 const std::string index_data_start
=
2790 IndexDataKey::EncodeMinKey(database_id
, object_store_id
, index_id
);
2791 const std::string index_data_end
=
2792 IndexDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
2793 s
= DeleteRangeBasic(
2794 leveldb_transaction
, index_data_start
, index_data_end
, true);
2798 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX
);
2803 leveldb::Status
IndexedDBBackingStore::PutIndexDataForRecord(
2804 IndexedDBBackingStore::Transaction
* transaction
,
2806 int64 object_store_id
,
2808 const IndexedDBKey
& key
,
2809 const RecordIdentifier
& record_identifier
) {
2810 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2811 DCHECK(key
.IsValid());
2812 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2813 return InvalidDBKeyStatus();
2815 std::string encoded_key
;
2816 EncodeIDBKey(key
, &encoded_key
);
2818 const std::string index_data_key
=
2819 IndexDataKey::Encode(database_id
,
2823 record_identifier
.primary_key(),
2827 EncodeVarInt(record_identifier
.version(), &data
);
2828 data
.append(record_identifier
.primary_key());
2830 transaction
->transaction()->Put(index_data_key
, &data
);
2831 return leveldb::Status::OK();
2834 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction
* transaction
,
2835 const std::string
& target
,
2836 std::string
* found_key
,
2837 leveldb::Status
* s
) {
2838 scoped_ptr
<LevelDBIterator
> it
= transaction
->CreateIterator();
2839 *s
= it
->Seek(target
);
2843 if (!it
->IsValid()) {
2844 *s
= it
->SeekToLast();
2845 if (!s
->ok() || !it
->IsValid())
2849 while (CompareIndexKeys(it
->Key(), target
) > 0) {
2851 if (!s
->ok() || !it
->IsValid())
2856 *found_key
= it
->Key().as_string();
2858 // There can be several index keys that compare equal. We want the last one.
2860 } while (s
->ok() && it
->IsValid() && !CompareIndexKeys(it
->Key(), target
));
2865 static leveldb::Status
VersionExists(LevelDBTransaction
* transaction
,
2867 int64 object_store_id
,
2869 const std::string
& encoded_primary_key
,
2871 const std::string key
=
2872 ExistsEntryKey::Encode(database_id
, object_store_id
, encoded_primary_key
);
2875 leveldb::Status s
= transaction
->Get(key
, &data
, exists
);
2877 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS
);
2883 StringPiece
slice(data
);
2885 if (!DecodeInt(&slice
, &decoded
) || !slice
.empty())
2886 return InternalInconsistencyStatus();
2887 *exists
= (decoded
== version
);
2891 leveldb::Status
IndexedDBBackingStore::FindKeyInIndex(
2892 IndexedDBBackingStore::Transaction
* transaction
,
2894 int64 object_store_id
,
2896 const IndexedDBKey
& key
,
2897 std::string
* found_encoded_primary_key
,
2899 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
2900 DCHECK(KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
));
2902 DCHECK(found_encoded_primary_key
->empty());
2905 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2906 const std::string leveldb_key
=
2907 IndexDataKey::Encode(database_id
, object_store_id
, index_id
, key
);
2908 scoped_ptr
<LevelDBIterator
> it
= leveldb_transaction
->CreateIterator();
2909 leveldb::Status s
= it
->Seek(leveldb_key
);
2911 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX
);
2917 return leveldb::Status::OK();
2918 if (CompareIndexKeys(it
->Key(), leveldb_key
) > 0)
2919 return leveldb::Status::OK();
2921 StringPiece
slice(it
->Value());
2924 if (!DecodeVarInt(&slice
, &version
)) {
2925 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX
);
2926 return InternalInconsistencyStatus();
2928 *found_encoded_primary_key
= slice
.as_string();
2930 bool exists
= false;
2931 s
= VersionExists(leveldb_transaction
,
2935 *found_encoded_primary_key
,
2940 // Delete stale index data entry and continue.
2941 leveldb_transaction
->Remove(it
->Key());
2950 leveldb::Status
IndexedDBBackingStore::GetPrimaryKeyViaIndex(
2951 IndexedDBBackingStore::Transaction
* transaction
,
2953 int64 object_store_id
,
2955 const IndexedDBKey
& key
,
2956 scoped_ptr
<IndexedDBKey
>* primary_key
) {
2957 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
2958 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2959 return InvalidDBKeyStatus();
2962 std::string found_encoded_primary_key
;
2963 leveldb::Status s
= FindKeyInIndex(transaction
,
2968 &found_encoded_primary_key
,
2971 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX
);
2976 if (!found_encoded_primary_key
.size()) {
2977 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX
);
2978 return InvalidDBKeyStatus();
2981 StringPiece
slice(found_encoded_primary_key
);
2982 if (DecodeIDBKey(&slice
, primary_key
) && slice
.empty())
2985 return InvalidDBKeyStatus();
2988 leveldb::Status
IndexedDBBackingStore::KeyExistsInIndex(
2989 IndexedDBBackingStore::Transaction
* transaction
,
2991 int64 object_store_id
,
2993 const IndexedDBKey
& index_key
,
2994 scoped_ptr
<IndexedDBKey
>* found_primary_key
,
2996 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
2997 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2998 return InvalidDBKeyStatus();
3001 std::string found_encoded_primary_key
;
3002 leveldb::Status s
= FindKeyInIndex(transaction
,
3007 &found_encoded_primary_key
,
3010 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX
);
3014 return leveldb::Status::OK();
3015 if (found_encoded_primary_key
.empty()) {
3016 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX
);
3017 return InvalidDBKeyStatus();
3020 StringPiece
slice(found_encoded_primary_key
);
3021 if (DecodeIDBKey(&slice
, found_primary_key
) && slice
.empty())
3024 return InvalidDBKeyStatus();
3027 IndexedDBBackingStore::Cursor::Cursor(
3028 const IndexedDBBackingStore::Cursor
* other
)
3029 : backing_store_(other
->backing_store_
),
3030 transaction_(other
->transaction_
),
3031 database_id_(other
->database_id_
),
3032 cursor_options_(other
->cursor_options_
),
3033 current_key_(new IndexedDBKey(*other
->current_key_
)) {
3034 if (other
->iterator_
) {
3035 iterator_
= transaction_
->transaction()->CreateIterator();
3037 if (other
->iterator_
->IsValid()) {
3038 leveldb::Status s
= iterator_
->Seek(other
->iterator_
->Key());
3039 // TODO(cmumford): Handle this error (crbug.com/363397)
3040 DCHECK(iterator_
->IsValid());
3045 IndexedDBBackingStore::Cursor::Cursor(
3046 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3047 IndexedDBBackingStore::Transaction
* transaction
,
3049 const CursorOptions
& cursor_options
)
3050 : backing_store_(backing_store
.get()),
3051 transaction_(transaction
),
3052 database_id_(database_id
),
3053 cursor_options_(cursor_options
) {
3055 IndexedDBBackingStore::Cursor::~Cursor() {}
3057 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status
* s
) {
3058 iterator_
= transaction_
->transaction()->CreateIterator();
3059 if (cursor_options_
.forward
)
3060 *s
= iterator_
->Seek(cursor_options_
.low_key
);
3062 *s
= iterator_
->Seek(cursor_options_
.high_key
);
3066 return Continue(0, READY
, s
);
3069 bool IndexedDBBackingStore::Cursor::Advance(uint32 count
, leveldb::Status
* s
) {
3070 *s
= leveldb::Status::OK();
3078 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey
* key
,
3079 const IndexedDBKey
* primary_key
,
3080 IteratorState next_state
,
3081 leveldb::Status
* s
) {
3082 DCHECK(!key
|| key
->IsValid());
3083 DCHECK(!primary_key
|| primary_key
->IsValid());
3084 *s
= leveldb::Status::OK();
3086 // TODO(alecflett): avoid a copy here?
3087 IndexedDBKey previous_key
= current_key_
? *current_key_
: IndexedDBKey();
3089 // When iterating with PrevNoDuplicate, spec requires that the
3090 // value we yield for each key is the first duplicate in forwards
3092 IndexedDBKey last_duplicate_key
;
3094 bool forward
= cursor_options_
.forward
;
3095 bool first_iteration_forward
= forward
;
3096 bool flipped
= false;
3099 if (next_state
== SEEK
) {
3100 // TODO(jsbell): Optimize seeking for reverse cursors as well.
3101 if (first_iteration_forward
&& key
) {
3102 first_iteration_forward
= false;
3103 std::string leveldb_key
;
3105 leveldb_key
= EncodeKey(*key
, *primary_key
);
3107 leveldb_key
= EncodeKey(*key
);
3109 *s
= iterator_
->Seek(leveldb_key
);
3110 } else if (forward
) {
3111 *s
= iterator_
->Next();
3113 *s
= iterator_
->Prev();
3118 next_state
= SEEK
; // for subsequent iterations
3121 if (!iterator_
->IsValid()) {
3122 if (!forward
&& last_duplicate_key
.IsValid()) {
3123 // We need to walk forward because we hit the end of
3133 if (IsPastBounds()) {
3134 if (!forward
&& last_duplicate_key
.IsValid()) {
3135 // We need to walk forward because now we're beyond the
3136 // bounds defined by the cursor.
3145 if (!HaveEnteredRange())
3148 // The row may not load because there's a stale entry in the
3149 // index. This is not fatal.
3150 if (!LoadCurrentRow())
3155 if (primary_key
&& current_key_
->Equals(*key
) &&
3156 this->primary_key().IsLessThan(*primary_key
))
3158 if (!flipped
&& current_key_
->IsLessThan(*key
))
3161 if (primary_key
&& key
->Equals(*current_key_
) &&
3162 primary_key
->IsLessThan(this->primary_key()))
3164 if (key
->IsLessThan(*current_key_
))
3169 if (cursor_options_
.unique
) {
3170 if (previous_key
.IsValid() && current_key_
->Equals(previous_key
)) {
3171 // We should never be able to walk forward all the way
3172 // to the previous key.
3173 DCHECK(!last_duplicate_key
.IsValid());
3178 if (!last_duplicate_key
.IsValid()) {
3179 last_duplicate_key
= *current_key_
;
3183 // We need to walk forward because we hit the boundary
3184 // between key ranges.
3185 if (!last_duplicate_key
.Equals(*current_key_
)) {
3197 DCHECK(!last_duplicate_key
.IsValid() ||
3198 (forward
&& last_duplicate_key
.Equals(*current_key_
)));
3202 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3203 if (cursor_options_
.forward
) {
3204 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.low_key
);
3205 if (cursor_options_
.low_open
) {
3208 return compare
>= 0;
3210 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.high_key
);
3211 if (cursor_options_
.high_open
) {
3214 return compare
<= 0;
3217 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
3218 if (cursor_options_
.forward
) {
3219 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.high_key
);
3220 if (cursor_options_
.high_open
) {
3221 return compare
>= 0;
3225 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.low_key
);
3226 if (cursor_options_
.low_open
) {
3227 return compare
<= 0;
3232 const IndexedDBKey
& IndexedDBBackingStore::Cursor::primary_key() const {
3233 return *current_key_
;
3236 const IndexedDBBackingStore::RecordIdentifier
&
3237 IndexedDBBackingStore::Cursor::record_identifier() const {
3238 return record_identifier_
;
3241 class ObjectStoreKeyCursorImpl
: public IndexedDBBackingStore::Cursor
{
3243 ObjectStoreKeyCursorImpl(
3244 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3245 IndexedDBBackingStore::Transaction
* transaction
,
3247 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3248 : IndexedDBBackingStore::Cursor(backing_store
,
3253 Cursor
* Clone() override
{ return new ObjectStoreKeyCursorImpl(this); }
3255 // IndexedDBBackingStore::Cursor
3256 IndexedDBValue
* value() override
{
3260 bool LoadCurrentRow() override
;
3263 std::string
EncodeKey(const IndexedDBKey
& key
) override
{
3264 return ObjectStoreDataKey::Encode(
3265 cursor_options_
.database_id
, cursor_options_
.object_store_id
, key
);
3267 std::string
EncodeKey(const IndexedDBKey
& key
,
3268 const IndexedDBKey
& primary_key
) override
{
3270 return std::string();
3274 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl
* other
)
3275 : IndexedDBBackingStore::Cursor(other
) {}
3277 DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl
);
3280 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
3281 StringPiece
slice(iterator_
->Key());
3282 ObjectStoreDataKey object_store_data_key
;
3283 if (!ObjectStoreDataKey::Decode(&slice
, &object_store_data_key
)) {
3284 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3288 current_key_
= object_store_data_key
.user_key();
3291 slice
= StringPiece(iterator_
->Value());
3292 if (!DecodeVarInt(&slice
, &version
)) {
3293 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3297 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3298 std::string encoded_key
;
3299 EncodeIDBKey(*current_key_
, &encoded_key
);
3300 record_identifier_
.Reset(encoded_key
, version
);
3305 class ObjectStoreCursorImpl
: public IndexedDBBackingStore::Cursor
{
3307 ObjectStoreCursorImpl(
3308 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3309 IndexedDBBackingStore::Transaction
* transaction
,
3311 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3312 : IndexedDBBackingStore::Cursor(backing_store
,
3317 Cursor
* Clone() override
{ return new ObjectStoreCursorImpl(this); }
3319 // IndexedDBBackingStore::Cursor
3320 IndexedDBValue
* value() override
{ return ¤t_value_
; }
3321 bool LoadCurrentRow() override
;
3324 std::string
EncodeKey(const IndexedDBKey
& key
) override
{
3325 return ObjectStoreDataKey::Encode(
3326 cursor_options_
.database_id
, cursor_options_
.object_store_id
, key
);
3328 std::string
EncodeKey(const IndexedDBKey
& key
,
3329 const IndexedDBKey
& primary_key
) override
{
3331 return std::string();
3335 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl
* other
)
3336 : IndexedDBBackingStore::Cursor(other
),
3337 current_value_(other
->current_value_
) {}
3339 IndexedDBValue current_value_
;
3341 DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl
);
3344 bool ObjectStoreCursorImpl::LoadCurrentRow() {
3345 StringPiece
key_slice(iterator_
->Key());
3346 ObjectStoreDataKey object_store_data_key
;
3347 if (!ObjectStoreDataKey::Decode(&key_slice
, &object_store_data_key
)) {
3348 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3352 current_key_
= object_store_data_key
.user_key();
3355 StringPiece value_slice
= StringPiece(iterator_
->Value());
3356 if (!DecodeVarInt(&value_slice
, &version
)) {
3357 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3361 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3362 std::string encoded_key
;
3363 EncodeIDBKey(*current_key_
, &encoded_key
);
3364 record_identifier_
.Reset(encoded_key
, version
);
3366 if (!transaction_
->GetBlobInfoForRecord(database_id_
,
3367 iterator_
->Key().as_string(),
3368 ¤t_value_
).ok()) {
3371 current_value_
.bits
= value_slice
.as_string();
3375 class IndexKeyCursorImpl
: public IndexedDBBackingStore::Cursor
{
3378 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3379 IndexedDBBackingStore::Transaction
* transaction
,
3381 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3382 : IndexedDBBackingStore::Cursor(backing_store
,
3387 Cursor
* Clone() override
{ return new IndexKeyCursorImpl(this); }
3389 // IndexedDBBackingStore::Cursor
3390 IndexedDBValue
* value() override
{
3394 const IndexedDBKey
& primary_key() const override
{ return *primary_key_
; }
3395 const IndexedDBBackingStore::RecordIdentifier
& record_identifier()
3398 return record_identifier_
;
3400 bool LoadCurrentRow() override
;
3403 std::string
EncodeKey(const IndexedDBKey
& key
) override
{
3404 return IndexDataKey::Encode(cursor_options_
.database_id
,
3405 cursor_options_
.object_store_id
,
3406 cursor_options_
.index_id
,
3409 std::string
EncodeKey(const IndexedDBKey
& key
,
3410 const IndexedDBKey
& primary_key
) override
{
3411 return IndexDataKey::Encode(cursor_options_
.database_id
,
3412 cursor_options_
.object_store_id
,
3413 cursor_options_
.index_id
,
3419 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl
* other
)
3420 : IndexedDBBackingStore::Cursor(other
),
3421 primary_key_(new IndexedDBKey(*other
->primary_key_
)) {}
3423 scoped_ptr
<IndexedDBKey
> primary_key_
;
3425 DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl
);
3428 bool IndexKeyCursorImpl::LoadCurrentRow() {
3429 StringPiece
slice(iterator_
->Key());
3430 IndexDataKey index_data_key
;
3431 if (!IndexDataKey::Decode(&slice
, &index_data_key
)) {
3432 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3436 current_key_
= index_data_key
.user_key();
3437 DCHECK(current_key_
);
3439 slice
= StringPiece(iterator_
->Value());
3440 int64 index_data_version
;
3441 if (!DecodeVarInt(&slice
, &index_data_version
)) {
3442 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3446 if (!DecodeIDBKey(&slice
, &primary_key_
) || !slice
.empty()) {
3447 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3451 std::string primary_leveldb_key
=
3452 ObjectStoreDataKey::Encode(index_data_key
.DatabaseId(),
3453 index_data_key
.ObjectStoreId(),
3459 transaction_
->transaction()->Get(primary_leveldb_key
, &result
, &found
);
3461 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3465 transaction_
->transaction()->Remove(iterator_
->Key());
3468 if (!result
.size()) {
3469 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3473 int64 object_store_data_version
;
3474 slice
= StringPiece(result
);
3475 if (!DecodeVarInt(&slice
, &object_store_data_version
)) {
3476 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3480 if (object_store_data_version
!= index_data_version
) {
3481 transaction_
->transaction()->Remove(iterator_
->Key());
3488 class IndexCursorImpl
: public IndexedDBBackingStore::Cursor
{
3491 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3492 IndexedDBBackingStore::Transaction
* transaction
,
3494 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3495 : IndexedDBBackingStore::Cursor(backing_store
,
3500 Cursor
* Clone() override
{ return new IndexCursorImpl(this); }
3502 // IndexedDBBackingStore::Cursor
3503 IndexedDBValue
* value() override
{ return ¤t_value_
; }
3504 const IndexedDBKey
& primary_key() const override
{ return *primary_key_
; }
3505 const IndexedDBBackingStore::RecordIdentifier
& record_identifier()
3508 return record_identifier_
;
3510 bool LoadCurrentRow() override
;
3513 std::string
EncodeKey(const IndexedDBKey
& key
) override
{
3514 return IndexDataKey::Encode(cursor_options_
.database_id
,
3515 cursor_options_
.object_store_id
,
3516 cursor_options_
.index_id
,
3519 std::string
EncodeKey(const IndexedDBKey
& key
,
3520 const IndexedDBKey
& primary_key
) override
{
3521 return IndexDataKey::Encode(cursor_options_
.database_id
,
3522 cursor_options_
.object_store_id
,
3523 cursor_options_
.index_id
,
3529 explicit IndexCursorImpl(const IndexCursorImpl
* other
)
3530 : IndexedDBBackingStore::Cursor(other
),
3531 primary_key_(new IndexedDBKey(*other
->primary_key_
)),
3532 current_value_(other
->current_value_
),
3533 primary_leveldb_key_(other
->primary_leveldb_key_
) {}
3535 scoped_ptr
<IndexedDBKey
> primary_key_
;
3536 IndexedDBValue current_value_
;
3537 std::string primary_leveldb_key_
;
3539 DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl
);
3542 bool IndexCursorImpl::LoadCurrentRow() {
3543 StringPiece
slice(iterator_
->Key());
3544 IndexDataKey index_data_key
;
3545 if (!IndexDataKey::Decode(&slice
, &index_data_key
)) {
3546 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3550 current_key_
= index_data_key
.user_key();
3551 DCHECK(current_key_
);
3553 slice
= StringPiece(iterator_
->Value());
3554 int64 index_data_version
;
3555 if (!DecodeVarInt(&slice
, &index_data_version
)) {
3556 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3559 if (!DecodeIDBKey(&slice
, &primary_key_
)) {
3560 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3564 DCHECK_EQ(index_data_key
.DatabaseId(), database_id_
);
3565 primary_leveldb_key_
=
3566 ObjectStoreDataKey::Encode(index_data_key
.DatabaseId(),
3567 index_data_key
.ObjectStoreId(),
3573 transaction_
->transaction()->Get(primary_leveldb_key_
, &result
, &found
);
3575 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3579 transaction_
->transaction()->Remove(iterator_
->Key());
3582 if (!result
.size()) {
3583 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3587 int64 object_store_data_version
;
3588 slice
= StringPiece(result
);
3589 if (!DecodeVarInt(&slice
, &object_store_data_version
)) {
3590 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3594 if (object_store_data_version
!= index_data_version
) {
3595 transaction_
->transaction()->Remove(iterator_
->Key());
3599 current_value_
.bits
= slice
.as_string();
3600 return transaction_
->GetBlobInfoForRecord(database_id_
,
3601 primary_leveldb_key_
,
3602 ¤t_value_
).ok();
3605 bool ObjectStoreCursorOptions(
3606 LevelDBTransaction
* transaction
,
3608 int64 object_store_id
,
3609 const IndexedDBKeyRange
& range
,
3610 blink::WebIDBCursorDirection direction
,
3611 IndexedDBBackingStore::Cursor::CursorOptions
* cursor_options
) {
3612 cursor_options
->database_id
= database_id
;
3613 cursor_options
->object_store_id
= object_store_id
;
3615 bool lower_bound
= range
.lower().IsValid();
3616 bool upper_bound
= range
.upper().IsValid();
3617 cursor_options
->forward
=
3618 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3619 direction
== blink::WebIDBCursorDirectionNext
);
3620 cursor_options
->unique
=
3621 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3622 direction
== blink::WebIDBCursorDirectionPrevNoDuplicate
);
3625 cursor_options
->low_key
=
3626 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MinIDBKey());
3627 cursor_options
->low_open
= true; // Not included.
3629 cursor_options
->low_key
=
3630 ObjectStoreDataKey::Encode(database_id
, object_store_id
, range
.lower());
3631 cursor_options
->low_open
= range
.lowerOpen();
3637 cursor_options
->high_key
=
3638 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MaxIDBKey());
3640 if (cursor_options
->forward
) {
3641 cursor_options
->high_open
= true; // Not included.
3643 // We need a key that exists.
3644 // TODO(cmumford): Handle this error (crbug.com/363397)
3645 if (!FindGreatestKeyLessThanOrEqual(transaction
,
3646 cursor_options
->high_key
,
3647 &cursor_options
->high_key
,
3650 cursor_options
->high_open
= false;
3653 cursor_options
->high_key
=
3654 ObjectStoreDataKey::Encode(database_id
, object_store_id
, range
.upper());
3655 cursor_options
->high_open
= range
.upperOpen();
3657 if (!cursor_options
->forward
) {
3658 // For reverse cursors, we need a key that exists.
3659 std::string found_high_key
;
3660 // TODO(cmumford): Handle this error (crbug.com/363397)
3661 if (!FindGreatestKeyLessThanOrEqual(
3662 transaction
, cursor_options
->high_key
, &found_high_key
, &s
))
3665 // If the target key should not be included, but we end up with a smaller
3666 // key, we should include that.
3667 if (cursor_options
->high_open
&&
3668 CompareIndexKeys(found_high_key
, cursor_options
->high_key
) < 0)
3669 cursor_options
->high_open
= false;
3671 cursor_options
->high_key
= found_high_key
;
3678 bool IndexCursorOptions(
3679 LevelDBTransaction
* transaction
,
3681 int64 object_store_id
,
3683 const IndexedDBKeyRange
& range
,
3684 blink::WebIDBCursorDirection direction
,
3685 IndexedDBBackingStore::Cursor::CursorOptions
* cursor_options
) {
3686 DCHECK(transaction
);
3687 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
3690 cursor_options
->database_id
= database_id
;
3691 cursor_options
->object_store_id
= object_store_id
;
3692 cursor_options
->index_id
= index_id
;
3694 bool lower_bound
= range
.lower().IsValid();
3695 bool upper_bound
= range
.upper().IsValid();
3696 cursor_options
->forward
=
3697 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3698 direction
== blink::WebIDBCursorDirectionNext
);
3699 cursor_options
->unique
=
3700 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3701 direction
== blink::WebIDBCursorDirectionPrevNoDuplicate
);
3704 cursor_options
->low_key
=
3705 IndexDataKey::EncodeMinKey(database_id
, object_store_id
, index_id
);
3706 cursor_options
->low_open
= false; // Included.
3708 cursor_options
->low_key
= IndexDataKey::Encode(
3709 database_id
, object_store_id
, index_id
, range
.lower());
3710 cursor_options
->low_open
= range
.lowerOpen();
3716 cursor_options
->high_key
=
3717 IndexDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
3718 cursor_options
->high_open
= false; // Included.
3720 if (!cursor_options
->forward
) { // We need a key that exists.
3721 if (!FindGreatestKeyLessThanOrEqual(transaction
,
3722 cursor_options
->high_key
,
3723 &cursor_options
->high_key
,
3726 cursor_options
->high_open
= false;
3729 cursor_options
->high_key
= IndexDataKey::Encode(
3730 database_id
, object_store_id
, index_id
, range
.upper());
3731 cursor_options
->high_open
= range
.upperOpen();
3733 std::string found_high_key
;
3734 // Seek to the *last* key in the set of non-unique keys
3735 // TODO(cmumford): Handle this error (crbug.com/363397)
3736 if (!FindGreatestKeyLessThanOrEqual(
3737 transaction
, cursor_options
->high_key
, &found_high_key
, &s
))
3740 // If the target key should not be included, but we end up with a smaller
3741 // key, we should include that.
3742 if (cursor_options
->high_open
&&
3743 CompareIndexKeys(found_high_key
, cursor_options
->high_key
) < 0)
3744 cursor_options
->high_open
= false;
3746 cursor_options
->high_key
= found_high_key
;
3752 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3753 IndexedDBBackingStore::OpenObjectStoreCursor(
3754 IndexedDBBackingStore::Transaction
* transaction
,
3756 int64 object_store_id
,
3757 const IndexedDBKeyRange
& range
,
3758 blink::WebIDBCursorDirection direction
,
3759 leveldb::Status
* s
) {
3760 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3761 *s
= leveldb::Status::OK();
3762 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3763 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3764 if (!ObjectStoreCursorOptions(leveldb_transaction
,
3770 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3771 scoped_ptr
<ObjectStoreCursorImpl
> cursor(new ObjectStoreCursorImpl(
3772 this, transaction
, database_id
, cursor_options
));
3773 if (!cursor
->FirstSeek(s
))
3774 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3776 return cursor
.Pass();
3779 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3780 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3781 IndexedDBBackingStore::Transaction
* transaction
,
3783 int64 object_store_id
,
3784 const IndexedDBKeyRange
& range
,
3785 blink::WebIDBCursorDirection direction
,
3786 leveldb::Status
* s
) {
3787 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3788 *s
= leveldb::Status::OK();
3789 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3790 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3791 if (!ObjectStoreCursorOptions(leveldb_transaction
,
3797 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3798 scoped_ptr
<ObjectStoreKeyCursorImpl
> cursor(new ObjectStoreKeyCursorImpl(
3799 this, transaction
, database_id
, cursor_options
));
3800 if (!cursor
->FirstSeek(s
))
3801 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3803 return cursor
.Pass();
3806 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3807 IndexedDBBackingStore::OpenIndexKeyCursor(
3808 IndexedDBBackingStore::Transaction
* transaction
,
3810 int64 object_store_id
,
3812 const IndexedDBKeyRange
& range
,
3813 blink::WebIDBCursorDirection direction
,
3814 leveldb::Status
* s
) {
3815 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3816 *s
= leveldb::Status::OK();
3817 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3818 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3819 if (!IndexCursorOptions(leveldb_transaction
,
3826 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3827 scoped_ptr
<IndexKeyCursorImpl
> cursor(
3828 new IndexKeyCursorImpl(this, transaction
, database_id
, cursor_options
));
3829 if (!cursor
->FirstSeek(s
))
3830 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3832 return cursor
.Pass();
3835 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3836 IndexedDBBackingStore::OpenIndexCursor(
3837 IndexedDBBackingStore::Transaction
* transaction
,
3839 int64 object_store_id
,
3841 const IndexedDBKeyRange
& range
,
3842 blink::WebIDBCursorDirection direction
,
3843 leveldb::Status
* s
) {
3844 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
3845 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3846 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3847 if (!IndexCursorOptions(leveldb_transaction
,
3854 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3855 scoped_ptr
<IndexCursorImpl
> cursor(
3856 new IndexCursorImpl(this, transaction
, database_id
, cursor_options
));
3857 if (!cursor
->FirstSeek(s
))
3858 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3860 return cursor
.Pass();
3863 IndexedDBBackingStore::Transaction::Transaction(
3864 IndexedDBBackingStore
* backing_store
)
3865 : backing_store_(backing_store
), database_id_(-1) {
3868 IndexedDBBackingStore::Transaction::~Transaction() {
3869 STLDeleteContainerPairSecondPointers(
3870 blob_change_map_
.begin(), blob_change_map_
.end());
3871 STLDeleteContainerPairSecondPointers(incognito_blob_map_
.begin(),
3872 incognito_blob_map_
.end());
3875 void IndexedDBBackingStore::Transaction::Begin() {
3876 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
3877 DCHECK(!transaction_
.get());
3878 transaction_
= IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3879 backing_store_
->db_
.get());
3881 // If incognito, this snapshots blobs just as the above transaction_
3882 // constructor snapshots the leveldb.
3883 for (const auto& iter
: backing_store_
->incognito_blob_map_
)
3884 incognito_blob_map_
[iter
.first
] = iter
.second
->Clone().release();
3887 static GURL
getURLFromUUID(const string
& uuid
) {
3888 return GURL("blob:uuid/" + uuid
);
3891 leveldb::Status
IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
3892 BlobEntryKeyValuePairVec
* new_blob_entries
,
3893 WriteDescriptorVec
* new_files_to_write
) {
3894 if (backing_store_
->is_incognito())
3895 return leveldb::Status::OK();
3897 new_blob_entries
->clear();
3898 new_files_to_write
->clear();
3899 if (!blob_change_map_
.empty()) {
3900 // Create LevelDBTransaction for the name generator seed and add-journal.
3901 scoped_refptr
<LevelDBTransaction
> pre_transaction
=
3902 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3903 backing_store_
->db_
.get());
3904 BlobJournalType journal
;
3905 for (auto& iter
: blob_change_map_
) {
3906 std::vector
<IndexedDBBlobInfo
*> new_blob_keys
;
3907 for (auto& entry
: iter
.second
->mutable_blob_info()) {
3908 int64 next_blob_key
= -1;
3909 bool result
= GetBlobKeyGeneratorCurrentNumber(
3910 pre_transaction
.get(), database_id_
, &next_blob_key
);
3911 if (!result
|| next_blob_key
< 0)
3912 return InternalInconsistencyStatus();
3913 BlobJournalEntryType journal_entry
=
3914 std::make_pair(database_id_
, next_blob_key
);
3915 journal
.push_back(journal_entry
);
3916 if (entry
.is_file()) {
3917 new_files_to_write
->push_back(
3918 WriteDescriptor(entry
.file_path(),
3921 entry
.last_modified()));
3923 new_files_to_write
->push_back(
3924 WriteDescriptor(getURLFromUUID(entry
.uuid()),
3928 entry
.set_key(next_blob_key
);
3929 new_blob_keys
.push_back(&entry
);
3930 result
= UpdateBlobKeyGeneratorCurrentNumber(
3931 pre_transaction
.get(), database_id_
, next_blob_key
+ 1);
3933 return InternalInconsistencyStatus();
3935 BlobEntryKey blob_entry_key
;
3936 StringPiece
key_piece(iter
.second
->key());
3937 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece
, &blob_entry_key
)) {
3939 return InternalInconsistencyStatus();
3941 new_blob_entries
->push_back(
3942 std::make_pair(blob_entry_key
, EncodeBlobData(new_blob_keys
)));
3944 UpdatePrimaryJournalWithBlobList(pre_transaction
.get(), journal
);
3945 leveldb::Status s
= pre_transaction
->Commit();
3947 return InternalInconsistencyStatus();
3949 return leveldb::Status::OK();
3952 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
3953 if (backing_store_
->is_incognito())
3956 // Look up all old files to remove as part of the transaction, store their
3957 // names in blobs_to_remove_, and remove their old blob data entries.
3958 for (const auto& iter
: blob_change_map_
) {
3959 BlobEntryKey blob_entry_key
;
3960 StringPiece
key_piece(iter
.second
->key());
3961 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece
, &blob_entry_key
)) {
3963 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
3964 transaction_
= NULL
;
3967 if (database_id_
< 0)
3968 database_id_
= blob_entry_key
.database_id();
3970 DCHECK_EQ(database_id_
, blob_entry_key
.database_id());
3971 std::string blob_entry_key_bytes
= blob_entry_key
.Encode();
3973 std::string blob_entry_value_bytes
;
3974 leveldb::Status s
= transaction_
->Get(
3975 blob_entry_key_bytes
, &blob_entry_value_bytes
, &found
);
3976 if (s
.ok() && found
) {
3977 std::vector
<IndexedDBBlobInfo
> blob_info
;
3978 if (!DecodeBlobData(blob_entry_value_bytes
, &blob_info
)) {
3979 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
3980 transaction_
= NULL
;
3983 for (const auto& blob
: blob_info
) {
3984 blobs_to_remove_
.push_back(std::make_pair(database_id_
, blob
.key()));
3985 transaction_
->Remove(blob_entry_key_bytes
);
3992 leveldb::Status
IndexedDBBackingStore::Transaction::SortBlobsToRemove() {
3993 IndexedDBActiveBlobRegistry
* registry
=
3994 backing_store_
->active_blob_registry();
3995 BlobJournalType primary_journal
, live_blob_journal
;
3996 for (const auto& iter
: blobs_to_remove_
) {
3997 if (registry
->MarkDeletedCheckIfUsed(iter
.first
, iter
.second
))
3998 live_blob_journal
.push_back(iter
);
4000 primary_journal
.push_back(iter
);
4002 UpdatePrimaryJournalWithBlobList(transaction_
.get(), primary_journal
);
4004 MergeBlobsIntoLiveBlobJournal(transaction_
.get(), live_blob_journal
);
4007 // To signal how many blobs need attention right now.
4008 blobs_to_remove_
.swap(primary_journal
);
4009 return leveldb::Status::OK();
4012 leveldb::Status
IndexedDBBackingStore::Transaction::CommitPhaseOne(
4013 scoped_refptr
<BlobWriteCallback
> callback
) {
4014 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
4015 DCHECK(transaction_
.get());
4016 DCHECK(backing_store_
->task_runner()->RunsTasksOnCurrentThread());
4020 s
= backing_store_
->CleanUpBlobJournal(BlobJournalKey::Encode());
4022 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD
);
4023 transaction_
= NULL
;
4027 BlobEntryKeyValuePairVec new_blob_entries
;
4028 WriteDescriptorVec new_files_to_write
;
4029 s
= HandleBlobPreTransaction(&new_blob_entries
, &new_files_to_write
);
4031 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4032 transaction_
= NULL
;
4036 DCHECK(!new_files_to_write
.size() ||
4037 KeyPrefix::IsValidDatabaseId(database_id_
));
4038 if (!CollectBlobFilesToRemove()) {
4039 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4040 transaction_
= NULL
;
4041 return InternalInconsistencyStatus();
4044 if (new_files_to_write
.size()) {
4045 // This kicks off the writes of the new blobs, if any.
4046 // This call will zero out new_blob_entries and new_files_to_write.
4047 WriteNewBlobs(&new_blob_entries
, &new_files_to_write
, callback
);
4048 // Remove the add journal, if any; once the blobs are written, and we
4049 // commit, this will do the cleanup.
4050 ClearBlobJournal(transaction_
.get(), BlobJournalKey::Encode());
4052 callback
->Run(true);
4055 return leveldb::Status::OK();
4058 leveldb::Status
IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
4059 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
4061 if (blobs_to_remove_
.size()) {
4062 s
= SortBlobsToRemove();
4064 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4065 transaction_
= NULL
;
4070 s
= transaction_
->Commit();
4071 transaction_
= NULL
;
4073 if (s
.ok() && backing_store_
->is_incognito() && !blob_change_map_
.empty()) {
4074 BlobChangeMap
& target_map
= backing_store_
->incognito_blob_map_
;
4075 for (auto& iter
: blob_change_map_
) {
4076 BlobChangeMap::iterator target_record
= target_map
.find(iter
.first
);
4077 if (target_record
!= target_map
.end()) {
4078 delete target_record
->second
;
4079 target_map
.erase(target_record
);
4082 target_map
[iter
.first
] = iter
.second
;
4088 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD
);
4089 else if (blobs_to_remove_
.size())
4090 s
= backing_store_
->CleanUpBlobJournal(BlobJournalKey::Encode());
4096 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
4097 : public IndexedDBBackingStore::BlobWriteCallback
{
4099 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction
* transaction
,
4100 scoped_refptr
<BlobWriteCallback
> callback
)
4101 : transaction_(transaction
), callback_(callback
) {}
4102 void Run(bool succeeded
) override
{
4103 callback_
->Run(succeeded
);
4104 if (succeeded
) // Else it's already been deleted during rollback.
4105 transaction_
->chained_blob_writer_
= NULL
;
4109 ~BlobWriteCallbackWrapper() override
{}
4110 friend class base::RefCounted
<IndexedDBBackingStore::BlobWriteCallback
>;
4112 IndexedDBBackingStore::Transaction
* transaction_
;
4113 scoped_refptr
<BlobWriteCallback
> callback_
;
4115 DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper
);
4118 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
4119 BlobEntryKeyValuePairVec
* new_blob_entries
,
4120 WriteDescriptorVec
* new_files_to_write
,
4121 scoped_refptr
<BlobWriteCallback
> callback
) {
4122 DCHECK_GT(new_files_to_write
->size(), 0UL);
4123 DCHECK_GT(database_id_
, 0);
4124 for (auto& blob_entry_iter
: *new_blob_entries
) {
4125 // Add the new blob-table entry for each blob to the main transaction, or
4126 // remove any entry that may exist if there's no new one.
4127 if (!blob_entry_iter
.second
.size())
4128 transaction_
->Remove(blob_entry_iter
.first
.Encode());
4130 transaction_
->Put(blob_entry_iter
.first
.Encode(),
4131 &blob_entry_iter
.second
);
4133 // Creating the writer will start it going asynchronously.
4134 chained_blob_writer_
=
4135 new ChainedBlobWriterImpl(database_id_
,
4138 new BlobWriteCallbackWrapper(this, callback
));
4141 void IndexedDBBackingStore::Transaction::Rollback() {
4142 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
4143 if (chained_blob_writer_
.get()) {
4144 chained_blob_writer_
->Abort();
4145 chained_blob_writer_
= NULL
;
4147 if (transaction_
.get() == NULL
)
4149 transaction_
->Rollback();
4150 transaction_
= NULL
;
4153 IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
4154 const std::string
& key
,
4155 int64 object_store_id
)
4156 : key_(key
), object_store_id_(object_store_id
) {
4159 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
4162 void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
4163 std::vector
<IndexedDBBlobInfo
>* blob_info
) {
4166 blob_info_
.swap(*blob_info
);
4169 void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
4170 ScopedVector
<storage::BlobDataHandle
>* handles
) {
4173 handles_
.swap(*handles
);
4176 scoped_ptr
<IndexedDBBackingStore::BlobChangeRecord
>
4177 IndexedDBBackingStore::BlobChangeRecord::Clone() const {
4178 scoped_ptr
<IndexedDBBackingStore::BlobChangeRecord
> record(
4179 new BlobChangeRecord(key_
, object_store_id_
));
4180 record
->blob_info_
= blob_info_
;
4182 for (const auto* handle
: handles_
)
4183 record
->handles_
.push_back(new storage::BlobDataHandle(*handle
));
4184 return record
.Pass();
4187 leveldb::Status
IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
4189 int64 object_store_id
,
4190 const std::string
& object_store_data_key
,
4191 std::vector
<IndexedDBBlobInfo
>* blob_info
,
4192 ScopedVector
<storage::BlobDataHandle
>* handles
) {
4193 if (!blob_info
|| blob_info
->empty()) {
4194 blob_change_map_
.erase(object_store_data_key
);
4195 incognito_blob_map_
.erase(object_store_data_key
);
4197 BlobEntryKey blob_entry_key
;
4198 StringPiece
leveldb_key_piece(object_store_data_key
);
4199 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece
,
4202 return InternalInconsistencyStatus();
4207 transaction()->Get(blob_entry_key
.Encode(), &value
, &found
);
4211 return leveldb::Status::OK();
4214 database_id
, object_store_id
, object_store_data_key
, blob_info
, handles
);
4215 return leveldb::Status::OK();
4218 // This is storing an info, even if empty, even if the previous key had no blob
4219 // info that we know of. It duplicates a bunch of information stored in the
4220 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
4221 // changes to exists or index keys here.
4222 void IndexedDBBackingStore::Transaction::PutBlobInfo(
4224 int64 object_store_id
,
4225 const std::string
& object_store_data_key
,
4226 std::vector
<IndexedDBBlobInfo
>* blob_info
,
4227 ScopedVector
<storage::BlobDataHandle
>* handles
) {
4228 DCHECK_GT(object_store_data_key
.size(), 0UL);
4229 if (database_id_
< 0)
4230 database_id_
= database_id
;
4231 DCHECK_EQ(database_id_
, database_id
);
4233 BlobChangeMap::iterator it
= blob_change_map_
.find(object_store_data_key
);
4234 BlobChangeRecord
* record
= NULL
;
4235 if (it
== blob_change_map_
.end()) {
4236 record
= new BlobChangeRecord(object_store_data_key
, object_store_id
);
4237 blob_change_map_
[object_store_data_key
] = record
;
4239 record
= it
->second
;
4241 DCHECK_EQ(record
->object_store_id(), object_store_id
);
4242 record
->SetBlobInfo(blob_info
);
4243 record
->SetHandles(handles
);
4244 DCHECK(!handles
|| !handles
->size());
4247 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4251 : is_file_(false), url_(url
), key_(key
), size_(size
) {
4254 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4255 const FilePath
& file_path
,
4258 base::Time last_modified
)
4260 file_path_(file_path
),
4263 last_modified_(last_modified
) {
4266 } // namespace content