1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/indexed_db/indexed_db_backing_store.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/format_macros.h"
12 #include "base/json/json_reader.h"
13 #include "base/json/json_writer.h"
14 #include "base/logging.h"
15 #include "base/metrics/histogram.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "content/browser/child_process_security_policy_impl.h"
20 #include "content/browser/indexed_db/indexed_db_blob_info.h"
21 #include "content/browser/indexed_db/indexed_db_class_factory.h"
22 #include "content/browser/indexed_db/indexed_db_database_error.h"
23 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
24 #include "content/browser/indexed_db/indexed_db_metadata.h"
25 #include "content/browser/indexed_db/indexed_db_tracing.h"
26 #include "content/browser/indexed_db/indexed_db_value.h"
27 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
28 #include "content/browser/indexed_db/leveldb/leveldb_database.h"
29 #include "content/browser/indexed_db/leveldb/leveldb_factory.h"
30 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
31 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
32 #include "content/common/indexed_db/indexed_db_key.h"
33 #include "content/common/indexed_db/indexed_db_key_path.h"
34 #include "content/common/indexed_db/indexed_db_key_range.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "net/url_request/url_request_context.h"
37 #include "storage/browser/blob/blob_data_handle.h"
38 #include "storage/browser/fileapi/file_stream_writer.h"
39 #include "storage/browser/fileapi/file_writer_delegate.h"
40 #include "storage/browser/fileapi/local_file_stream_writer.h"
41 #include "storage/common/database/database_identifier.h"
42 #include "storage/common/fileapi/file_system_mount_option.h"
43 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBTypes.h"
44 #include "third_party/WebKit/public/web/WebSerializedScriptValueVersion.h"
45 #include "third_party/leveldatabase/env_chromium.h"
48 using base::StringPiece
;
50 using storage::FileWriterDelegate
;
56 FilePath
GetBlobDirectoryName(const FilePath
& path_base
, int64 database_id
) {
57 return path_base
.AppendASCII(base::StringPrintf("%" PRIx64
, database_id
));
60 FilePath
GetBlobDirectoryNameForKey(const FilePath
& path_base
,
63 FilePath path
= GetBlobDirectoryName(path_base
, database_id
);
64 path
= path
.AppendASCII(base::StringPrintf(
65 "%02x", static_cast<int>(key
& 0x000000000000ff00) >> 8));
69 FilePath
GetBlobFileNameForKey(const FilePath
& path_base
,
72 FilePath path
= GetBlobDirectoryNameForKey(path_base
, database_id
, key
);
73 path
= path
.AppendASCII(base::StringPrintf("%" PRIx64
, key
));
77 bool MakeIDBBlobDirectory(const FilePath
& path_base
,
80 FilePath path
= GetBlobDirectoryNameForKey(path_base
, database_id
, key
);
81 return base::CreateDirectory(path
);
84 static std::string
ComputeOriginIdentifier(const GURL
& origin_url
) {
85 return storage::GetIdentifierFromOrigin(origin_url
) + "@1";
88 static base::FilePath
ComputeFileName(const GURL
& origin_url
) {
89 return base::FilePath()
90 .AppendASCII(storage::GetIdentifierFromOrigin(origin_url
))
91 .AddExtension(FILE_PATH_LITERAL(".indexeddb.leveldb"));
94 static base::FilePath
ComputeBlobPath(const GURL
& origin_url
) {
95 return base::FilePath()
96 .AppendASCII(storage::GetIdentifierFromOrigin(origin_url
))
97 .AddExtension(FILE_PATH_LITERAL(".indexeddb.blob"));
100 static base::FilePath
ComputeCorruptionFileName(const GURL
& origin_url
) {
101 return ComputeFileName(origin_url
)
102 .Append(FILE_PATH_LITERAL("corruption_info.json"));
107 static const int64 kKeyGeneratorInitialNumber
=
108 1; // From the IndexedDB specification.
110 enum IndexedDBBackingStoreErrorSource
{
111 // 0 - 2 are no longer used.
112 FIND_KEY_IN_INDEX
= 3,
113 GET_IDBDATABASE_METADATA
,
115 GET_KEY_GENERATOR_CURRENT_NUMBER
,
118 KEY_EXISTS_IN_OBJECT_STORE
,
121 GET_PRIMARY_KEY_VIA_INDEX
,
125 SET_MAX_OBJECT_STORE_ID
,
128 GET_NEW_VERSION_NUMBER
,
129 CREATE_IDBDATABASE_METADATA
,
131 TRANSACTION_COMMIT_METHOD
, // TRANSACTION_COMMIT is a WinNT.h macro
137 GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER
,
138 GET_BLOB_INFO_FOR_RECORD
,
142 static void RecordInternalError(const char* type
,
143 IndexedDBBackingStoreErrorSource location
) {
145 name
.append("WebCore.IndexedDB.BackingStore.").append(type
).append("Error");
146 base::Histogram::FactoryGet(name
,
149 INTERNAL_ERROR_MAX
+ 1,
150 base::HistogramBase::kUmaTargetedHistogramFlag
)
154 // Use to signal conditions caused by data corruption.
155 // A macro is used instead of an inline function so that the assert and log
156 // report the line number.
157 #define REPORT_ERROR(type, location) \
159 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
160 RecordInternalError(type, location); \
163 #define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location)
164 #define INTERNAL_CONSISTENCY_ERROR(location) \
165 REPORT_ERROR("Consistency", location)
166 #define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
168 // Use to signal conditions that usually indicate developer error, but
169 // could be caused by data corruption. A macro is used instead of an
170 // inline function so that the assert and log report the line number.
171 // TODO(cmumford): Improve test coverage so that all error conditions are
172 // "tested" and then delete this macro.
173 #define REPORT_ERROR_UNTESTED(type, location) \
175 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
177 RecordInternalError(type, location); \
180 #define INTERNAL_READ_ERROR_UNTESTED(location) \
181 REPORT_ERROR_UNTESTED("Read", location)
182 #define INTERNAL_CONSISTENCY_ERROR_UNTESTED(location) \
183 REPORT_ERROR_UNTESTED("Consistency", location)
184 #define INTERNAL_WRITE_ERROR_UNTESTED(location) \
185 REPORT_ERROR_UNTESTED("Write", location)
187 static void PutBool(LevelDBTransaction
* transaction
,
188 const StringPiece
& key
,
191 EncodeBool(value
, &buffer
);
192 transaction
->Put(key
, &buffer
);
195 // Was able to use LevelDB to read the data w/o error, but the data read was not
196 // in the expected format.
197 static leveldb::Status
InternalInconsistencyStatus() {
198 return leveldb::Status::Corruption("Internal inconsistency");
201 static leveldb::Status
InvalidDBKeyStatus() {
202 return leveldb::Status::InvalidArgument("Invalid database key ID");
205 static leveldb::Status
IOErrorStatus() {
206 return leveldb::Status::IOError("IO Error");
209 template <typename DBOrTransaction
>
210 static leveldb::Status
GetInt(DBOrTransaction
* db
,
211 const StringPiece
& key
,
215 leveldb::Status s
= db
->Get(key
, &result
, found
);
219 return leveldb::Status::OK();
220 StringPiece
slice(result
);
221 if (DecodeInt(&slice
, found_int
) && slice
.empty())
223 return InternalInconsistencyStatus();
226 static void PutInt(LevelDBTransaction
* transaction
,
227 const StringPiece
& key
,
231 EncodeInt(value
, &buffer
);
232 transaction
->Put(key
, &buffer
);
235 template <typename DBOrTransaction
>
236 WARN_UNUSED_RESULT
static leveldb::Status
GetVarInt(DBOrTransaction
* db
,
237 const StringPiece
& key
,
241 leveldb::Status s
= db
->Get(key
, &result
, found
);
245 return leveldb::Status::OK();
246 StringPiece
slice(result
);
247 if (DecodeVarInt(&slice
, found_int
) && slice
.empty())
249 return InternalInconsistencyStatus();
252 static void PutVarInt(LevelDBTransaction
* transaction
,
253 const StringPiece
& key
,
256 EncodeVarInt(value
, &buffer
);
257 transaction
->Put(key
, &buffer
);
260 template <typename DBOrTransaction
>
261 WARN_UNUSED_RESULT
static leveldb::Status
GetString(
263 const StringPiece
& key
,
264 base::string16
* found_string
,
268 leveldb::Status s
= db
->Get(key
, &result
, found
);
272 return leveldb::Status::OK();
273 StringPiece
slice(result
);
274 if (DecodeString(&slice
, found_string
) && slice
.empty())
276 return InternalInconsistencyStatus();
279 static void PutString(LevelDBTransaction
* transaction
,
280 const StringPiece
& key
,
281 const base::string16
& value
) {
283 EncodeString(value
, &buffer
);
284 transaction
->Put(key
, &buffer
);
287 static void PutIDBKeyPath(LevelDBTransaction
* transaction
,
288 const StringPiece
& key
,
289 const IndexedDBKeyPath
& value
) {
291 EncodeIDBKeyPath(value
, &buffer
);
292 transaction
->Put(key
, &buffer
);
295 static int CompareKeys(const StringPiece
& a
, const StringPiece
& b
) {
296 return Compare(a
, b
, false /*index_keys*/);
299 static int CompareIndexKeys(const StringPiece
& a
, const StringPiece
& b
) {
300 return Compare(a
, b
, true /*index_keys*/);
303 int IndexedDBBackingStore::Comparator::Compare(const StringPiece
& a
,
304 const StringPiece
& b
) const {
305 return content::Compare(a
, b
, false /*index_keys*/);
308 const char* IndexedDBBackingStore::Comparator::Name() const {
312 // 0 - Initial version.
313 // 1 - Adds UserIntVersion to DatabaseMetaData.
314 // 2 - Adds DataVersion to to global metadata.
315 // 3 - Adds metadata needed for blob support.
316 static const int64 kLatestKnownSchemaVersion
= 3;
317 WARN_UNUSED_RESULT
static bool IsSchemaKnown(LevelDBDatabase
* db
, bool* known
) {
318 int64 db_schema_version
= 0;
321 GetInt(db
, SchemaVersionKey::Encode(), &db_schema_version
, &found
);
328 if (db_schema_version
> kLatestKnownSchemaVersion
) {
333 const uint32 latest_known_data_version
=
334 blink::kSerializedScriptValueVersion
;
335 int64 db_data_version
= 0;
336 s
= GetInt(db
, DataVersionKey::Encode(), &db_data_version
, &found
);
344 if (db_data_version
> latest_known_data_version
) {
353 // TODO(ericu): Move this down into the member section of this file. I'm
354 // leaving it here for this CL as it's easier to see the diffs in place.
355 WARN_UNUSED_RESULT
leveldb::Status
IndexedDBBackingStore::SetUpMetadata() {
356 const uint32 latest_known_data_version
=
357 blink::kSerializedScriptValueVersion
;
358 const std::string schema_version_key
= SchemaVersionKey::Encode();
359 const std::string data_version_key
= DataVersionKey::Encode();
361 scoped_refptr
<LevelDBTransaction
> transaction
=
362 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
364 int64 db_schema_version
= 0;
365 int64 db_data_version
= 0;
368 GetInt(transaction
.get(), schema_version_key
, &db_schema_version
, &found
);
370 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
374 // Initialize new backing store.
375 db_schema_version
= kLatestKnownSchemaVersion
;
376 PutInt(transaction
.get(), schema_version_key
, db_schema_version
);
377 db_data_version
= latest_known_data_version
;
378 PutInt(transaction
.get(), data_version_key
, db_data_version
);
379 // If a blob directory already exists for this database, blow it away. It's
380 // leftover from a partially-purged previous generation of data.
381 if (!base::DeleteFile(blob_path_
, true)) {
382 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA
);
383 return IOErrorStatus();
386 // Upgrade old backing store.
387 DCHECK_LE(db_schema_version
, kLatestKnownSchemaVersion
);
388 if (db_schema_version
< 1) {
389 db_schema_version
= 1;
390 PutInt(transaction
.get(), schema_version_key
, db_schema_version
);
391 const std::string start_key
=
392 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_
);
393 const std::string stop_key
=
394 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_
);
395 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
396 for (s
= it
->Seek(start_key
);
397 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
399 int64 database_id
= 0;
401 s
= GetInt(transaction
.get(), it
->Key(), &database_id
, &found
);
403 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
407 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA
);
408 return InternalInconsistencyStatus();
410 std::string int_version_key
= DatabaseMetaDataKey::Encode(
411 database_id
, DatabaseMetaDataKey::USER_INT_VERSION
);
412 PutVarInt(transaction
.get(),
414 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
417 if (s
.ok() && db_schema_version
< 2) {
418 db_schema_version
= 2;
419 PutInt(transaction
.get(), schema_version_key
, db_schema_version
);
420 db_data_version
= blink::kSerializedScriptValueVersion
;
421 PutInt(transaction
.get(), data_version_key
, db_data_version
);
423 if (db_schema_version
< 3) {
424 db_schema_version
= 3;
425 if (!base::DeleteFile(blob_path_
, true)) {
426 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA
);
427 return IOErrorStatus();
433 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
437 // All new values will be written using this serialization version.
439 s
= GetInt(transaction
.get(), data_version_key
, &db_data_version
, &found
);
441 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
445 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA
);
446 return InternalInconsistencyStatus();
448 if (db_data_version
< latest_known_data_version
) {
449 db_data_version
= latest_known_data_version
;
450 PutInt(transaction
.get(), data_version_key
, db_data_version
);
453 DCHECK_EQ(db_schema_version
, kLatestKnownSchemaVersion
);
454 DCHECK_EQ(db_data_version
, latest_known_data_version
);
456 s
= transaction
->Commit();
458 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA
);
462 template <typename DBOrTransaction
>
463 WARN_UNUSED_RESULT
static leveldb::Status
GetMaxObjectStoreId(
466 int64
* max_object_store_id
) {
467 const std::string max_object_store_id_key
= DatabaseMetaDataKey::Encode(
468 database_id
, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID
);
469 return GetMaxObjectStoreId(db
, max_object_store_id_key
, max_object_store_id
);
472 template <typename DBOrTransaction
>
473 WARN_UNUSED_RESULT
static leveldb::Status
GetMaxObjectStoreId(
475 const std::string
& max_object_store_id_key
,
476 int64
* max_object_store_id
) {
477 *max_object_store_id
= -1;
480 GetInt(db
, max_object_store_id_key
, max_object_store_id
, &found
);
484 *max_object_store_id
= 0;
486 DCHECK_GE(*max_object_store_id
, 0);
490 class DefaultLevelDBFactory
: public LevelDBFactory
{
492 DefaultLevelDBFactory() {}
493 leveldb::Status
OpenLevelDB(const base::FilePath
& file_name
,
494 const LevelDBComparator
* comparator
,
495 scoped_ptr
<LevelDBDatabase
>* db
,
496 bool* is_disk_full
) override
{
497 return LevelDBDatabase::Open(file_name
, comparator
, db
, is_disk_full
);
499 leveldb::Status
DestroyLevelDB(const base::FilePath
& file_name
) override
{
500 return LevelDBDatabase::Destroy(file_name
);
504 DISALLOW_COPY_AND_ASSIGN(DefaultLevelDBFactory
);
507 static bool GetBlobKeyGeneratorCurrentNumber(
508 LevelDBTransaction
* leveldb_transaction
,
510 int64
* blob_key_generator_current_number
) {
511 const std::string key_gen_key
= DatabaseMetaDataKey::Encode(
512 database_id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
514 // Default to initial number if not found.
515 int64 cur_number
= DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber
;
519 bool ok
= leveldb_transaction
->Get(key_gen_key
, &data
, &found
).ok();
521 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
525 StringPiece
slice(data
);
526 if (!DecodeVarInt(&slice
, &cur_number
) || !slice
.empty() ||
527 !DatabaseMetaDataKey::IsValidBlobKey(cur_number
)) {
528 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
532 *blob_key_generator_current_number
= cur_number
;
536 static bool UpdateBlobKeyGeneratorCurrentNumber(
537 LevelDBTransaction
* leveldb_transaction
,
539 int64 blob_key_generator_current_number
) {
542 if (!GetBlobKeyGeneratorCurrentNumber(
543 leveldb_transaction
, database_id
, &old_number
))
545 DCHECK_LT(old_number
, blob_key_generator_current_number
);
548 DatabaseMetaDataKey::IsValidBlobKey(blob_key_generator_current_number
));
549 const std::string key
= DatabaseMetaDataKey::Encode(
550 database_id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
552 PutVarInt(leveldb_transaction
, key
, blob_key_generator_current_number
);
556 // TODO(ericu): Error recovery. If we persistently can't read the
557 // blob journal, the safe thing to do is to clear it and leak the blobs,
558 // though that may be costly. Still, database/directory deletion should always
559 // clean things up, and we can write an fsck that will do a full correction if
562 // Read and decode the specified blob journal via the supplied transaction.
563 // The key must be either the primary journal key or live journal key.
564 template <typename TransactionType
>
565 static leveldb::Status
GetBlobJournal(const StringPiece
& key
,
566 TransactionType
* transaction
,
567 BlobJournalType
* journal
) {
568 IDB_TRACE("IndexedDBBackingStore::GetBlobJournal");
571 leveldb::Status s
= transaction
->Get(key
, &data
, &found
);
573 INTERNAL_READ_ERROR(READ_BLOB_JOURNAL
);
577 if (!found
|| data
.empty())
578 return leveldb::Status::OK();
579 StringPiece
slice(data
);
580 if (!DecodeBlobJournal(&slice
, journal
)) {
581 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DECODE_BLOB_JOURNAL
);
582 s
= InternalInconsistencyStatus();
587 template <typename TransactionType
>
588 static leveldb::Status
GetPrimaryBlobJournal(TransactionType
* transaction
,
589 BlobJournalType
* journal
) {
590 return GetBlobJournal(BlobJournalKey::Encode(), transaction
, journal
);
593 template <typename TransactionType
>
594 static leveldb::Status
GetLiveBlobJournal(TransactionType
* transaction
,
595 BlobJournalType
* journal
) {
596 return GetBlobJournal(LiveBlobJournalKey::Encode(), transaction
, journal
);
599 // Clear the specified blob journal via the supplied transaction.
600 // The key must be either the primary journal key or live journal key.
601 template <typename TransactionType
>
602 static void ClearBlobJournal(TransactionType
* transaction
,
603 const std::string
& key
) {
604 transaction
->Remove(key
);
607 // Overwrite the specified blob journal via the supplied transaction.
608 // The key must be either the primary journal key or live journal key.
609 template <typename TransactionType
>
610 static void UpdateBlobJournal(TransactionType
* transaction
,
611 const std::string
& key
,
612 const BlobJournalType
& journal
) {
614 EncodeBlobJournal(journal
, &data
);
615 transaction
->Put(key
, &data
);
618 template <typename TransactionType
>
619 static void UpdatePrimaryBlobJournal(TransactionType
* transaction
,
620 const BlobJournalType
& journal
) {
621 UpdateBlobJournal(transaction
, BlobJournalKey::Encode(), journal
);
624 template <typename TransactionType
>
625 static void UpdateLiveBlobJournal(TransactionType
* transaction
,
626 const BlobJournalType
& journal
) {
627 UpdateBlobJournal(transaction
, LiveBlobJournalKey::Encode(), journal
);
630 // Append blobs to the specified blob journal via the supplied transaction.
631 // The key must be either the primary journal key or live journal key.
632 template <typename TransactionType
>
633 static leveldb::Status
AppendBlobsToBlobJournal(
634 TransactionType
* transaction
,
635 const std::string
& key
,
636 const BlobJournalType
& journal
) {
638 return leveldb::Status::OK();
639 BlobJournalType old_journal
;
640 leveldb::Status s
= GetBlobJournal(key
, transaction
, &old_journal
);
643 old_journal
.insert(old_journal
.end(), journal
.begin(), journal
.end());
644 UpdateBlobJournal(transaction
, key
, old_journal
);
645 return leveldb::Status::OK();
648 template <typename TransactionType
>
649 static leveldb::Status
AppendBlobsToPrimaryBlobJournal(
650 TransactionType
* transaction
,
651 const BlobJournalType
& journal
) {
652 return AppendBlobsToBlobJournal(transaction
, BlobJournalKey::Encode(),
656 template <typename TransactionType
>
657 static leveldb::Status
AppendBlobsToLiveBlobJournal(
658 TransactionType
* transaction
,
659 const BlobJournalType
& journal
) {
660 return AppendBlobsToBlobJournal(transaction
, LiveBlobJournalKey::Encode(),
664 // Append a database to the specified blob journal via the supplied transaction.
665 // The key must be either the primary journal key or live journal key.
666 static leveldb::Status
MergeDatabaseIntoBlobJournal(
667 LevelDBDirectTransaction
* transaction
,
668 const std::string
& key
,
670 IDB_TRACE("IndexedDBBackingStore::MergeDatabaseIntoBlobJournal");
671 BlobJournalType journal
;
672 leveldb::Status s
= GetBlobJournal(key
, transaction
, &journal
);
676 std::make_pair(database_id
, DatabaseMetaDataKey::kAllBlobsKey
));
677 UpdateBlobJournal(transaction
, key
, journal
);
678 return leveldb::Status::OK();
681 static leveldb::Status
MergeDatabaseIntoPrimaryBlobJournal(
682 LevelDBDirectTransaction
* leveldb_transaction
,
684 return MergeDatabaseIntoBlobJournal(leveldb_transaction
,
685 BlobJournalKey::Encode(), database_id
);
688 static leveldb::Status
MergeDatabaseIntoLiveBlobJournal(
689 LevelDBDirectTransaction
* leveldb_transaction
,
691 return MergeDatabaseIntoBlobJournal(
692 leveldb_transaction
, LiveBlobJournalKey::Encode(), database_id
);
695 // Blob Data is encoded as a series of:
696 // { is_file [bool], key [int64 as varInt],
697 // type [string-with-length, may be empty],
698 // (for Blobs only) size [int64 as varInt]
699 // (for Files only) fileName [string-with-length]
701 // There is no length field; just read until you run out of data.
702 static std::string
EncodeBlobData(
703 const std::vector
<IndexedDBBlobInfo
*>& blob_info
) {
705 for (const auto* info
: blob_info
) {
706 EncodeBool(info
->is_file(), &ret
);
707 EncodeVarInt(info
->key(), &ret
);
708 EncodeStringWithLength(info
->type(), &ret
);
710 EncodeStringWithLength(info
->file_name(), &ret
);
712 EncodeVarInt(info
->size(), &ret
);
717 static bool DecodeBlobData(const std::string
& data
,
718 std::vector
<IndexedDBBlobInfo
>* output
) {
719 std::vector
<IndexedDBBlobInfo
> ret
;
721 StringPiece
slice(data
);
722 while (!slice
.empty()) {
727 base::string16 file_name
;
729 if (!DecodeBool(&slice
, &is_file
))
731 if (!DecodeVarInt(&slice
, &key
) ||
732 !DatabaseMetaDataKey::IsValidBlobKey(key
))
734 if (!DecodeStringWithLength(&slice
, &type
))
737 if (!DecodeStringWithLength(&slice
, &file_name
))
739 ret
.push_back(IndexedDBBlobInfo(key
, type
, file_name
));
741 if (!DecodeVarInt(&slice
, &size
) || size
< 0)
743 ret
.push_back(IndexedDBBlobInfo(type
, static_cast<uint64
>(size
), key
));
751 IndexedDBBackingStore::IndexedDBBackingStore(
752 IndexedDBFactory
* indexed_db_factory
,
753 const GURL
& origin_url
,
754 const base::FilePath
& blob_path
,
755 net::URLRequestContext
* request_context
,
756 scoped_ptr
<LevelDBDatabase
> db
,
757 scoped_ptr
<LevelDBComparator
> comparator
,
758 base::SequencedTaskRunner
* task_runner
)
759 : indexed_db_factory_(indexed_db_factory
),
760 origin_url_(origin_url
),
761 blob_path_(blob_path
),
762 origin_identifier_(ComputeOriginIdentifier(origin_url
)),
763 request_context_(request_context
),
764 task_runner_(task_runner
),
766 comparator_(comparator
.Pass()),
767 active_blob_registry_(this),
768 committing_transaction_count_(0) {
771 IndexedDBBackingStore::~IndexedDBBackingStore() {
772 if (!blob_path_
.empty() && !child_process_ids_granted_
.empty()) {
773 ChildProcessSecurityPolicyImpl
* policy
=
774 ChildProcessSecurityPolicyImpl::GetInstance();
775 for (const auto& pid
: child_process_ids_granted_
)
776 policy
->RevokeAllPermissionsForFile(pid
, blob_path_
);
778 STLDeleteContainerPairSecondPointers(incognito_blob_map_
.begin(),
779 incognito_blob_map_
.end());
780 // db_'s destructor uses comparator_. The order of destruction is important.
785 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
786 const std::string
& primary_key
,
788 : primary_key_(primary_key
), version_(version
) {
789 DCHECK(!primary_key
.empty());
791 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
792 : primary_key_(), version_(-1) {}
793 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
795 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
796 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
798 // Values match entries in tools/metrics/histograms/histograms.xml
799 enum IndexedDBBackingStoreOpenResult
{
800 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS
,
801 INDEXED_DB_BACKING_STORE_OPEN_SUCCESS
,
802 INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY
,
803 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA
,
804 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED
,
805 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED
,
806 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS
,
807 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA
,
808 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR_DEPRECATED
,
809 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED
,
810 INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII
,
811 INDEXED_DB_BACKING_STORE_OPEN_DISK_FULL_DEPRECATED
,
812 INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG
,
813 INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY
,
814 INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION
,
815 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR
,
816 INDEXED_DB_BACKING_STORE_OPEN_MAX
,
820 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::Open(
821 IndexedDBFactory
* indexed_db_factory
,
822 const GURL
& origin_url
,
823 const base::FilePath
& path_base
,
824 net::URLRequestContext
* request_context
,
825 blink::WebIDBDataLoss
* data_loss
,
826 std::string
* data_loss_message
,
828 base::SequencedTaskRunner
* task_runner
,
830 leveldb::Status
* status
) {
831 *data_loss
= blink::WebIDBDataLossNone
;
832 DefaultLevelDBFactory leveldb_factory
;
833 return IndexedDBBackingStore::Open(indexed_db_factory
,
846 static std::string
OriginToCustomHistogramSuffix(const GURL
& origin_url
) {
847 if (origin_url
.host() == "docs.google.com")
849 return std::string();
852 static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result
,
853 const GURL
& origin_url
) {
854 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.BackingStore.OpenStatus",
856 INDEXED_DB_BACKING_STORE_OPEN_MAX
);
857 const std::string suffix
= OriginToCustomHistogramSuffix(origin_url
);
858 // Data from the WebCore.IndexedDB.BackingStore.OpenStatus histogram is used
859 // to generate a graph. So as not to alter the meaning of that graph,
860 // continue to collect all stats there (above) but also now collect docs stats
861 // separately (below).
862 if (!suffix
.empty()) {
863 base::LinearHistogram::FactoryGet(
864 "WebCore.IndexedDB.BackingStore.OpenStatus" + suffix
,
866 INDEXED_DB_BACKING_STORE_OPEN_MAX
,
867 INDEXED_DB_BACKING_STORE_OPEN_MAX
+ 1,
868 base::HistogramBase::kUmaTargetedHistogramFlag
)->Add(result
);
872 static bool IsPathTooLong(const base::FilePath
& leveldb_dir
) {
873 int limit
= base::GetMaximumPathComponentLength(leveldb_dir
.DirName());
875 DLOG(WARNING
) << "GetMaximumPathComponentLength returned -1";
876 // In limited testing, ChromeOS returns 143, other OSes 255.
877 #if defined(OS_CHROMEOS)
883 size_t component_length
= leveldb_dir
.BaseName().value().length();
884 if (component_length
> static_cast<uint32_t>(limit
)) {
885 DLOG(WARNING
) << "Path component length (" << component_length
886 << ") exceeds maximum (" << limit
887 << ") allowed by this filesystem.";
890 const int num_buckets
= 12;
891 UMA_HISTOGRAM_CUSTOM_COUNTS(
892 "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
902 leveldb::Status
IndexedDBBackingStore::DestroyBackingStore(
903 const base::FilePath
& path_base
,
904 const GURL
& origin_url
) {
905 const base::FilePath file_path
=
906 path_base
.Append(ComputeFileName(origin_url
));
907 DefaultLevelDBFactory leveldb_factory
;
908 return leveldb_factory
.DestroyLevelDB(file_path
);
911 bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath
& path_base
,
912 const GURL
& origin_url
,
913 std::string
* message
) {
914 const base::FilePath info_path
=
915 path_base
.Append(ComputeCorruptionFileName(origin_url
));
917 if (IsPathTooLong(info_path
))
920 const int64 max_json_len
= 4096;
922 if (!GetFileSize(info_path
, &file_size
) || file_size
> max_json_len
)
929 base::File
file(info_path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
930 bool success
= false;
931 if (file
.IsValid()) {
932 std::vector
<char> bytes(file_size
);
933 if (file_size
== file
.Read(0, &bytes
[0], file_size
)) {
934 std::string
input_js(&bytes
[0], file_size
);
935 base::JSONReader reader
;
936 scoped_ptr
<base::Value
> val(reader
.ReadToValue(input_js
));
937 if (val
&& val
->GetType() == base::Value::TYPE_DICTIONARY
) {
938 base::DictionaryValue
* dict_val
=
939 static_cast<base::DictionaryValue
*>(val
.get());
940 success
= dict_val
->GetString("message", message
);
946 base::DeleteFile(info_path
, false);
951 bool IndexedDBBackingStore::RecordCorruptionInfo(
952 const base::FilePath
& path_base
,
953 const GURL
& origin_url
,
954 const std::string
& message
) {
955 const base::FilePath info_path
=
956 path_base
.Append(ComputeCorruptionFileName(origin_url
));
957 if (IsPathTooLong(info_path
))
960 base::DictionaryValue root_dict
;
961 root_dict
.SetString("message", message
);
962 std::string output_js
;
963 base::JSONWriter::Write(root_dict
, &output_js
);
965 base::File
file(info_path
,
966 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
969 int written
= file
.Write(0, output_js
.c_str(), output_js
.length());
970 return size_t(written
) == output_js
.length();
974 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::Open(
975 IndexedDBFactory
* indexed_db_factory
,
976 const GURL
& origin_url
,
977 const base::FilePath
& path_base
,
978 net::URLRequestContext
* request_context
,
979 blink::WebIDBDataLoss
* data_loss
,
980 std::string
* data_loss_message
,
982 LevelDBFactory
* leveldb_factory
,
983 base::SequencedTaskRunner
* task_runner
,
985 leveldb::Status
* status
) {
986 IDB_TRACE("IndexedDBBackingStore::Open");
987 DCHECK(!path_base
.empty());
988 *data_loss
= blink::WebIDBDataLossNone
;
989 *data_loss_message
= "";
990 *is_disk_full
= false;
992 *status
= leveldb::Status::OK();
994 scoped_ptr
<LevelDBComparator
> comparator(new Comparator());
996 if (!base::IsStringASCII(path_base
.AsUTF8Unsafe())) {
997 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII
,
1000 if (!base::CreateDirectory(path_base
)) {
1002 leveldb::Status::IOError("Unable to create IndexedDB database path");
1003 LOG(ERROR
) << status
->ToString() << ": \"" << path_base
.AsUTF8Unsafe()
1005 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY
,
1007 return scoped_refptr
<IndexedDBBackingStore
>();
1010 const base::FilePath file_path
=
1011 path_base
.Append(ComputeFileName(origin_url
));
1012 const base::FilePath blob_path
=
1013 path_base
.Append(ComputeBlobPath(origin_url
));
1015 if (IsPathTooLong(file_path
)) {
1016 *status
= leveldb::Status::IOError("File path too long");
1017 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG
,
1019 return scoped_refptr
<IndexedDBBackingStore
>();
1022 scoped_ptr
<LevelDBDatabase
> db
;
1023 *status
= leveldb_factory
->OpenLevelDB(
1024 file_path
, comparator
.get(), &db
, is_disk_full
);
1026 DCHECK(!db
== !status
->ok());
1027 if (!status
->ok()) {
1028 if (leveldb_env::IndicatesDiskFull(*status
)) {
1029 *is_disk_full
= true;
1030 } else if (status
->IsCorruption()) {
1031 *data_loss
= blink::WebIDBDataLossTotal
;
1032 *data_loss_message
= leveldb_env::GetCorruptionMessage(*status
);
1036 bool is_schema_known
= false;
1038 std::string corruption_message
;
1039 if (ReadCorruptionInfo(path_base
, origin_url
, &corruption_message
)) {
1040 LOG(ERROR
) << "IndexedDB recovering from a corrupted (and deleted) "
1042 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION
,
1045 *data_loss
= blink::WebIDBDataLossTotal
;
1046 *data_loss_message
=
1047 "IndexedDB (database was corrupt): " + corruption_message
;
1048 } else if (!IsSchemaKnown(db
.get(), &is_schema_known
)) {
1049 LOG(ERROR
) << "IndexedDB had IO error checking schema, treating it as "
1051 HistogramOpenStatus(
1052 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA
,
1055 *data_loss
= blink::WebIDBDataLossTotal
;
1056 *data_loss_message
= "I/O error checking schema";
1057 } else if (!is_schema_known
) {
1058 LOG(ERROR
) << "IndexedDB backing store had unknown schema, treating it "
1059 "as failure to open";
1060 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA
,
1063 *data_loss
= blink::WebIDBDataLossTotal
;
1064 *data_loss_message
= "Unknown schema";
1068 DCHECK(status
->ok() || !is_schema_known
|| status
->IsIOError() ||
1069 status
->IsCorruption());
1072 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_SUCCESS
, origin_url
);
1073 } else if (status
->IsIOError()) {
1074 LOG(ERROR
) << "Unable to open backing store, not trying to recover - "
1075 << status
->ToString();
1076 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY
, origin_url
);
1077 return scoped_refptr
<IndexedDBBackingStore
>();
1079 DCHECK(!is_schema_known
|| status
->IsCorruption());
1080 LOG(ERROR
) << "IndexedDB backing store open failed, attempting cleanup";
1081 *status
= leveldb_factory
->DestroyLevelDB(file_path
);
1082 if (!status
->ok()) {
1083 LOG(ERROR
) << "IndexedDB backing store cleanup failed";
1084 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED
,
1086 return scoped_refptr
<IndexedDBBackingStore
>();
1089 LOG(ERROR
) << "IndexedDB backing store cleanup succeeded, reopening";
1090 leveldb_factory
->OpenLevelDB(file_path
, comparator
.get(), &db
, NULL
);
1092 LOG(ERROR
) << "IndexedDB backing store reopen after recovery failed";
1093 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED
,
1095 return scoped_refptr
<IndexedDBBackingStore
>();
1097 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS
,
1101 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
1102 Create(indexed_db_factory
,
1111 if (clean_journal
&& backing_store
.get() &&
1112 !backing_store
->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
1113 HistogramOpenStatus(
1114 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR
, origin_url
);
1115 return scoped_refptr
<IndexedDBBackingStore
>();
1117 return backing_store
;
1121 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::OpenInMemory(
1122 const GURL
& origin_url
,
1123 base::SequencedTaskRunner
* task_runner
,
1124 leveldb::Status
* status
) {
1125 DefaultLevelDBFactory leveldb_factory
;
1126 return IndexedDBBackingStore::OpenInMemory(
1127 origin_url
, &leveldb_factory
, task_runner
, status
);
1131 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::OpenInMemory(
1132 const GURL
& origin_url
,
1133 LevelDBFactory
* leveldb_factory
,
1134 base::SequencedTaskRunner
* task_runner
,
1135 leveldb::Status
* status
) {
1136 IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
1138 scoped_ptr
<LevelDBComparator
> comparator(new Comparator());
1139 scoped_ptr
<LevelDBDatabase
> db
=
1140 LevelDBDatabase::OpenInMemory(comparator
.get());
1142 LOG(ERROR
) << "LevelDBDatabase::OpenInMemory failed.";
1143 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED
,
1145 return scoped_refptr
<IndexedDBBackingStore
>();
1147 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS
, origin_url
);
1149 return Create(NULL
/* indexed_db_factory */,
1152 NULL
/* request_context */,
1160 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::Create(
1161 IndexedDBFactory
* indexed_db_factory
,
1162 const GURL
& origin_url
,
1163 const base::FilePath
& blob_path
,
1164 net::URLRequestContext
* request_context
,
1165 scoped_ptr
<LevelDBDatabase
> db
,
1166 scoped_ptr
<LevelDBComparator
> comparator
,
1167 base::SequencedTaskRunner
* task_runner
,
1168 leveldb::Status
* status
) {
1169 // TODO(jsbell): Handle comparator name changes.
1170 scoped_refptr
<IndexedDBBackingStore
> backing_store(
1171 new IndexedDBBackingStore(indexed_db_factory
,
1178 *status
= backing_store
->SetUpMetadata();
1180 return scoped_refptr
<IndexedDBBackingStore
>();
1182 return backing_store
;
1185 void IndexedDBBackingStore::GrantChildProcessPermissions(int child_process_id
) {
1186 if (!child_process_ids_granted_
.count(child_process_id
)) {
1187 child_process_ids_granted_
.insert(child_process_id
);
1188 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
1189 child_process_id
, blob_path_
);
1193 std::vector
<base::string16
> IndexedDBBackingStore::GetDatabaseNames(
1194 leveldb::Status
* s
) {
1195 *s
= leveldb::Status::OK();
1196 std::vector
<base::string16
> found_names
;
1197 const std::string start_key
=
1198 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_
);
1199 const std::string stop_key
=
1200 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_
);
1202 DCHECK(found_names
.empty());
1204 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
1205 for (*s
= it
->Seek(start_key
);
1206 s
->ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
1208 // Decode database name (in iterator key).
1209 StringPiece
slice(it
->Key());
1210 DatabaseNameKey database_name_key
;
1211 if (!DatabaseNameKey::Decode(&slice
, &database_name_key
) ||
1213 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES
);
1217 // Decode database id (in iterator value).
1218 int64 database_id
= 0;
1219 StringPiece
value_slice(it
->Value());
1220 if (!DecodeInt(&value_slice
, &database_id
) || !value_slice
.empty()) {
1221 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES
);
1225 // Look up version by id.
1227 int64 database_version
= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
;
1228 *s
= GetVarInt(db_
.get(),
1229 DatabaseMetaDataKey::Encode(
1230 database_id
, DatabaseMetaDataKey::USER_INT_VERSION
),
1233 if (!s
->ok() || !found
) {
1234 INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES
);
1238 // Ignore stale metadata from failed initial opens.
1239 if (database_version
!= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
)
1240 found_names
.push_back(database_name_key
.database_name());
1244 INTERNAL_READ_ERROR(GET_DATABASE_NAMES
);
1249 leveldb::Status
IndexedDBBackingStore::GetIDBDatabaseMetaData(
1250 const base::string16
& name
,
1251 IndexedDBDatabaseMetadata
* metadata
,
1253 const std::string key
= DatabaseNameKey::Encode(origin_identifier_
, name
);
1256 leveldb::Status s
= GetInt(db_
.get(), key
, &metadata
->id
, found
);
1258 INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA
);
1262 return leveldb::Status::OK();
1264 s
= GetString(db_
.get(),
1265 DatabaseMetaDataKey::Encode(metadata
->id
,
1266 DatabaseMetaDataKey::USER_VERSION
),
1270 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1274 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1275 return InternalInconsistencyStatus();
1278 s
= GetVarInt(db_
.get(),
1279 DatabaseMetaDataKey::Encode(
1280 metadata
->id
, DatabaseMetaDataKey::USER_INT_VERSION
),
1281 &metadata
->int_version
,
1284 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1288 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1289 return InternalInconsistencyStatus();
1292 if (metadata
->int_version
== IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
)
1293 metadata
->int_version
= IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1295 s
= GetMaxObjectStoreId(
1296 db_
.get(), metadata
->id
, &metadata
->max_object_store_id
);
1298 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1301 // We don't cache this, we just check it if it's there.
1302 int64 blob_key_generator_current_number
=
1303 DatabaseMetaDataKey::kInvalidBlobKey
;
1307 DatabaseMetaDataKey::Encode(
1308 metadata
->id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
),
1309 &blob_key_generator_current_number
,
1312 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1316 // This database predates blob support.
1318 } else if (!DatabaseMetaDataKey::IsValidBlobKey(
1319 blob_key_generator_current_number
)) {
1320 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1321 return InternalInconsistencyStatus();
1327 WARN_UNUSED_RESULT
static leveldb::Status
GetNewDatabaseId(
1328 LevelDBTransaction
* transaction
,
1331 int64 max_database_id
= -1;
1334 GetInt(transaction
, MaxDatabaseIdKey::Encode(), &max_database_id
, &found
);
1336 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID
);
1340 max_database_id
= 0;
1342 DCHECK_GE(max_database_id
, 0);
1344 int64 database_id
= max_database_id
+ 1;
1345 PutInt(transaction
, MaxDatabaseIdKey::Encode(), database_id
);
1346 *new_id
= database_id
;
1347 return leveldb::Status::OK();
1350 leveldb::Status
IndexedDBBackingStore::CreateIDBDatabaseMetaData(
1351 const base::string16
& name
,
1352 const base::string16
& version
,
1355 // TODO(jsbell): Don't persist metadata if open fails. http://crbug.com/395472
1356 scoped_refptr
<LevelDBTransaction
> transaction
=
1357 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
1359 leveldb::Status s
= GetNewDatabaseId(transaction
.get(), row_id
);
1362 DCHECK_GE(*row_id
, 0);
1364 if (int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
)
1365 int_version
= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
;
1367 PutInt(transaction
.get(),
1368 DatabaseNameKey::Encode(origin_identifier_
, name
),
1372 DatabaseMetaDataKey::Encode(*row_id
, DatabaseMetaDataKey::USER_VERSION
),
1374 PutVarInt(transaction
.get(),
1375 DatabaseMetaDataKey::Encode(*row_id
,
1376 DatabaseMetaDataKey::USER_INT_VERSION
),
1380 DatabaseMetaDataKey::Encode(
1381 *row_id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
),
1382 DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber
);
1384 s
= transaction
->Commit();
1386 INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA
);
1390 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
1391 IndexedDBBackingStore::Transaction
* transaction
,
1393 int64 int_version
) {
1394 if (int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
)
1395 int_version
= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
;
1396 DCHECK_GE(int_version
, 0) << "int_version was " << int_version
;
1397 PutVarInt(transaction
->transaction(),
1398 DatabaseMetaDataKey::Encode(row_id
,
1399 DatabaseMetaDataKey::USER_INT_VERSION
),
1404 // If you're deleting a range that contains user keys that have blob info, this
1405 // won't clean up the blobs.
1406 static leveldb::Status
DeleteRangeBasic(LevelDBTransaction
* transaction
,
1407 const std::string
& begin
,
1408 const std::string
& end
,
1410 scoped_ptr
<LevelDBIterator
> it
= transaction
->CreateIterator();
1412 for (s
= it
->Seek(begin
); s
.ok() && it
->IsValid() &&
1413 (upper_open
? CompareKeys(it
->Key(), end
) < 0
1414 : CompareKeys(it
->Key(), end
) <= 0);
1416 transaction
->Remove(it
->Key());
1420 static leveldb::Status
DeleteBlobsInRange(
1421 IndexedDBBackingStore::Transaction
* transaction
,
1423 int64 object_store_id
,
1424 const std::string
& start_key
,
1425 const std::string
& end_key
,
1427 scoped_ptr
<LevelDBIterator
> it
= transaction
->transaction()->CreateIterator();
1428 leveldb::Status s
= it
->Seek(start_key
);
1429 for (; s
.ok() && it
->IsValid() &&
1430 (upper_open
? CompareKeys(it
->Key(), end_key
) < 0
1431 : CompareKeys(it
->Key(), end_key
) <= 0);
1433 StringPiece
key_piece(it
->Key());
1434 std::string user_key
=
1435 BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece
);
1436 if (!user_key
.size()) {
1437 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1438 return InternalInconsistencyStatus();
1440 transaction
->PutBlobInfo(
1441 database_id
, object_store_id
, user_key
, NULL
, NULL
);
1446 static leveldb::Status
DeleteBlobsInObjectStore(
1447 IndexedDBBackingStore::Transaction
* transaction
,
1449 int64 object_store_id
) {
1450 std::string start_key
, stop_key
;
1452 BlobEntryKey::EncodeMinKeyForObjectStore(database_id
, object_store_id
);
1454 BlobEntryKey::EncodeStopKeyForObjectStore(database_id
, object_store_id
);
1455 return DeleteBlobsInRange(
1456 transaction
, database_id
, object_store_id
, start_key
, stop_key
, true);
1459 leveldb::Status
IndexedDBBackingStore::DeleteDatabase(
1460 const base::string16
& name
) {
1461 IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
1462 scoped_ptr
<LevelDBDirectTransaction
> transaction
=
1463 LevelDBDirectTransaction::Create(db_
.get());
1467 IndexedDBDatabaseMetadata metadata
;
1468 bool success
= false;
1469 s
= GetIDBDatabaseMetaData(name
, &metadata
, &success
);
1473 return leveldb::Status::OK();
1475 const std::string start_key
= DatabaseMetaDataKey::Encode(
1476 metadata
.id
, DatabaseMetaDataKey::ORIGIN_NAME
);
1477 const std::string stop_key
= DatabaseMetaDataKey::Encode(
1478 metadata
.id
+ 1, DatabaseMetaDataKey::ORIGIN_NAME
);
1480 IDB_TRACE("IndexedDBBackingStore::DeleteDatabase.DeleteEntries");
1481 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
1482 for (s
= it
->Seek(start_key
);
1483 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
1485 transaction
->Remove(it
->Key());
1488 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE
);
1492 const std::string key
= DatabaseNameKey::Encode(origin_identifier_
, name
);
1493 transaction
->Remove(key
);
1495 bool need_cleanup
= false;
1496 if (active_blob_registry()->MarkDeletedCheckIfUsed(
1497 metadata
.id
, DatabaseMetaDataKey::kAllBlobsKey
)) {
1498 s
= MergeDatabaseIntoLiveBlobJournal(transaction
.get(), metadata
.id
);
1502 s
= MergeDatabaseIntoPrimaryBlobJournal(transaction
.get(), metadata
.id
);
1505 need_cleanup
= true;
1508 s
= transaction
->Commit();
1510 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE
);
1514 // If another transaction is running, this will defer processing
1515 // the journal until completion.
1517 CleanPrimaryJournalIgnoreReturn();
1519 db_
->Compact(start_key
, stop_key
);
1523 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator
* it
,
1524 const std::string
& stop_key
,
1525 int64 object_store_id
,
1526 int64 meta_data_type
) {
1527 if (!it
->IsValid() || CompareKeys(it
->Key(), stop_key
) >= 0)
1530 StringPiece
slice(it
->Key());
1531 ObjectStoreMetaDataKey meta_data_key
;
1533 ObjectStoreMetaDataKey::Decode(&slice
, &meta_data_key
) && slice
.empty();
1535 if (meta_data_key
.ObjectStoreId() != object_store_id
)
1537 if (meta_data_key
.MetaDataType() != meta_data_type
)
1542 // TODO(jsbell): This should do some error handling rather than
1543 // plowing ahead when bad data is encountered.
1544 leveldb::Status
IndexedDBBackingStore::GetObjectStores(
1546 IndexedDBDatabaseMetadata::ObjectStoreMap
* object_stores
) {
1547 IDB_TRACE("IndexedDBBackingStore::GetObjectStores");
1548 if (!KeyPrefix::IsValidDatabaseId(database_id
))
1549 return InvalidDBKeyStatus();
1550 const std::string start_key
=
1551 ObjectStoreMetaDataKey::Encode(database_id
, 1, 0);
1552 const std::string stop_key
=
1553 ObjectStoreMetaDataKey::EncodeMaxKey(database_id
);
1555 DCHECK(object_stores
->empty());
1557 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
1558 leveldb::Status s
= it
->Seek(start_key
);
1559 while (s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0) {
1560 StringPiece
slice(it
->Key());
1561 ObjectStoreMetaDataKey meta_data_key
;
1563 ObjectStoreMetaDataKey::Decode(&slice
, &meta_data_key
) && slice
.empty();
1565 if (!ok
|| meta_data_key
.MetaDataType() != ObjectStoreMetaDataKey::NAME
) {
1566 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1567 // Possible stale metadata, but don't fail the load.
1574 int64 object_store_id
= meta_data_key
.ObjectStoreId();
1576 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
1578 base::string16 object_store_name
;
1580 StringPiece
slice(it
->Value());
1581 if (!DecodeString(&slice
, &object_store_name
) || !slice
.empty())
1582 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1588 if (!CheckObjectStoreAndMetaDataType(it
.get(),
1591 ObjectStoreMetaDataKey::KEY_PATH
)) {
1592 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1595 IndexedDBKeyPath key_path
;
1597 StringPiece
slice(it
->Value());
1598 if (!DecodeIDBKeyPath(&slice
, &key_path
) || !slice
.empty())
1599 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1605 if (!CheckObjectStoreAndMetaDataType(
1609 ObjectStoreMetaDataKey::AUTO_INCREMENT
)) {
1610 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1613 bool auto_increment
;
1615 StringPiece
slice(it
->Value());
1616 if (!DecodeBool(&slice
, &auto_increment
) || !slice
.empty())
1617 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1620 s
= it
->Next(); // Is evictable.
1623 if (!CheckObjectStoreAndMetaDataType(it
.get(),
1626 ObjectStoreMetaDataKey::EVICTABLE
)) {
1627 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1631 s
= it
->Next(); // Last version.
1634 if (!CheckObjectStoreAndMetaDataType(
1638 ObjectStoreMetaDataKey::LAST_VERSION
)) {
1639 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1643 s
= it
->Next(); // Maximum index id allocated.
1646 if (!CheckObjectStoreAndMetaDataType(
1650 ObjectStoreMetaDataKey::MAX_INDEX_ID
)) {
1651 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1656 StringPiece
slice(it
->Value());
1657 if (!DecodeInt(&slice
, &max_index_id
) || !slice
.empty())
1658 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1661 s
= it
->Next(); // [optional] has key path (is not null)
1664 if (CheckObjectStoreAndMetaDataType(it
.get(),
1667 ObjectStoreMetaDataKey::HAS_KEY_PATH
)) {
1670 StringPiece
slice(it
->Value());
1671 if (!DecodeBool(&slice
, &has_key_path
))
1672 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1674 // This check accounts for two layers of legacy coding:
1675 // (1) Initially, has_key_path was added to distinguish null vs. string.
1676 // (2) Later, null vs. string vs. array was stored in the key_path itself.
1677 // So this check is only relevant for string-type key_paths.
1678 if (!has_key_path
&&
1679 (key_path
.type() == blink::WebIDBKeyPathTypeString
&&
1680 !key_path
.string().empty())) {
1681 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1685 key_path
= IndexedDBKeyPath();
1691 int64 key_generator_current_number
= -1;
1692 if (CheckObjectStoreAndMetaDataType(
1696 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
)) {
1697 StringPiece
slice(it
->Value());
1698 if (!DecodeInt(&slice
, &key_generator_current_number
) || !slice
.empty())
1699 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1701 // TODO(jsbell): Return key_generator_current_number, cache in
1702 // object store, and write lazily to backing store. For now,
1703 // just assert that if it was written it was valid.
1704 DCHECK_GE(key_generator_current_number
, kKeyGeneratorInitialNumber
);
1710 IndexedDBObjectStoreMetadata
metadata(object_store_name
,
1715 s
= GetIndexes(database_id
, object_store_id
, &metadata
.indexes
);
1718 (*object_stores
)[object_store_id
] = metadata
;
1722 INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES
);
1727 WARN_UNUSED_RESULT
static leveldb::Status
SetMaxObjectStoreId(
1728 LevelDBTransaction
* transaction
,
1730 int64 object_store_id
) {
1731 const std::string max_object_store_id_key
= DatabaseMetaDataKey::Encode(
1732 database_id
, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID
);
1733 int64 max_object_store_id
= -1;
1734 leveldb::Status s
= GetMaxObjectStoreId(
1735 transaction
, max_object_store_id_key
, &max_object_store_id
);
1737 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID
);
1741 if (object_store_id
<= max_object_store_id
) {
1742 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID
);
1743 return InternalInconsistencyStatus();
1745 PutInt(transaction
, max_object_store_id_key
, object_store_id
);
1749 void IndexedDBBackingStore::Compact() { db_
->CompactAll(); }
1751 leveldb::Status
IndexedDBBackingStore::CreateObjectStore(
1752 IndexedDBBackingStore::Transaction
* transaction
,
1754 int64 object_store_id
,
1755 const base::string16
& name
,
1756 const IndexedDBKeyPath
& key_path
,
1757 bool auto_increment
) {
1758 IDB_TRACE("IndexedDBBackingStore::CreateObjectStore");
1759 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1760 return InvalidDBKeyStatus();
1761 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1763 SetMaxObjectStoreId(leveldb_transaction
, database_id
, object_store_id
);
1767 const std::string name_key
= ObjectStoreMetaDataKey::Encode(
1768 database_id
, object_store_id
, ObjectStoreMetaDataKey::NAME
);
1769 const std::string key_path_key
= ObjectStoreMetaDataKey::Encode(
1770 database_id
, object_store_id
, ObjectStoreMetaDataKey::KEY_PATH
);
1771 const std::string auto_increment_key
= ObjectStoreMetaDataKey::Encode(
1772 database_id
, object_store_id
, ObjectStoreMetaDataKey::AUTO_INCREMENT
);
1773 const std::string evictable_key
= ObjectStoreMetaDataKey::Encode(
1774 database_id
, object_store_id
, ObjectStoreMetaDataKey::EVICTABLE
);
1775 const std::string last_version_key
= ObjectStoreMetaDataKey::Encode(
1776 database_id
, object_store_id
, ObjectStoreMetaDataKey::LAST_VERSION
);
1777 const std::string max_index_id_key
= ObjectStoreMetaDataKey::Encode(
1778 database_id
, object_store_id
, ObjectStoreMetaDataKey::MAX_INDEX_ID
);
1779 const std::string has_key_path_key
= ObjectStoreMetaDataKey::Encode(
1780 database_id
, object_store_id
, ObjectStoreMetaDataKey::HAS_KEY_PATH
);
1781 const std::string key_generator_current_number_key
=
1782 ObjectStoreMetaDataKey::Encode(
1785 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
1786 const std::string names_key
= ObjectStoreNamesKey::Encode(database_id
, name
);
1788 PutString(leveldb_transaction
, name_key
, name
);
1789 PutIDBKeyPath(leveldb_transaction
, key_path_key
, key_path
);
1790 PutInt(leveldb_transaction
, auto_increment_key
, auto_increment
);
1791 PutInt(leveldb_transaction
, evictable_key
, false);
1792 PutInt(leveldb_transaction
, last_version_key
, 1);
1793 PutInt(leveldb_transaction
, max_index_id_key
, kMinimumIndexId
);
1794 PutBool(leveldb_transaction
, has_key_path_key
, !key_path
.IsNull());
1795 PutInt(leveldb_transaction
,
1796 key_generator_current_number_key
,
1797 kKeyGeneratorInitialNumber
);
1798 PutInt(leveldb_transaction
, names_key
, object_store_id
);
1802 leveldb::Status
IndexedDBBackingStore::DeleteObjectStore(
1803 IndexedDBBackingStore::Transaction
* transaction
,
1805 int64 object_store_id
) {
1806 IDB_TRACE("IndexedDBBackingStore::DeleteObjectStore");
1807 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1808 return InvalidDBKeyStatus();
1809 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1811 base::string16 object_store_name
;
1814 GetString(leveldb_transaction
,
1815 ObjectStoreMetaDataKey::Encode(
1816 database_id
, object_store_id
, ObjectStoreMetaDataKey::NAME
),
1820 INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1824 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1825 return InternalInconsistencyStatus();
1828 s
= DeleteBlobsInObjectStore(transaction
, database_id
, object_store_id
);
1830 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1834 s
= DeleteRangeBasic(
1835 leveldb_transaction
,
1836 ObjectStoreMetaDataKey::Encode(database_id
, object_store_id
, 0),
1837 ObjectStoreMetaDataKey::EncodeMaxKey(database_id
, object_store_id
),
1841 leveldb_transaction
->Remove(
1842 ObjectStoreNamesKey::Encode(database_id
, object_store_name
));
1844 s
= DeleteRangeBasic(
1845 leveldb_transaction
,
1846 IndexFreeListKey::Encode(database_id
, object_store_id
, 0),
1847 IndexFreeListKey::EncodeMaxKey(database_id
, object_store_id
),
1852 s
= DeleteRangeBasic(
1853 leveldb_transaction
,
1854 IndexMetaDataKey::Encode(database_id
, object_store_id
, 0, 0),
1855 IndexMetaDataKey::EncodeMaxKey(database_id
, object_store_id
),
1860 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1864 return ClearObjectStore(transaction
, database_id
, object_store_id
);
1867 leveldb::Status
IndexedDBBackingStore::GetRecord(
1868 IndexedDBBackingStore::Transaction
* transaction
,
1870 int64 object_store_id
,
1871 const IndexedDBKey
& key
,
1872 IndexedDBValue
* record
) {
1873 IDB_TRACE("IndexedDBBackingStore::GetRecord");
1874 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1875 return InvalidDBKeyStatus();
1876 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1878 const std::string leveldb_key
=
1879 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
1885 leveldb::Status s
= leveldb_transaction
->Get(leveldb_key
, &data
, &found
);
1887 INTERNAL_READ_ERROR(GET_RECORD
);
1893 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD
);
1894 return leveldb::Status::NotFound("Record contained no data");
1898 StringPiece
slice(data
);
1899 if (!DecodeVarInt(&slice
, &version
)) {
1900 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD
);
1901 return InternalInconsistencyStatus();
1904 record
->bits
= slice
.as_string();
1905 return transaction
->GetBlobInfoForRecord(database_id
, leveldb_key
, record
);
1908 WARN_UNUSED_RESULT
static leveldb::Status
GetNewVersionNumber(
1909 LevelDBTransaction
* transaction
,
1911 int64 object_store_id
,
1912 int64
* new_version_number
) {
1913 const std::string last_version_key
= ObjectStoreMetaDataKey::Encode(
1914 database_id
, object_store_id
, ObjectStoreMetaDataKey::LAST_VERSION
);
1916 *new_version_number
= -1;
1917 int64 last_version
= -1;
1920 GetInt(transaction
, last_version_key
, &last_version
, &found
);
1922 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER
);
1928 DCHECK_GE(last_version
, 0);
1930 int64 version
= last_version
+ 1;
1931 PutInt(transaction
, last_version_key
, version
);
1933 // TODO(jsbell): Think about how we want to handle the overflow scenario.
1934 DCHECK(version
> last_version
);
1936 *new_version_number
= version
;
1940 leveldb::Status
IndexedDBBackingStore::PutRecord(
1941 IndexedDBBackingStore::Transaction
* transaction
,
1943 int64 object_store_id
,
1944 const IndexedDBKey
& key
,
1945 IndexedDBValue
* value
,
1946 ScopedVector
<storage::BlobDataHandle
>* handles
,
1947 RecordIdentifier
* record_identifier
) {
1948 IDB_TRACE("IndexedDBBackingStore::PutRecord");
1949 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1950 return InvalidDBKeyStatus();
1951 DCHECK(key
.IsValid());
1953 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1955 leveldb::Status s
= GetNewVersionNumber(
1956 leveldb_transaction
, database_id
, object_store_id
, &version
);
1959 DCHECK_GE(version
, 0);
1960 const std::string object_store_data_key
=
1961 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
1964 EncodeVarInt(version
, &v
);
1965 v
.append(value
->bits
);
1967 leveldb_transaction
->Put(object_store_data_key
, &v
);
1968 s
= transaction
->PutBlobInfoIfNeeded(database_id
,
1970 object_store_data_key
,
1975 DCHECK(!handles
->size());
1977 const std::string exists_entry_key
=
1978 ExistsEntryKey::Encode(database_id
, object_store_id
, key
);
1979 std::string version_encoded
;
1980 EncodeInt(version
, &version_encoded
);
1981 leveldb_transaction
->Put(exists_entry_key
, &version_encoded
);
1983 std::string key_encoded
;
1984 EncodeIDBKey(key
, &key_encoded
);
1985 record_identifier
->Reset(key_encoded
, version
);
1989 leveldb::Status
IndexedDBBackingStore::ClearObjectStore(
1990 IndexedDBBackingStore::Transaction
* transaction
,
1992 int64 object_store_id
) {
1993 IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
1994 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1995 return InvalidDBKeyStatus();
1996 const std::string start_key
=
1997 KeyPrefix(database_id
, object_store_id
).Encode();
1998 const std::string stop_key
=
1999 KeyPrefix(database_id
, object_store_id
+ 1).Encode();
2002 DeleteRangeBasic(transaction
->transaction(), start_key
, stop_key
, true);
2004 INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE
);
2007 return DeleteBlobsInObjectStore(transaction
, database_id
, object_store_id
);
2010 leveldb::Status
IndexedDBBackingStore::DeleteRecord(
2011 IndexedDBBackingStore::Transaction
* transaction
,
2013 int64 object_store_id
,
2014 const RecordIdentifier
& record_identifier
) {
2015 IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
2016 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2017 return InvalidDBKeyStatus();
2018 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2020 const std::string object_store_data_key
= ObjectStoreDataKey::Encode(
2021 database_id
, object_store_id
, record_identifier
.primary_key());
2022 leveldb_transaction
->Remove(object_store_data_key
);
2023 leveldb::Status s
= transaction
->PutBlobInfoIfNeeded(
2024 database_id
, object_store_id
, object_store_data_key
, NULL
, NULL
);
2028 const std::string exists_entry_key
= ExistsEntryKey::Encode(
2029 database_id
, object_store_id
, record_identifier
.primary_key());
2030 leveldb_transaction
->Remove(exists_entry_key
);
2031 return leveldb::Status::OK();
2034 leveldb::Status
IndexedDBBackingStore::DeleteRange(
2035 IndexedDBBackingStore::Transaction
* transaction
,
2037 int64 object_store_id
,
2038 const IndexedDBKeyRange
& key_range
) {
2040 scoped_ptr
<IndexedDBBackingStore::Cursor
> start_cursor
=
2041 OpenObjectStoreCursor(transaction
,
2045 blink::WebIDBCursorDirectionNext
,
2050 return leveldb::Status::OK(); // Empty range == delete success.
2052 scoped_ptr
<IndexedDBBackingStore::Cursor
> end_cursor
=
2053 OpenObjectStoreCursor(transaction
,
2057 blink::WebIDBCursorDirectionPrev
,
2063 return leveldb::Status::OK(); // Empty range == delete success.
2065 BlobEntryKey start_blob_key
, end_blob_key
;
2067 std::string start_key
= ObjectStoreDataKey::Encode(
2068 database_id
, object_store_id
, start_cursor
->key());
2069 base::StringPiece
start_key_piece(start_key
);
2070 if (!BlobEntryKey::FromObjectStoreDataKey(&start_key_piece
, &start_blob_key
))
2071 return InternalInconsistencyStatus();
2072 std::string stop_key
= ObjectStoreDataKey::Encode(
2073 database_id
, object_store_id
, end_cursor
->key());
2074 base::StringPiece
stop_key_piece(stop_key
);
2075 if (!BlobEntryKey::FromObjectStoreDataKey(&stop_key_piece
, &end_blob_key
))
2076 return InternalInconsistencyStatus();
2078 s
= DeleteBlobsInRange(transaction
,
2081 start_blob_key
.Encode(),
2082 end_blob_key
.Encode(),
2086 s
= DeleteRangeBasic(transaction
->transaction(), start_key
, stop_key
, false);
2090 ExistsEntryKey::Encode(database_id
, object_store_id
, start_cursor
->key());
2092 ExistsEntryKey::Encode(database_id
, object_store_id
, end_cursor
->key());
2093 return DeleteRangeBasic(
2094 transaction
->transaction(), start_key
, stop_key
, false);
2097 leveldb::Status
IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
2098 IndexedDBBackingStore::Transaction
* transaction
,
2100 int64 object_store_id
,
2101 int64
* key_generator_current_number
) {
2102 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2103 return InvalidDBKeyStatus();
2104 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2106 const std::string key_generator_current_number_key
=
2107 ObjectStoreMetaDataKey::Encode(
2110 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
2112 *key_generator_current_number
= -1;
2117 leveldb_transaction
->Get(key_generator_current_number_key
, &data
, &found
);
2119 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2122 if (found
&& !data
.empty()) {
2123 StringPiece
slice(data
);
2124 if (!DecodeInt(&slice
, key_generator_current_number
) || !slice
.empty()) {
2125 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2126 return InternalInconsistencyStatus();
2131 // Previously, the key generator state was not stored explicitly
2132 // but derived from the maximum numeric key present in existing
2133 // data. This violates the spec as the data may be cleared but the
2134 // key generator state must be preserved.
2135 // TODO(jsbell): Fix this for all stores on database open?
2136 const std::string start_key
=
2137 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MinIDBKey());
2138 const std::string stop_key
=
2139 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MaxIDBKey());
2141 scoped_ptr
<LevelDBIterator
> it
= leveldb_transaction
->CreateIterator();
2142 int64 max_numeric_key
= 0;
2144 for (s
= it
->Seek(start_key
);
2145 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
2147 StringPiece
slice(it
->Key());
2148 ObjectStoreDataKey data_key
;
2149 if (!ObjectStoreDataKey::Decode(&slice
, &data_key
) || !slice
.empty()) {
2150 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2151 return InternalInconsistencyStatus();
2153 scoped_ptr
<IndexedDBKey
> user_key
= data_key
.user_key();
2154 if (user_key
->type() == blink::WebIDBKeyTypeNumber
) {
2155 int64 n
= static_cast<int64
>(user_key
->number());
2156 if (n
> max_numeric_key
)
2157 max_numeric_key
= n
;
2162 *key_generator_current_number
= max_numeric_key
+ 1;
2164 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2169 leveldb::Status
IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
2170 IndexedDBBackingStore::Transaction
* transaction
,
2172 int64 object_store_id
,
2174 bool check_current
) {
2175 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2176 return InvalidDBKeyStatus();
2178 if (check_current
) {
2179 int64 current_number
;
2180 leveldb::Status s
= GetKeyGeneratorCurrentNumber(
2181 transaction
, database_id
, object_store_id
, ¤t_number
);
2184 if (new_number
<= current_number
)
2188 const std::string key_generator_current_number_key
=
2189 ObjectStoreMetaDataKey::Encode(
2192 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
2194 transaction
->transaction(), key_generator_current_number_key
, new_number
);
2195 return leveldb::Status::OK();
2198 leveldb::Status
IndexedDBBackingStore::KeyExistsInObjectStore(
2199 IndexedDBBackingStore::Transaction
* transaction
,
2201 int64 object_store_id
,
2202 const IndexedDBKey
& key
,
2203 RecordIdentifier
* found_record_identifier
,
2205 IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
2206 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2207 return InvalidDBKeyStatus();
2209 const std::string leveldb_key
=
2210 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
2214 transaction
->transaction()->Get(leveldb_key
, &data
, found
);
2216 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE
);
2220 return leveldb::Status::OK();
2222 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE
);
2223 return InternalInconsistencyStatus();
2227 StringPiece
slice(data
);
2228 if (!DecodeVarInt(&slice
, &version
))
2229 return InternalInconsistencyStatus();
2231 std::string encoded_key
;
2232 EncodeIDBKey(key
, &encoded_key
);
2233 found_record_identifier
->Reset(encoded_key
, version
);
2237 class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
2238 : public IndexedDBBackingStore::Transaction::ChainedBlobWriter
{
2240 typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
2242 ChainedBlobWriterImpl(
2244 IndexedDBBackingStore
* backing_store
,
2245 WriteDescriptorVec
* blobs
,
2246 scoped_refptr
<IndexedDBBackingStore::BlobWriteCallback
> callback
)
2247 : waiting_for_callback_(false),
2248 database_id_(database_id
),
2249 backing_store_(backing_store
),
2250 callback_(callback
) {
2251 blobs_
.swap(*blobs
);
2252 iter_
= blobs_
.begin();
2253 backing_store
->task_runner()->PostTask(
2254 FROM_HERE
, base::Bind(&ChainedBlobWriterImpl::WriteNextFile
, this));
2257 void set_delegate(scoped_ptr
<FileWriterDelegate
> delegate
) override
{
2258 delegate_
.reset(delegate
.release());
2261 void ReportWriteCompletion(bool succeeded
, int64 bytes_written
) override
{
2262 DCHECK(waiting_for_callback_
);
2263 DCHECK(!succeeded
|| bytes_written
>= 0);
2264 waiting_for_callback_
= false;
2265 if (delegate_
.get()) // Only present for Blob, not File.
2266 content::BrowserThread::DeleteSoon(
2267 content::BrowserThread::IO
, FROM_HERE
, delegate_
.release());
2268 if (aborted_self_ref_
.get()) {
2269 aborted_self_ref_
= NULL
;
2272 if (iter_
->size() != -1 && iter_
->size() != bytes_written
)
2278 callback_
->Run(false);
2282 void Abort() override
{
2283 if (!waiting_for_callback_
)
2285 aborted_self_ref_
= this;
2289 ~ChainedBlobWriterImpl() override
{ DCHECK(!waiting_for_callback_
); }
2291 void WriteNextFile() {
2292 DCHECK(!waiting_for_callback_
);
2293 DCHECK(!aborted_self_ref_
.get());
2294 if (iter_
== blobs_
.end()) {
2295 callback_
->Run(true);
2298 waiting_for_callback_
= true;
2299 if (!backing_store_
->WriteBlobFile(database_id_
, *iter_
, this)) {
2300 waiting_for_callback_
= false;
2301 callback_
->Run(false);
2307 bool waiting_for_callback_
;
2308 scoped_refptr
<ChainedBlobWriterImpl
> aborted_self_ref_
;
2309 WriteDescriptorVec blobs_
;
2310 WriteDescriptorVec::const_iterator iter_
;
2312 IndexedDBBackingStore
* backing_store_
;
2313 scoped_refptr
<IndexedDBBackingStore::BlobWriteCallback
> callback_
;
2314 scoped_ptr
<FileWriterDelegate
> delegate_
;
2316 DISALLOW_COPY_AND_ASSIGN(ChainedBlobWriterImpl
);
2319 class LocalWriteClosure
: public FileWriterDelegate::DelegateWriteCallback
,
2320 public base::RefCountedThreadSafe
<LocalWriteClosure
> {
2322 LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter
*
2323 chained_blob_writer
,
2324 base::SequencedTaskRunner
* task_runner
)
2325 : chained_blob_writer_(chained_blob_writer
),
2326 task_runner_(task_runner
),
2327 bytes_written_(0) {}
2329 void Run(base::File::Error rv
,
2331 FileWriterDelegate::WriteProgressStatus write_status
) {
2332 DCHECK_GE(bytes
, 0);
2333 bytes_written_
+= bytes
;
2334 if (write_status
== FileWriterDelegate::SUCCESS_IO_PENDING
)
2335 return; // We don't care about progress events.
2336 if (rv
== base::File::FILE_OK
) {
2337 DCHECK_EQ(write_status
, FileWriterDelegate::SUCCESS_COMPLETED
);
2339 DCHECK(write_status
== FileWriterDelegate::ERROR_WRITE_STARTED
||
2340 write_status
== FileWriterDelegate::ERROR_WRITE_NOT_STARTED
);
2343 bool success
= write_status
== FileWriterDelegate::SUCCESS_COMPLETED
;
2344 if (success
&& !bytes_written_
) {
2345 // LocalFileStreamWriter only creates a file if data is actually written.
2346 // If none was then create one now.
2347 task_runner_
->PostTask(
2348 FROM_HERE
, base::Bind(&LocalWriteClosure::CreateEmptyFile
, this));
2349 } else if (success
&& !last_modified_
.is_null()) {
2350 task_runner_
->PostTask(
2351 FROM_HERE
, base::Bind(&LocalWriteClosure::UpdateTimeStamp
, this));
2353 task_runner_
->PostTask(
2355 base::Bind(&IndexedDBBackingStore::Transaction::ChainedBlobWriter::
2356 ReportWriteCompletion
,
2357 chained_blob_writer_
,
2363 void WriteBlobToFileOnIOThread(const FilePath
& file_path
,
2364 const GURL
& blob_url
,
2365 const base::Time
& last_modified
,
2366 net::URLRequestContext
* request_context
) {
2367 DCHECK_CURRENTLY_ON(content::BrowserThread::IO
);
2368 scoped_ptr
<storage::FileStreamWriter
> writer(
2369 storage::FileStreamWriter::CreateForLocalFile(
2373 storage::FileStreamWriter::CREATE_NEW_FILE
));
2374 scoped_ptr
<FileWriterDelegate
> delegate(new FileWriterDelegate(
2375 writer
.Pass(), storage::FlushPolicy::FLUSH_ON_COMPLETION
));
2377 DCHECK(blob_url
.is_valid());
2378 scoped_ptr
<net::URLRequest
> blob_request(request_context
->CreateRequest(
2379 blob_url
, net::DEFAULT_PRIORITY
, delegate
.get()));
2381 this->file_path_
= file_path
;
2382 this->last_modified_
= last_modified
;
2384 delegate
->Start(blob_request
.Pass(),
2385 base::Bind(&LocalWriteClosure::Run
, this));
2386 chained_blob_writer_
->set_delegate(delegate
.Pass());
2390 virtual ~LocalWriteClosure() {
2391 // Make sure the last reference to a ChainedBlobWriter is released (and
2392 // deleted) on the IDB thread since it owns a transaction which has thread
2394 IndexedDBBackingStore::Transaction::ChainedBlobWriter
* raw_tmp
=
2395 chained_blob_writer_
.get();
2397 chained_blob_writer_
= NULL
;
2398 task_runner_
->ReleaseSoon(FROM_HERE
, raw_tmp
);
2400 friend class base::RefCountedThreadSafe
<LocalWriteClosure
>;
2402 // If necessary, update the timestamps on the file as a final
2403 // step before reporting success.
2404 void UpdateTimeStamp() {
2405 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
2406 if (!base::TouchFile(file_path_
, last_modified_
, last_modified_
)) {
2407 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2409 chained_blob_writer_
->ReportWriteCompletion(true, bytes_written_
);
2412 // Create an empty file.
2413 void CreateEmptyFile() {
2414 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
2415 base::File
file(file_path_
,
2416 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
2417 bool success
= file
.created();
2418 if (success
&& !last_modified_
.is_null() &&
2419 !file
.SetTimes(last_modified_
, last_modified_
)) {
2420 // TODO(cmumford): Complain quietly; timestamp's probably not vital.
2423 chained_blob_writer_
->ReportWriteCompletion(success
, bytes_written_
);
2426 scoped_refptr
<IndexedDBBackingStore::Transaction::ChainedBlobWriter
>
2427 chained_blob_writer_
;
2428 scoped_refptr
<base::SequencedTaskRunner
> task_runner_
;
2429 int64 bytes_written_
;
2431 base::FilePath file_path_
;
2432 base::Time last_modified_
;
2434 DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure
);
2437 bool IndexedDBBackingStore::WriteBlobFile(
2439 const Transaction::WriteDescriptor
& descriptor
,
2440 Transaction::ChainedBlobWriter
* chained_blob_writer
) {
2442 if (!MakeIDBBlobDirectory(blob_path_
, database_id
, descriptor
.key()))
2445 FilePath path
= GetBlobFileName(database_id
, descriptor
.key());
2447 if (descriptor
.is_file() && !descriptor
.file_path().empty()) {
2448 if (!base::CopyFile(descriptor
.file_path(), path
))
2451 base::File::Info info
;
2452 if (base::GetFileInfo(descriptor
.file_path(), &info
)) {
2453 if (descriptor
.size() != -1) {
2454 if (descriptor
.size() != info
.size
)
2456 // The round-trip can be lossy; round to nearest millisecond.
2457 int64 delta
= (descriptor
.last_modified() -
2458 info
.last_modified
).InMilliseconds();
2459 if (std::abs(delta
) > 1)
2462 if (!base::TouchFile(path
, info
.last_accessed
, info
.last_modified
)) {
2463 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2466 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2469 task_runner_
->PostTask(
2471 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion
,
2472 chained_blob_writer
,
2476 DCHECK(descriptor
.url().is_valid());
2477 scoped_refptr
<LocalWriteClosure
> write_closure(
2478 new LocalWriteClosure(chained_blob_writer
, task_runner_
.get()));
2479 content::BrowserThread::PostTask(
2480 content::BrowserThread::IO
,
2482 base::Bind(&LocalWriteClosure::WriteBlobToFileOnIOThread
,
2483 write_closure
.get(),
2486 descriptor
.last_modified(),
2492 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id
,
2494 DCHECK(KeyPrefix::IsValidDatabaseId(database_id
));
2495 bool all_blobs
= blob_key
== DatabaseMetaDataKey::kAllBlobsKey
;
2496 DCHECK(all_blobs
|| DatabaseMetaDataKey::IsValidBlobKey(blob_key
));
2497 scoped_refptr
<LevelDBTransaction
> transaction
=
2498 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
2500 BlobJournalType live_blob_journal
, primary_journal
;
2501 if (!GetLiveBlobJournal(transaction
.get(), &live_blob_journal
).ok())
2503 DCHECK(!live_blob_journal
.empty());
2504 if (!GetPrimaryBlobJournal(transaction
.get(), &primary_journal
).ok())
2507 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2508 // remove all entries with database_id from the live_blob journal and add only
2509 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2510 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2511 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2512 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2513 // matching (database_id, blob_key) tuple, we should move it to the primary
2515 BlobJournalType new_live_blob_journal
;
2516 for (BlobJournalType::iterator journal_iter
= live_blob_journal
.begin();
2517 journal_iter
!= live_blob_journal
.end();
2519 int64 current_database_id
= journal_iter
->first
;
2520 int64 current_blob_key
= journal_iter
->second
;
2521 bool current_all_blobs
=
2522 current_blob_key
== DatabaseMetaDataKey::kAllBlobsKey
;
2523 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id
) ||
2525 if (current_database_id
== database_id
&&
2526 (all_blobs
|| current_all_blobs
|| blob_key
== current_blob_key
)) {
2528 primary_journal
.push_back(
2529 std::make_pair(database_id
, current_blob_key
));
2530 if (current_all_blobs
)
2531 new_live_blob_journal
.push_back(*journal_iter
);
2532 new_live_blob_journal
.insert(new_live_blob_journal
.end(),
2534 live_blob_journal
.end()); // All the rest.
2538 new_live_blob_journal
.push_back(*journal_iter
);
2542 primary_journal
.push_back(
2543 std::make_pair(database_id
, DatabaseMetaDataKey::kAllBlobsKey
));
2545 UpdatePrimaryBlobJournal(transaction
.get(), primary_journal
);
2546 UpdateLiveBlobJournal(transaction
.get(), new_live_blob_journal
);
2547 transaction
->Commit();
2548 // We could just do the deletions/cleaning here, but if there are a lot of
2549 // blobs about to be garbage collected, it'd be better to wait and do them all
2551 StartJournalCleaningTimer();
2554 // The this reference is a raw pointer that's declared Unretained inside the
2555 // timer code, so this won't confuse IndexedDBFactory's check for
2556 // HasLastBackingStoreReference. It's safe because if the backing store is
2557 // deleted, the timer will automatically be canceled on destruction.
2558 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2559 journal_cleaning_timer_
.Start(
2561 base::TimeDelta::FromSeconds(5),
2563 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn
);
2566 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
2567 FilePath
IndexedDBBackingStore::GetBlobFileName(int64 database_id
,
2569 return GetBlobFileNameForKey(blob_path_
, database_id
, key
);
2572 static bool CheckIndexAndMetaDataKey(const LevelDBIterator
* it
,
2573 const std::string
& stop_key
,
2575 unsigned char meta_data_type
) {
2576 if (!it
->IsValid() || CompareKeys(it
->Key(), stop_key
) >= 0)
2579 StringPiece
slice(it
->Key());
2580 IndexMetaDataKey meta_data_key
;
2581 bool ok
= IndexMetaDataKey::Decode(&slice
, &meta_data_key
);
2583 if (meta_data_key
.IndexId() != index_id
)
2585 if (meta_data_key
.meta_data_type() != meta_data_type
)
2590 // TODO(jsbell): This should do some error handling rather than plowing ahead
2591 // when bad data is encountered.
2592 leveldb::Status
IndexedDBBackingStore::GetIndexes(
2594 int64 object_store_id
,
2595 IndexedDBObjectStoreMetadata::IndexMap
* indexes
) {
2596 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2597 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2598 return InvalidDBKeyStatus();
2599 const std::string start_key
=
2600 IndexMetaDataKey::Encode(database_id
, object_store_id
, 0, 0);
2601 const std::string stop_key
=
2602 IndexMetaDataKey::Encode(database_id
, object_store_id
+ 1, 0, 0);
2604 DCHECK(indexes
->empty());
2606 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
2607 leveldb::Status s
= it
->Seek(start_key
);
2608 while (s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0) {
2609 StringPiece
slice(it
->Key());
2610 IndexMetaDataKey meta_data_key
;
2611 bool ok
= IndexMetaDataKey::Decode(&slice
, &meta_data_key
);
2613 if (meta_data_key
.meta_data_type() != IndexMetaDataKey::NAME
) {
2614 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2615 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2623 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2625 int64 index_id
= meta_data_key
.IndexId();
2626 base::string16 index_name
;
2628 StringPiece
slice(it
->Value());
2629 if (!DecodeString(&slice
, &index_name
) || !slice
.empty())
2630 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2633 s
= it
->Next(); // unique flag
2636 if (!CheckIndexAndMetaDataKey(
2637 it
.get(), stop_key
, index_id
, IndexMetaDataKey::UNIQUE
)) {
2638 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2643 StringPiece
slice(it
->Value());
2644 if (!DecodeBool(&slice
, &index_unique
) || !slice
.empty())
2645 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2648 s
= it
->Next(); // key_path
2651 if (!CheckIndexAndMetaDataKey(
2652 it
.get(), stop_key
, index_id
, IndexMetaDataKey::KEY_PATH
)) {
2653 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2656 IndexedDBKeyPath key_path
;
2658 StringPiece
slice(it
->Value());
2659 if (!DecodeIDBKeyPath(&slice
, &key_path
) || !slice
.empty())
2660 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2663 s
= it
->Next(); // [optional] multi_entry flag
2666 bool index_multi_entry
= false;
2667 if (CheckIndexAndMetaDataKey(
2668 it
.get(), stop_key
, index_id
, IndexMetaDataKey::MULTI_ENTRY
)) {
2669 StringPiece
slice(it
->Value());
2670 if (!DecodeBool(&slice
, &index_multi_entry
) || !slice
.empty())
2671 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2678 (*indexes
)[index_id
] = IndexedDBIndexMetadata(
2679 index_name
, index_id
, key_path
, index_unique
, index_multi_entry
);
2683 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES
);
2688 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id
, int64 key
) const {
2689 FilePath path
= GetBlobFileName(database_id
, key
);
2690 return base::DeleteFile(path
, false);
2693 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id
) const {
2694 FilePath path
= GetBlobDirectoryName(blob_path_
, database_id
);
2695 return base::DeleteFile(path
, true);
2698 leveldb::Status
IndexedDBBackingStore::CleanUpBlobJournalEntries(
2699 const BlobJournalType
& journal
) const {
2700 IDB_TRACE("IndexedDBBackingStore::CleanUpBlobJournalEntries");
2701 if (journal
.empty())
2702 return leveldb::Status::OK();
2703 for (const auto& entry
: journal
) {
2704 int64 database_id
= entry
.first
;
2705 int64 blob_key
= entry
.second
;
2706 DCHECK(KeyPrefix::IsValidDatabaseId(database_id
));
2707 if (blob_key
== DatabaseMetaDataKey::kAllBlobsKey
) {
2708 if (!RemoveBlobDirectory(database_id
))
2709 return IOErrorStatus();
2711 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key
));
2712 if (!RemoveBlobFile(database_id
, blob_key
))
2713 return IOErrorStatus();
2716 return leveldb::Status::OK();
2719 leveldb::Status
IndexedDBBackingStore::CleanUpBlobJournal(
2720 const std::string
& level_db_key
) const {
2721 IDB_TRACE("IndexedDBBackingStore::CleanUpBlobJournal");
2722 DCHECK(!committing_transaction_count_
);
2724 scoped_refptr
<LevelDBTransaction
> journal_transaction
=
2725 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
2726 BlobJournalType journal
;
2728 s
= GetBlobJournal(level_db_key
, journal_transaction
.get(), &journal
);
2731 if (journal
.empty())
2732 return leveldb::Status::OK();
2733 s
= CleanUpBlobJournalEntries(journal
);
2736 ClearBlobJournal(journal_transaction
.get(), level_db_key
);
2737 return journal_transaction
->Commit();
2740 leveldb::Status
IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
2742 const std::string
& object_store_data_key
,
2743 IndexedDBValue
* value
) {
2744 BlobChangeRecord
* change_record
= NULL
;
2745 BlobChangeMap::const_iterator blob_iter
=
2746 blob_change_map_
.find(object_store_data_key
);
2747 if (blob_iter
!= blob_change_map_
.end()) {
2748 change_record
= blob_iter
->second
;
2750 blob_iter
= incognito_blob_map_
.find(object_store_data_key
);
2751 if (blob_iter
!= incognito_blob_map_
.end())
2752 change_record
= blob_iter
->second
;
2754 if (change_record
) {
2755 // Either we haven't written the blob to disk yet or we're in incognito
2756 // mode, so we have to send back the one they sent us. This change record
2757 // includes the original UUID.
2758 value
->blob_info
= change_record
->blob_info();
2759 return leveldb::Status::OK();
2762 BlobEntryKey blob_entry_key
;
2763 StringPiece
leveldb_key_piece(object_store_data_key
);
2764 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece
,
2767 return InternalInconsistencyStatus();
2769 std::string encoded_key
= blob_entry_key
.Encode();
2771 std::string encoded_value
;
2772 leveldb::Status s
= transaction()->Get(encoded_key
, &encoded_value
, &found
);
2776 if (!DecodeBlobData(encoded_value
, &value
->blob_info
)) {
2777 INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD
);
2778 return InternalInconsistencyStatus();
2780 for (auto& entry
: value
->blob_info
) {
2781 entry
.set_file_path(
2782 backing_store_
->GetBlobFileName(database_id
, entry
.key()));
2783 entry
.set_mark_used_callback(
2784 backing_store_
->active_blob_registry()->GetAddBlobRefCallback(
2785 database_id
, entry
.key()));
2786 entry
.set_release_callback(
2787 backing_store_
->active_blob_registry()->GetFinalReleaseCallback(
2788 database_id
, entry
.key()));
2789 if (entry
.is_file() && !entry
.file_path().empty()) {
2790 base::File::Info info
;
2791 if (base::GetFileInfo(entry
.file_path(), &info
)) {
2792 // This should always work, but it isn't fatal if it doesn't; it just
2793 // means a potential slow synchronous call from the renderer later.
2794 entry
.set_last_modified(info
.last_modified
);
2795 entry
.set_size(info
.size
);
2800 return leveldb::Status::OK();
2803 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2804 // While a transaction is busy it is not safe to clean the journal.
2805 if (committing_transaction_count_
> 0)
2806 StartJournalCleaningTimer();
2808 CleanUpBlobJournal(BlobJournalKey::Encode());
2811 WARN_UNUSED_RESULT
static leveldb::Status
SetMaxIndexId(
2812 LevelDBTransaction
* transaction
,
2814 int64 object_store_id
,
2816 int64 max_index_id
= -1;
2817 const std::string max_index_id_key
= ObjectStoreMetaDataKey::Encode(
2818 database_id
, object_store_id
, ObjectStoreMetaDataKey::MAX_INDEX_ID
);
2821 GetInt(transaction
, max_index_id_key
, &max_index_id
, &found
);
2823 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID
);
2827 max_index_id
= kMinimumIndexId
;
2829 if (index_id
<= max_index_id
) {
2830 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID
);
2831 return InternalInconsistencyStatus();
2834 PutInt(transaction
, max_index_id_key
, index_id
);
2838 leveldb::Status
IndexedDBBackingStore::CreateIndex(
2839 IndexedDBBackingStore::Transaction
* transaction
,
2841 int64 object_store_id
,
2843 const base::string16
& name
,
2844 const IndexedDBKeyPath
& key_path
,
2846 bool is_multi_entry
) {
2847 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2848 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2849 return InvalidDBKeyStatus();
2850 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2851 leveldb::Status s
= SetMaxIndexId(
2852 leveldb_transaction
, database_id
, object_store_id
, index_id
);
2857 const std::string name_key
= IndexMetaDataKey::Encode(
2858 database_id
, object_store_id
, index_id
, IndexMetaDataKey::NAME
);
2859 const std::string unique_key
= IndexMetaDataKey::Encode(
2860 database_id
, object_store_id
, index_id
, IndexMetaDataKey::UNIQUE
);
2861 const std::string key_path_key
= IndexMetaDataKey::Encode(
2862 database_id
, object_store_id
, index_id
, IndexMetaDataKey::KEY_PATH
);
2863 const std::string multi_entry_key
= IndexMetaDataKey::Encode(
2864 database_id
, object_store_id
, index_id
, IndexMetaDataKey::MULTI_ENTRY
);
2866 PutString(leveldb_transaction
, name_key
, name
);
2867 PutBool(leveldb_transaction
, unique_key
, is_unique
);
2868 PutIDBKeyPath(leveldb_transaction
, key_path_key
, key_path
);
2869 PutBool(leveldb_transaction
, multi_entry_key
, is_multi_entry
);
2873 leveldb::Status
IndexedDBBackingStore::DeleteIndex(
2874 IndexedDBBackingStore::Transaction
* transaction
,
2876 int64 object_store_id
,
2878 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2879 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2880 return InvalidDBKeyStatus();
2881 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2883 const std::string index_meta_data_start
=
2884 IndexMetaDataKey::Encode(database_id
, object_store_id
, index_id
, 0);
2885 const std::string index_meta_data_end
=
2886 IndexMetaDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
2887 leveldb::Status s
= DeleteRangeBasic(
2888 leveldb_transaction
, index_meta_data_start
, index_meta_data_end
, true);
2891 const std::string index_data_start
=
2892 IndexDataKey::EncodeMinKey(database_id
, object_store_id
, index_id
);
2893 const std::string index_data_end
=
2894 IndexDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
2895 s
= DeleteRangeBasic(
2896 leveldb_transaction
, index_data_start
, index_data_end
, true);
2900 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX
);
2905 leveldb::Status
IndexedDBBackingStore::PutIndexDataForRecord(
2906 IndexedDBBackingStore::Transaction
* transaction
,
2908 int64 object_store_id
,
2910 const IndexedDBKey
& key
,
2911 const RecordIdentifier
& record_identifier
) {
2912 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2913 DCHECK(key
.IsValid());
2914 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2915 return InvalidDBKeyStatus();
2917 std::string encoded_key
;
2918 EncodeIDBKey(key
, &encoded_key
);
2920 const std::string index_data_key
=
2921 IndexDataKey::Encode(database_id
,
2925 record_identifier
.primary_key(),
2929 EncodeVarInt(record_identifier
.version(), &data
);
2930 data
.append(record_identifier
.primary_key());
2932 transaction
->transaction()->Put(index_data_key
, &data
);
2933 return leveldb::Status::OK();
2936 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction
* transaction
,
2937 const std::string
& target
,
2938 std::string
* found_key
,
2939 leveldb::Status
* s
) {
2940 scoped_ptr
<LevelDBIterator
> it
= transaction
->CreateIterator();
2941 *s
= it
->Seek(target
);
2945 if (!it
->IsValid()) {
2946 *s
= it
->SeekToLast();
2947 if (!s
->ok() || !it
->IsValid())
2951 while (CompareIndexKeys(it
->Key(), target
) > 0) {
2953 if (!s
->ok() || !it
->IsValid())
2958 *found_key
= it
->Key().as_string();
2960 // There can be several index keys that compare equal. We want the last one.
2962 } while (s
->ok() && it
->IsValid() && !CompareIndexKeys(it
->Key(), target
));
2967 static leveldb::Status
VersionExists(LevelDBTransaction
* transaction
,
2969 int64 object_store_id
,
2971 const std::string
& encoded_primary_key
,
2973 const std::string key
=
2974 ExistsEntryKey::Encode(database_id
, object_store_id
, encoded_primary_key
);
2977 leveldb::Status s
= transaction
->Get(key
, &data
, exists
);
2979 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS
);
2985 StringPiece
slice(data
);
2987 if (!DecodeInt(&slice
, &decoded
) || !slice
.empty())
2988 return InternalInconsistencyStatus();
2989 *exists
= (decoded
== version
);
2993 leveldb::Status
IndexedDBBackingStore::FindKeyInIndex(
2994 IndexedDBBackingStore::Transaction
* transaction
,
2996 int64 object_store_id
,
2998 const IndexedDBKey
& key
,
2999 std::string
* found_encoded_primary_key
,
3001 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
3002 DCHECK(KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
));
3004 DCHECK(found_encoded_primary_key
->empty());
3007 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3008 const std::string leveldb_key
=
3009 IndexDataKey::Encode(database_id
, object_store_id
, index_id
, key
);
3010 scoped_ptr
<LevelDBIterator
> it
= leveldb_transaction
->CreateIterator();
3011 leveldb::Status s
= it
->Seek(leveldb_key
);
3013 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX
);
3019 return leveldb::Status::OK();
3020 if (CompareIndexKeys(it
->Key(), leveldb_key
) > 0)
3021 return leveldb::Status::OK();
3023 StringPiece
slice(it
->Value());
3026 if (!DecodeVarInt(&slice
, &version
)) {
3027 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX
);
3028 return InternalInconsistencyStatus();
3030 *found_encoded_primary_key
= slice
.as_string();
3032 bool exists
= false;
3033 s
= VersionExists(leveldb_transaction
,
3037 *found_encoded_primary_key
,
3042 // Delete stale index data entry and continue.
3043 leveldb_transaction
->Remove(it
->Key());
3052 leveldb::Status
IndexedDBBackingStore::GetPrimaryKeyViaIndex(
3053 IndexedDBBackingStore::Transaction
* transaction
,
3055 int64 object_store_id
,
3057 const IndexedDBKey
& key
,
3058 scoped_ptr
<IndexedDBKey
>* primary_key
) {
3059 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
3060 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
3061 return InvalidDBKeyStatus();
3064 std::string found_encoded_primary_key
;
3065 leveldb::Status s
= FindKeyInIndex(transaction
,
3070 &found_encoded_primary_key
,
3073 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX
);
3078 if (!found_encoded_primary_key
.size()) {
3079 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX
);
3080 return InvalidDBKeyStatus();
3083 StringPiece
slice(found_encoded_primary_key
);
3084 if (DecodeIDBKey(&slice
, primary_key
) && slice
.empty())
3087 return InvalidDBKeyStatus();
3090 leveldb::Status
IndexedDBBackingStore::KeyExistsInIndex(
3091 IndexedDBBackingStore::Transaction
* transaction
,
3093 int64 object_store_id
,
3095 const IndexedDBKey
& index_key
,
3096 scoped_ptr
<IndexedDBKey
>* found_primary_key
,
3098 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
3099 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
3100 return InvalidDBKeyStatus();
3103 std::string found_encoded_primary_key
;
3104 leveldb::Status s
= FindKeyInIndex(transaction
,
3109 &found_encoded_primary_key
,
3112 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX
);
3116 return leveldb::Status::OK();
3117 if (found_encoded_primary_key
.empty()) {
3118 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX
);
3119 return InvalidDBKeyStatus();
3122 StringPiece
slice(found_encoded_primary_key
);
3123 if (DecodeIDBKey(&slice
, found_primary_key
) && slice
.empty())
3126 return InvalidDBKeyStatus();
3129 IndexedDBBackingStore::Cursor::Cursor(
3130 const IndexedDBBackingStore::Cursor
* other
)
3131 : backing_store_(other
->backing_store_
),
3132 transaction_(other
->transaction_
),
3133 database_id_(other
->database_id_
),
3134 cursor_options_(other
->cursor_options_
),
3135 current_key_(new IndexedDBKey(*other
->current_key_
)) {
3136 if (other
->iterator_
) {
3137 iterator_
= transaction_
->transaction()->CreateIterator();
3139 if (other
->iterator_
->IsValid()) {
3140 leveldb::Status s
= iterator_
->Seek(other
->iterator_
->Key());
3141 // TODO(cmumford): Handle this error (crbug.com/363397)
3142 DCHECK(iterator_
->IsValid());
3147 IndexedDBBackingStore::Cursor::Cursor(
3148 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3149 IndexedDBBackingStore::Transaction
* transaction
,
3151 const CursorOptions
& cursor_options
)
3152 : backing_store_(backing_store
.get()),
3153 transaction_(transaction
),
3154 database_id_(database_id
),
3155 cursor_options_(cursor_options
) {
3157 IndexedDBBackingStore::Cursor::~Cursor() {}
3159 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status
* s
) {
3160 iterator_
= transaction_
->transaction()->CreateIterator();
3161 if (cursor_options_
.forward
)
3162 *s
= iterator_
->Seek(cursor_options_
.low_key
);
3164 *s
= iterator_
->Seek(cursor_options_
.high_key
);
3168 return Continue(0, READY
, s
);
3171 bool IndexedDBBackingStore::Cursor::Advance(uint32 count
, leveldb::Status
* s
) {
3172 *s
= leveldb::Status::OK();
3180 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey
* key
,
3181 const IndexedDBKey
* primary_key
,
3182 IteratorState next_state
,
3183 leveldb::Status
* s
) {
3184 DCHECK(!key
|| next_state
== SEEK
);
3186 if (cursor_options_
.forward
)
3187 return ContinueNext(key
, primary_key
, next_state
, s
);
3189 return ContinuePrevious(key
, primary_key
, next_state
, s
);
3192 bool IndexedDBBackingStore::Cursor::ContinueNext(
3193 const IndexedDBKey
* key
,
3194 const IndexedDBKey
* primary_key
,
3195 IteratorState next_state
,
3196 leveldb::Status
* s
) {
3197 DCHECK(cursor_options_
.forward
);
3198 DCHECK(!key
|| key
->IsValid());
3199 DCHECK(!primary_key
|| primary_key
->IsValid());
3200 *s
= leveldb::Status::OK();
3202 // TODO(alecflett): avoid a copy here?
3203 IndexedDBKey previous_key
= current_key_
? *current_key_
: IndexedDBKey();
3205 // If seeking to a particular key (or key and primary key), skip the cursor
3206 // forward rather than iterating it.
3207 if (next_state
== SEEK
&& key
) {
3208 std::string leveldb_key
=
3209 primary_key
? EncodeKey(*key
, *primary_key
) : EncodeKey(*key
);
3210 *s
= iterator_
->Seek(leveldb_key
);
3213 // Cursor is at the next value already; don't advance it again below.
3218 // Only advance the cursor if it was not set to position already, either
3219 // because it is newly opened (and positioned at start of range) or
3220 // skipped forward by continue with a specific key.
3221 if (next_state
== SEEK
) {
3222 *s
= iterator_
->Next();
3229 // Fail if we've run out of data or gone past the cursor's bounds.
3230 if (!iterator_
->IsValid() || IsPastBounds())
3233 // TODO(jsbell): Document why this might be false. When do we ever not
3234 // seek into the range before starting cursor iteration?
3235 if (!HaveEnteredRange())
3238 // The row may not load because there's a stale entry in the index. If no
3239 // error then not fatal.
3240 if (!LoadCurrentRow(s
)) {
3246 // Cursor is now positioned at a non-stale record in range.
3248 // "Unique" cursors should continue seeking until a new key value is seen.
3249 if (cursor_options_
.unique
&& previous_key
.IsValid() &&
3250 current_key_
->Equals(previous_key
)) {
3260 bool IndexedDBBackingStore::Cursor::ContinuePrevious(
3261 const IndexedDBKey
* key
,
3262 const IndexedDBKey
* primary_key
,
3263 IteratorState next_state
,
3264 leveldb::Status
* s
) {
3265 DCHECK(!cursor_options_
.forward
);
3266 DCHECK(!key
|| key
->IsValid());
3267 DCHECK(!primary_key
|| primary_key
->IsValid());
3268 *s
= leveldb::Status::OK();
3270 // TODO(alecflett): avoid a copy here?
3271 IndexedDBKey previous_key
= current_key_
? *current_key_
: IndexedDBKey();
3273 // When iterating with PrevNoDuplicate, spec requires that the value we
3274 // yield for each key is the *first* duplicate in forwards order. We do this
3275 // by remembering the duplicate key (implicitly, the first record seen with
3276 // a new key), keeping track of the earliest duplicate seen, and continuing
3277 // until yet another new key is seen, at which point the earliest duplicate
3278 // is the correct cursor position.
3279 IndexedDBKey duplicate_key
;
3280 std::string earliest_duplicate
;
3282 // TODO(jsbell): Optimize continuing to a specific key (or key and primary
3283 // key) for reverse cursors as well. See Seek() optimization at the start of
3284 // ContinueNext() for an example.
3287 if (next_state
== SEEK
) {
3288 *s
= iterator_
->Prev();
3292 next_state
= SEEK
; // for subsequent iterations
3295 // If we've run out of data or gone past the cursor's bounds.
3296 if (!iterator_
->IsValid() || IsPastBounds()) {
3297 if (duplicate_key
.IsValid())
3302 // TODO(jsbell): Document why this might be false. When do we ever not
3303 // seek into the range before starting cursor iteration?
3304 if (!HaveEnteredRange())
3307 // The row may not load because there's a stale entry in the index. If no
3308 // error then not fatal.
3309 if (!LoadCurrentRow(s
)) {
3315 // If seeking to a key (or key and primary key), continue until found.
3316 // TODO(jsbell): If Seek() optimization is added above, remove this.
3318 if (primary_key
&& key
->Equals(*current_key_
) &&
3319 primary_key
->IsLessThan(this->primary_key()))
3321 if (key
->IsLessThan(*current_key_
))
3325 // Cursor is now positioned at a non-stale record in range.
3327 if (cursor_options_
.unique
) {
3328 // If entry is a duplicate of the previous, keep going. Although the
3329 // cursor should be positioned at the first duplicate already, new
3330 // duplicates may have been inserted since the cursor was last iterated,
3331 // and should be skipped to maintain "unique" iteration.
3332 if (previous_key
.IsValid() && current_key_
->Equals(previous_key
))
3335 // If we've found a new key, remember it and keep going.
3336 if (!duplicate_key
.IsValid()) {
3337 duplicate_key
= *current_key_
;
3338 earliest_duplicate
= iterator_
->Key().as_string();
3342 // If we're still seeing duplicates, keep going.
3343 if (duplicate_key
.Equals(*current_key_
)) {
3344 earliest_duplicate
= iterator_
->Key().as_string();
3352 if (cursor_options_
.unique
) {
3353 DCHECK(duplicate_key
.IsValid());
3354 DCHECK(!earliest_duplicate
.empty());
3356 *s
= iterator_
->Seek(earliest_duplicate
);
3359 if (!LoadCurrentRow(s
)) {
3368 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3369 if (cursor_options_
.forward
) {
3370 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.low_key
);
3371 if (cursor_options_
.low_open
) {
3374 return compare
>= 0;
3376 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.high_key
);
3377 if (cursor_options_
.high_open
) {
3380 return compare
<= 0;
3383 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
3384 if (cursor_options_
.forward
) {
3385 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.high_key
);
3386 if (cursor_options_
.high_open
) {
3387 return compare
>= 0;
3391 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.low_key
);
3392 if (cursor_options_
.low_open
) {
3393 return compare
<= 0;
3398 const IndexedDBKey
& IndexedDBBackingStore::Cursor::primary_key() const {
3399 return *current_key_
;
3402 const IndexedDBBackingStore::RecordIdentifier
&
3403 IndexedDBBackingStore::Cursor::record_identifier() const {
3404 return record_identifier_
;
3407 class ObjectStoreKeyCursorImpl
: public IndexedDBBackingStore::Cursor
{
3409 ObjectStoreKeyCursorImpl(
3410 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3411 IndexedDBBackingStore::Transaction
* transaction
,
3413 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3414 : IndexedDBBackingStore::Cursor(backing_store
,
3419 Cursor
* Clone() override
{ return new ObjectStoreKeyCursorImpl(this); }
3421 // IndexedDBBackingStore::Cursor
3422 IndexedDBValue
* value() override
{
3426 bool LoadCurrentRow(leveldb::Status
* s
) override
;
3429 std::string
EncodeKey(const IndexedDBKey
& key
) override
{
3430 return ObjectStoreDataKey::Encode(
3431 cursor_options_
.database_id
, cursor_options_
.object_store_id
, key
);
3433 std::string
EncodeKey(const IndexedDBKey
& key
,
3434 const IndexedDBKey
& primary_key
) override
{
3436 return std::string();
3440 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl
* other
)
3441 : IndexedDBBackingStore::Cursor(other
) {}
3443 DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl
);
3446 bool ObjectStoreKeyCursorImpl::LoadCurrentRow(leveldb::Status
* s
) {
3447 StringPiece
slice(iterator_
->Key());
3448 ObjectStoreDataKey object_store_data_key
;
3449 if (!ObjectStoreDataKey::Decode(&slice
, &object_store_data_key
)) {
3450 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3451 *s
= InvalidDBKeyStatus();
3455 current_key_
= object_store_data_key
.user_key();
3458 slice
= StringPiece(iterator_
->Value());
3459 if (!DecodeVarInt(&slice
, &version
)) {
3460 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3461 *s
= InternalInconsistencyStatus();
3465 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3466 std::string encoded_key
;
3467 EncodeIDBKey(*current_key_
, &encoded_key
);
3468 record_identifier_
.Reset(encoded_key
, version
);
3473 class ObjectStoreCursorImpl
: public IndexedDBBackingStore::Cursor
{
3475 ObjectStoreCursorImpl(
3476 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3477 IndexedDBBackingStore::Transaction
* transaction
,
3479 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3480 : IndexedDBBackingStore::Cursor(backing_store
,
3485 Cursor
* Clone() override
{ return new ObjectStoreCursorImpl(this); }
3487 // IndexedDBBackingStore::Cursor
3488 IndexedDBValue
* value() override
{ return ¤t_value_
; }
3489 bool LoadCurrentRow(leveldb::Status
* s
) override
;
3492 std::string
EncodeKey(const IndexedDBKey
& key
) override
{
3493 return ObjectStoreDataKey::Encode(
3494 cursor_options_
.database_id
, cursor_options_
.object_store_id
, key
);
3496 std::string
EncodeKey(const IndexedDBKey
& key
,
3497 const IndexedDBKey
& primary_key
) override
{
3499 return std::string();
3503 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl
* other
)
3504 : IndexedDBBackingStore::Cursor(other
),
3505 current_value_(other
->current_value_
) {}
3507 IndexedDBValue current_value_
;
3509 DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl
);
3512 bool ObjectStoreCursorImpl::LoadCurrentRow(leveldb::Status
* s
) {
3513 StringPiece
key_slice(iterator_
->Key());
3514 ObjectStoreDataKey object_store_data_key
;
3515 if (!ObjectStoreDataKey::Decode(&key_slice
, &object_store_data_key
)) {
3516 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3517 *s
= InvalidDBKeyStatus();
3521 current_key_
= object_store_data_key
.user_key();
3524 StringPiece value_slice
= StringPiece(iterator_
->Value());
3525 if (!DecodeVarInt(&value_slice
, &version
)) {
3526 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3527 *s
= InternalInconsistencyStatus();
3531 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3532 std::string encoded_key
;
3533 EncodeIDBKey(*current_key_
, &encoded_key
);
3534 record_identifier_
.Reset(encoded_key
, version
);
3536 *s
= transaction_
->GetBlobInfoForRecord(
3537 database_id_
, iterator_
->Key().as_string(), ¤t_value_
);
3541 current_value_
.bits
= value_slice
.as_string();
3545 class IndexKeyCursorImpl
: public IndexedDBBackingStore::Cursor
{
3548 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3549 IndexedDBBackingStore::Transaction
* transaction
,
3551 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3552 : IndexedDBBackingStore::Cursor(backing_store
,
3557 Cursor
* Clone() override
{ return new IndexKeyCursorImpl(this); }
3559 // IndexedDBBackingStore::Cursor
3560 IndexedDBValue
* value() override
{
3564 const IndexedDBKey
& primary_key() const override
{ return *primary_key_
; }
3565 const IndexedDBBackingStore::RecordIdentifier
& record_identifier()
3568 return record_identifier_
;
3570 bool LoadCurrentRow(leveldb::Status
* s
) override
;
3573 std::string
EncodeKey(const IndexedDBKey
& key
) override
{
3574 return IndexDataKey::Encode(cursor_options_
.database_id
,
3575 cursor_options_
.object_store_id
,
3576 cursor_options_
.index_id
,
3579 std::string
EncodeKey(const IndexedDBKey
& key
,
3580 const IndexedDBKey
& primary_key
) override
{
3581 return IndexDataKey::Encode(cursor_options_
.database_id
,
3582 cursor_options_
.object_store_id
,
3583 cursor_options_
.index_id
,
3589 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl
* other
)
3590 : IndexedDBBackingStore::Cursor(other
),
3591 primary_key_(new IndexedDBKey(*other
->primary_key_
)) {}
3593 scoped_ptr
<IndexedDBKey
> primary_key_
;
3595 DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl
);
3598 bool IndexKeyCursorImpl::LoadCurrentRow(leveldb::Status
* s
) {
3599 StringPiece
slice(iterator_
->Key());
3600 IndexDataKey index_data_key
;
3601 if (!IndexDataKey::Decode(&slice
, &index_data_key
)) {
3602 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3603 *s
= InvalidDBKeyStatus();
3607 current_key_
= index_data_key
.user_key();
3608 DCHECK(current_key_
);
3610 slice
= StringPiece(iterator_
->Value());
3611 int64 index_data_version
;
3612 if (!DecodeVarInt(&slice
, &index_data_version
)) {
3613 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3614 *s
= InternalInconsistencyStatus();
3618 if (!DecodeIDBKey(&slice
, &primary_key_
) || !slice
.empty()) {
3619 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3620 *s
= InternalInconsistencyStatus();
3624 std::string primary_leveldb_key
=
3625 ObjectStoreDataKey::Encode(index_data_key
.DatabaseId(),
3626 index_data_key
.ObjectStoreId(),
3631 *s
= transaction_
->transaction()->Get(primary_leveldb_key
, &result
, &found
);
3633 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3637 transaction_
->transaction()->Remove(iterator_
->Key());
3640 if (!result
.size()) {
3641 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3645 int64 object_store_data_version
;
3646 slice
= StringPiece(result
);
3647 if (!DecodeVarInt(&slice
, &object_store_data_version
)) {
3648 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3649 *s
= InternalInconsistencyStatus();
3653 if (object_store_data_version
!= index_data_version
) {
3654 transaction_
->transaction()->Remove(iterator_
->Key());
3661 class IndexCursorImpl
: public IndexedDBBackingStore::Cursor
{
3664 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3665 IndexedDBBackingStore::Transaction
* transaction
,
3667 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3668 : IndexedDBBackingStore::Cursor(backing_store
,
3673 Cursor
* Clone() override
{ return new IndexCursorImpl(this); }
3675 // IndexedDBBackingStore::Cursor
3676 IndexedDBValue
* value() override
{ return ¤t_value_
; }
3677 const IndexedDBKey
& primary_key() const override
{ return *primary_key_
; }
3678 const IndexedDBBackingStore::RecordIdentifier
& record_identifier()
3681 return record_identifier_
;
3683 bool LoadCurrentRow(leveldb::Status
* s
) override
;
3686 std::string
EncodeKey(const IndexedDBKey
& key
) override
{
3687 return IndexDataKey::Encode(cursor_options_
.database_id
,
3688 cursor_options_
.object_store_id
,
3689 cursor_options_
.index_id
,
3692 std::string
EncodeKey(const IndexedDBKey
& key
,
3693 const IndexedDBKey
& primary_key
) override
{
3694 return IndexDataKey::Encode(cursor_options_
.database_id
,
3695 cursor_options_
.object_store_id
,
3696 cursor_options_
.index_id
,
3702 explicit IndexCursorImpl(const IndexCursorImpl
* other
)
3703 : IndexedDBBackingStore::Cursor(other
),
3704 primary_key_(new IndexedDBKey(*other
->primary_key_
)),
3705 current_value_(other
->current_value_
),
3706 primary_leveldb_key_(other
->primary_leveldb_key_
) {}
3708 scoped_ptr
<IndexedDBKey
> primary_key_
;
3709 IndexedDBValue current_value_
;
3710 std::string primary_leveldb_key_
;
3712 DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl
);
3715 bool IndexCursorImpl::LoadCurrentRow(leveldb::Status
* s
) {
3716 StringPiece
slice(iterator_
->Key());
3717 IndexDataKey index_data_key
;
3718 if (!IndexDataKey::Decode(&slice
, &index_data_key
)) {
3719 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3720 *s
= InvalidDBKeyStatus();
3724 current_key_
= index_data_key
.user_key();
3725 DCHECK(current_key_
);
3727 slice
= StringPiece(iterator_
->Value());
3728 int64 index_data_version
;
3729 if (!DecodeVarInt(&slice
, &index_data_version
)) {
3730 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3731 *s
= InternalInconsistencyStatus();
3734 if (!DecodeIDBKey(&slice
, &primary_key_
)) {
3735 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3736 *s
= InvalidDBKeyStatus();
3740 DCHECK_EQ(index_data_key
.DatabaseId(), database_id_
);
3741 primary_leveldb_key_
=
3742 ObjectStoreDataKey::Encode(index_data_key
.DatabaseId(),
3743 index_data_key
.ObjectStoreId(),
3748 *s
= transaction_
->transaction()->Get(primary_leveldb_key_
, &result
, &found
);
3750 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3754 transaction_
->transaction()->Remove(iterator_
->Key());
3757 if (!result
.size()) {
3758 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3762 int64 object_store_data_version
;
3763 slice
= StringPiece(result
);
3764 if (!DecodeVarInt(&slice
, &object_store_data_version
)) {
3765 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3766 *s
= InternalInconsistencyStatus();
3770 if (object_store_data_version
!= index_data_version
) {
3771 transaction_
->transaction()->Remove(iterator_
->Key());
3775 current_value_
.bits
= slice
.as_string();
3776 *s
= transaction_
->GetBlobInfoForRecord(database_id_
, primary_leveldb_key_
,
3781 bool ObjectStoreCursorOptions(
3782 LevelDBTransaction
* transaction
,
3784 int64 object_store_id
,
3785 const IndexedDBKeyRange
& range
,
3786 blink::WebIDBCursorDirection direction
,
3787 IndexedDBBackingStore::Cursor::CursorOptions
* cursor_options
) {
3788 cursor_options
->database_id
= database_id
;
3789 cursor_options
->object_store_id
= object_store_id
;
3791 bool lower_bound
= range
.lower().IsValid();
3792 bool upper_bound
= range
.upper().IsValid();
3793 cursor_options
->forward
=
3794 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3795 direction
== blink::WebIDBCursorDirectionNext
);
3796 cursor_options
->unique
=
3797 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3798 direction
== blink::WebIDBCursorDirectionPrevNoDuplicate
);
3801 cursor_options
->low_key
=
3802 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MinIDBKey());
3803 cursor_options
->low_open
= true; // Not included.
3805 cursor_options
->low_key
=
3806 ObjectStoreDataKey::Encode(database_id
, object_store_id
, range
.lower());
3807 cursor_options
->low_open
= range
.lower_open();
3813 cursor_options
->high_key
=
3814 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MaxIDBKey());
3816 if (cursor_options
->forward
) {
3817 cursor_options
->high_open
= true; // Not included.
3819 // We need a key that exists.
3820 // TODO(cmumford): Handle this error (crbug.com/363397)
3821 if (!FindGreatestKeyLessThanOrEqual(transaction
,
3822 cursor_options
->high_key
,
3823 &cursor_options
->high_key
,
3826 cursor_options
->high_open
= false;
3829 cursor_options
->high_key
=
3830 ObjectStoreDataKey::Encode(database_id
, object_store_id
, range
.upper());
3831 cursor_options
->high_open
= range
.upper_open();
3833 if (!cursor_options
->forward
) {
3834 // For reverse cursors, we need a key that exists.
3835 std::string found_high_key
;
3836 // TODO(cmumford): Handle this error (crbug.com/363397)
3837 if (!FindGreatestKeyLessThanOrEqual(
3838 transaction
, cursor_options
->high_key
, &found_high_key
, &s
))
3841 // If the target key should not be included, but we end up with a smaller
3842 // key, we should include that.
3843 if (cursor_options
->high_open
&&
3844 CompareIndexKeys(found_high_key
, cursor_options
->high_key
) < 0)
3845 cursor_options
->high_open
= false;
3847 cursor_options
->high_key
= found_high_key
;
3854 bool IndexCursorOptions(
3855 LevelDBTransaction
* transaction
,
3857 int64 object_store_id
,
3859 const IndexedDBKeyRange
& range
,
3860 blink::WebIDBCursorDirection direction
,
3861 IndexedDBBackingStore::Cursor::CursorOptions
* cursor_options
) {
3862 DCHECK(transaction
);
3863 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
3866 cursor_options
->database_id
= database_id
;
3867 cursor_options
->object_store_id
= object_store_id
;
3868 cursor_options
->index_id
= index_id
;
3870 bool lower_bound
= range
.lower().IsValid();
3871 bool upper_bound
= range
.upper().IsValid();
3872 cursor_options
->forward
=
3873 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3874 direction
== blink::WebIDBCursorDirectionNext
);
3875 cursor_options
->unique
=
3876 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3877 direction
== blink::WebIDBCursorDirectionPrevNoDuplicate
);
3880 cursor_options
->low_key
=
3881 IndexDataKey::EncodeMinKey(database_id
, object_store_id
, index_id
);
3882 cursor_options
->low_open
= false; // Included.
3884 cursor_options
->low_key
= IndexDataKey::Encode(
3885 database_id
, object_store_id
, index_id
, range
.lower());
3886 cursor_options
->low_open
= range
.lower_open();
3892 cursor_options
->high_key
=
3893 IndexDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
3894 cursor_options
->high_open
= false; // Included.
3896 if (!cursor_options
->forward
) { // We need a key that exists.
3897 if (!FindGreatestKeyLessThanOrEqual(transaction
,
3898 cursor_options
->high_key
,
3899 &cursor_options
->high_key
,
3902 cursor_options
->high_open
= false;
3905 cursor_options
->high_key
= IndexDataKey::Encode(
3906 database_id
, object_store_id
, index_id
, range
.upper());
3907 cursor_options
->high_open
= range
.upper_open();
3909 std::string found_high_key
;
3910 // Seek to the *last* key in the set of non-unique keys
3911 // TODO(cmumford): Handle this error (crbug.com/363397)
3912 if (!FindGreatestKeyLessThanOrEqual(
3913 transaction
, cursor_options
->high_key
, &found_high_key
, &s
))
3916 // If the target key should not be included, but we end up with a smaller
3917 // key, we should include that.
3918 if (cursor_options
->high_open
&&
3919 CompareIndexKeys(found_high_key
, cursor_options
->high_key
) < 0)
3920 cursor_options
->high_open
= false;
3922 cursor_options
->high_key
= found_high_key
;
3928 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3929 IndexedDBBackingStore::OpenObjectStoreCursor(
3930 IndexedDBBackingStore::Transaction
* transaction
,
3932 int64 object_store_id
,
3933 const IndexedDBKeyRange
& range
,
3934 blink::WebIDBCursorDirection direction
,
3935 leveldb::Status
* s
) {
3936 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3937 *s
= leveldb::Status::OK();
3938 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3939 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3940 if (!ObjectStoreCursorOptions(leveldb_transaction
,
3946 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3947 scoped_ptr
<ObjectStoreCursorImpl
> cursor(new ObjectStoreCursorImpl(
3948 this, transaction
, database_id
, cursor_options
));
3949 if (!cursor
->FirstSeek(s
))
3950 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3952 return cursor
.Pass();
3955 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3956 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3957 IndexedDBBackingStore::Transaction
* transaction
,
3959 int64 object_store_id
,
3960 const IndexedDBKeyRange
& range
,
3961 blink::WebIDBCursorDirection direction
,
3962 leveldb::Status
* s
) {
3963 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3964 *s
= leveldb::Status::OK();
3965 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3966 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3967 if (!ObjectStoreCursorOptions(leveldb_transaction
,
3973 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3974 scoped_ptr
<ObjectStoreKeyCursorImpl
> cursor(new ObjectStoreKeyCursorImpl(
3975 this, transaction
, database_id
, cursor_options
));
3976 if (!cursor
->FirstSeek(s
))
3977 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3979 return cursor
.Pass();
3982 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3983 IndexedDBBackingStore::OpenIndexKeyCursor(
3984 IndexedDBBackingStore::Transaction
* transaction
,
3986 int64 object_store_id
,
3988 const IndexedDBKeyRange
& range
,
3989 blink::WebIDBCursorDirection direction
,
3990 leveldb::Status
* s
) {
3991 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3992 *s
= leveldb::Status::OK();
3993 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3994 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3995 if (!IndexCursorOptions(leveldb_transaction
,
4002 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
4003 scoped_ptr
<IndexKeyCursorImpl
> cursor(
4004 new IndexKeyCursorImpl(this, transaction
, database_id
, cursor_options
));
4005 if (!cursor
->FirstSeek(s
))
4006 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
4008 return cursor
.Pass();
4011 scoped_ptr
<IndexedDBBackingStore::Cursor
>
4012 IndexedDBBackingStore::OpenIndexCursor(
4013 IndexedDBBackingStore::Transaction
* transaction
,
4015 int64 object_store_id
,
4017 const IndexedDBKeyRange
& range
,
4018 blink::WebIDBCursorDirection direction
,
4019 leveldb::Status
* s
) {
4020 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
4021 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
4022 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
4023 if (!IndexCursorOptions(leveldb_transaction
,
4030 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
4031 scoped_ptr
<IndexCursorImpl
> cursor(
4032 new IndexCursorImpl(this, transaction
, database_id
, cursor_options
));
4033 if (!cursor
->FirstSeek(s
))
4034 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
4036 return cursor
.Pass();
4039 IndexedDBBackingStore::Transaction::Transaction(
4040 IndexedDBBackingStore
* backing_store
)
4041 : backing_store_(backing_store
), database_id_(-1), committing_(false) {
4044 IndexedDBBackingStore::Transaction::~Transaction() {
4045 STLDeleteContainerPairSecondPointers(
4046 blob_change_map_
.begin(), blob_change_map_
.end());
4047 STLDeleteContainerPairSecondPointers(incognito_blob_map_
.begin(),
4048 incognito_blob_map_
.end());
4049 DCHECK(!committing_
);
4052 void IndexedDBBackingStore::Transaction::Begin() {
4053 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
4054 DCHECK(!transaction_
.get());
4055 transaction_
= IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4056 backing_store_
->db_
.get());
4058 // If incognito, this snapshots blobs just as the above transaction_
4059 // constructor snapshots the leveldb.
4060 for (const auto& iter
: backing_store_
->incognito_blob_map_
)
4061 incognito_blob_map_
[iter
.first
] = iter
.second
->Clone().release();
4064 static GURL
GetURLFromUUID(const string
& uuid
) {
4065 return GURL("blob:uuid/" + uuid
);
4068 leveldb::Status
IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
4069 BlobEntryKeyValuePairVec
* new_blob_entries
,
4070 WriteDescriptorVec
* new_files_to_write
) {
4071 if (backing_store_
->is_incognito())
4072 return leveldb::Status::OK();
4074 DCHECK(new_blob_entries
->empty());
4075 DCHECK(new_files_to_write
->empty());
4076 DCHECK(blobs_to_write_
.empty());
4078 if (blob_change_map_
.empty())
4079 return leveldb::Status::OK();
4081 // Create LevelDBTransaction for the name generator seed and add-journal.
4082 scoped_refptr
<LevelDBTransaction
> pre_transaction
=
4083 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4084 backing_store_
->db_
.get());
4086 for (auto& iter
: blob_change_map_
) {
4087 std::vector
<IndexedDBBlobInfo
*> new_blob_keys
;
4088 for (auto& entry
: iter
.second
->mutable_blob_info()) {
4089 int64 next_blob_key
= -1;
4090 bool result
= GetBlobKeyGeneratorCurrentNumber(
4091 pre_transaction
.get(), database_id_
, &next_blob_key
);
4092 if (!result
|| next_blob_key
< 0)
4093 return InternalInconsistencyStatus();
4094 blobs_to_write_
.push_back(std::make_pair(database_id_
, next_blob_key
));
4095 if (entry
.is_file() && !entry
.file_path().empty()) {
4096 new_files_to_write
->push_back(
4097 WriteDescriptor(entry
.file_path(), next_blob_key
, entry
.size(),
4098 entry
.last_modified()));
4100 new_files_to_write
->push_back(
4101 WriteDescriptor(GetURLFromUUID(entry
.uuid()), next_blob_key
,
4102 entry
.size(), entry
.last_modified()));
4104 entry
.set_key(next_blob_key
);
4105 new_blob_keys
.push_back(&entry
);
4106 result
= UpdateBlobKeyGeneratorCurrentNumber(
4107 pre_transaction
.get(), database_id_
, next_blob_key
+ 1);
4109 return InternalInconsistencyStatus();
4111 BlobEntryKey blob_entry_key
;
4112 StringPiece
key_piece(iter
.second
->key());
4113 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece
, &blob_entry_key
)) {
4115 return InternalInconsistencyStatus();
4117 new_blob_entries
->push_back(
4118 std::make_pair(blob_entry_key
, EncodeBlobData(new_blob_keys
)));
4121 AppendBlobsToPrimaryBlobJournal(pre_transaction
.get(), blobs_to_write_
);
4122 leveldb::Status s
= pre_transaction
->Commit();
4124 return InternalInconsistencyStatus();
4126 return leveldb::Status::OK();
4129 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
4130 if (backing_store_
->is_incognito())
4133 // Look up all old files to remove as part of the transaction, store their
4134 // names in blobs_to_remove_, and remove their old blob data entries.
4135 for (const auto& iter
: blob_change_map_
) {
4136 BlobEntryKey blob_entry_key
;
4137 StringPiece
key_piece(iter
.second
->key());
4138 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece
, &blob_entry_key
)) {
4140 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4141 transaction_
= NULL
;
4144 if (database_id_
< 0)
4145 database_id_
= blob_entry_key
.database_id();
4147 DCHECK_EQ(database_id_
, blob_entry_key
.database_id());
4148 std::string blob_entry_key_bytes
= blob_entry_key
.Encode();
4150 std::string blob_entry_value_bytes
;
4151 leveldb::Status s
= transaction_
->Get(
4152 blob_entry_key_bytes
, &blob_entry_value_bytes
, &found
);
4153 if (s
.ok() && found
) {
4154 std::vector
<IndexedDBBlobInfo
> blob_info
;
4155 if (!DecodeBlobData(blob_entry_value_bytes
, &blob_info
)) {
4156 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4157 transaction_
= NULL
;
4160 for (const auto& blob
: blob_info
) {
4161 blobs_to_remove_
.push_back(std::make_pair(database_id_
, blob
.key()));
4162 transaction_
->Remove(blob_entry_key_bytes
);
4169 void IndexedDBBackingStore::Transaction::PartitionBlobsToRemove(
4170 BlobJournalType
* dead_blobs
,
4171 BlobJournalType
* live_blobs
) const {
4172 IndexedDBActiveBlobRegistry
* registry
=
4173 backing_store_
->active_blob_registry();
4174 for (const auto& iter
: blobs_to_remove_
) {
4175 if (registry
->MarkDeletedCheckIfUsed(iter
.first
, iter
.second
))
4176 live_blobs
->push_back(iter
);
4178 dead_blobs
->push_back(iter
);
4182 leveldb::Status
IndexedDBBackingStore::Transaction::CommitPhaseOne(
4183 scoped_refptr
<BlobWriteCallback
> callback
) {
4184 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
4185 DCHECK(transaction_
.get());
4186 DCHECK(backing_store_
->task_runner()->RunsTasksOnCurrentThread());
4190 BlobEntryKeyValuePairVec new_blob_entries
;
4191 WriteDescriptorVec new_files_to_write
;
4192 s
= HandleBlobPreTransaction(&new_blob_entries
, &new_files_to_write
);
4194 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4195 transaction_
= NULL
;
4199 DCHECK(!new_files_to_write
.size() ||
4200 KeyPrefix::IsValidDatabaseId(database_id_
));
4201 if (!CollectBlobFilesToRemove()) {
4202 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4203 transaction_
= NULL
;
4204 return InternalInconsistencyStatus();
4208 ++backing_store_
->committing_transaction_count_
;
4210 if (!new_files_to_write
.empty()) {
4211 // This kicks off the writes of the new blobs, if any.
4212 // This call will zero out new_blob_entries and new_files_to_write.
4213 WriteNewBlobs(&new_blob_entries
, &new_files_to_write
, callback
);
4215 callback
->Run(true);
4218 return leveldb::Status::OK();
4221 leveldb::Status
IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
4222 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
4225 DCHECK(committing_
);
4226 committing_
= false;
4227 DCHECK_GT(backing_store_
->committing_transaction_count_
, 0UL);
4228 --backing_store_
->committing_transaction_count_
;
4230 BlobJournalType primary_journal
, live_journal
, saved_primary_journal
,
4232 if (!blob_change_map_
.empty()) {
4233 IDB_TRACE("IndexedDBBackingStore::Transaction.BlobJournal");
4234 // Read the persisted states of the primary/live blob journals,
4235 // so that they can be updated correctly by the transaction.
4236 scoped_refptr
<LevelDBTransaction
> journal_transaction
=
4237 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4238 backing_store_
->db_
.get());
4239 s
= GetPrimaryBlobJournal(journal_transaction
.get(), &primary_journal
);
4242 s
= GetLiveBlobJournal(journal_transaction
.get(), &live_journal
);
4246 // Remove newly added blobs from the journal - they will be accounted
4247 // for in blob entry tables in the transaction.
4248 std::sort(primary_journal
.begin(), primary_journal
.end());
4249 std::sort(blobs_to_write_
.begin(), blobs_to_write_
.end());
4250 BlobJournalType new_journal
= base::STLSetDifference
<BlobJournalType
>(
4251 primary_journal
, blobs_to_write_
);
4252 primary_journal
.swap(new_journal
);
4254 // Append newly deleted blobs to appropriate primary/live journals.
4255 saved_primary_journal
= primary_journal
;
4256 BlobJournalType live_blobs
;
4257 if (!blobs_to_remove_
.empty()) {
4258 DCHECK(!backing_store_
->is_incognito());
4259 PartitionBlobsToRemove(&dead_blobs
, &live_blobs
);
4261 primary_journal
.insert(primary_journal
.end(), dead_blobs
.begin(),
4263 live_journal
.insert(live_journal
.end(), live_blobs
.begin(),
4265 UpdatePrimaryBlobJournal(transaction_
.get(), primary_journal
);
4266 UpdateLiveBlobJournal(transaction_
.get(), live_journal
);
4269 // Actually commit. If this succeeds, the journals will appropriately
4270 // reflect pending blob work - dead files that should be deleted
4271 // immediately, and live files to monitor.
4272 s
= transaction_
->Commit();
4273 transaction_
= NULL
;
4276 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD
);
4280 if (backing_store_
->is_incognito()) {
4281 if (!blob_change_map_
.empty()) {
4282 BlobChangeMap
& target_map
= backing_store_
->incognito_blob_map_
;
4283 for (auto& iter
: blob_change_map_
) {
4284 BlobChangeMap::iterator target_record
= target_map
.find(iter
.first
);
4285 if (target_record
!= target_map
.end()) {
4286 delete target_record
->second
;
4287 target_map
.erase(target_record
);
4290 target_map
[iter
.first
] = iter
.second
;
4295 return leveldb::Status::OK();
4298 // Actually delete dead blob files, then remove those entries
4299 // from the persisted primary journal.
4300 if (dead_blobs
.empty())
4301 return leveldb::Status::OK();
4303 DCHECK(!blob_change_map_
.empty());
4305 s
= backing_store_
->CleanUpBlobJournalEntries(dead_blobs
);
4307 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4311 scoped_refptr
<LevelDBTransaction
> update_journal_transaction
=
4312 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
4313 backing_store_
->db_
.get());
4314 UpdatePrimaryBlobJournal(update_journal_transaction
.get(),
4315 saved_primary_journal
);
4316 s
= update_journal_transaction
->Commit();
4321 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
4322 : public IndexedDBBackingStore::BlobWriteCallback
{
4324 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction
* transaction
,
4325 scoped_refptr
<BlobWriteCallback
> callback
)
4326 : transaction_(transaction
), callback_(callback
) {}
4327 void Run(bool succeeded
) override
{
4328 IDB_ASYNC_TRACE_END("IndexedDBBackingStore::Transaction::WriteNewBlobs",
4330 callback_
->Run(succeeded
);
4331 if (succeeded
) // Else it's already been deleted during rollback.
4332 transaction_
->chained_blob_writer_
= NULL
;
4336 ~BlobWriteCallbackWrapper() override
{}
4337 friend class base::RefCounted
<IndexedDBBackingStore::BlobWriteCallback
>;
4339 IndexedDBBackingStore::Transaction
* transaction_
;
4340 scoped_refptr
<BlobWriteCallback
> callback_
;
4342 DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper
);
4345 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
4346 BlobEntryKeyValuePairVec
* new_blob_entries
,
4347 WriteDescriptorVec
* new_files_to_write
,
4348 scoped_refptr
<BlobWriteCallback
> callback
) {
4349 IDB_ASYNC_TRACE_BEGIN("IndexedDBBackingStore::Transaction::WriteNewBlobs",
4351 DCHECK_GT(new_files_to_write
->size(), 0UL);
4352 DCHECK_GT(database_id_
, 0);
4353 for (auto& blob_entry_iter
: *new_blob_entries
) {
4354 // Add the new blob-table entry for each blob to the main transaction, or
4355 // remove any entry that may exist if there's no new one.
4356 if (blob_entry_iter
.second
.empty())
4357 transaction_
->Remove(blob_entry_iter
.first
.Encode());
4359 transaction_
->Put(blob_entry_iter
.first
.Encode(),
4360 &blob_entry_iter
.second
);
4362 // Creating the writer will start it going asynchronously.
4363 chained_blob_writer_
=
4364 new ChainedBlobWriterImpl(database_id_
,
4367 new BlobWriteCallbackWrapper(this, callback
));
4370 void IndexedDBBackingStore::Transaction::Rollback() {
4371 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
4373 committing_
= false;
4374 DCHECK_GT(backing_store_
->committing_transaction_count_
, 0UL);
4375 --backing_store_
->committing_transaction_count_
;
4378 if (chained_blob_writer_
.get()) {
4379 chained_blob_writer_
->Abort();
4380 chained_blob_writer_
= NULL
;
4382 if (transaction_
.get() == NULL
)
4384 transaction_
->Rollback();
4385 transaction_
= NULL
;
4388 IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
4389 const std::string
& key
,
4390 int64 object_store_id
)
4391 : key_(key
), object_store_id_(object_store_id
) {
4394 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
4397 void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
4398 std::vector
<IndexedDBBlobInfo
>* blob_info
) {
4401 blob_info_
.swap(*blob_info
);
4404 void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
4405 ScopedVector
<storage::BlobDataHandle
>* handles
) {
4408 handles_
.swap(*handles
);
4411 scoped_ptr
<IndexedDBBackingStore::BlobChangeRecord
>
4412 IndexedDBBackingStore::BlobChangeRecord::Clone() const {
4413 scoped_ptr
<IndexedDBBackingStore::BlobChangeRecord
> record(
4414 new BlobChangeRecord(key_
, object_store_id_
));
4415 record
->blob_info_
= blob_info_
;
4417 for (const auto* handle
: handles_
)
4418 record
->handles_
.push_back(new storage::BlobDataHandle(*handle
));
4419 return record
.Pass();
4422 leveldb::Status
IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
4424 int64 object_store_id
,
4425 const std::string
& object_store_data_key
,
4426 std::vector
<IndexedDBBlobInfo
>* blob_info
,
4427 ScopedVector
<storage::BlobDataHandle
>* handles
) {
4428 if (!blob_info
|| blob_info
->empty()) {
4429 blob_change_map_
.erase(object_store_data_key
);
4430 incognito_blob_map_
.erase(object_store_data_key
);
4432 BlobEntryKey blob_entry_key
;
4433 StringPiece
leveldb_key_piece(object_store_data_key
);
4434 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece
,
4437 return InternalInconsistencyStatus();
4442 transaction()->Get(blob_entry_key
.Encode(), &value
, &found
);
4446 return leveldb::Status::OK();
4449 database_id
, object_store_id
, object_store_data_key
, blob_info
, handles
);
4450 return leveldb::Status::OK();
4453 // This is storing an info, even if empty, even if the previous key had no blob
4454 // info that we know of. It duplicates a bunch of information stored in the
4455 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
4456 // changes to exists or index keys here.
4457 void IndexedDBBackingStore::Transaction::PutBlobInfo(
4459 int64 object_store_id
,
4460 const std::string
& object_store_data_key
,
4461 std::vector
<IndexedDBBlobInfo
>* blob_info
,
4462 ScopedVector
<storage::BlobDataHandle
>* handles
) {
4463 DCHECK_GT(object_store_data_key
.size(), 0UL);
4464 if (database_id_
< 0)
4465 database_id_
= database_id
;
4466 DCHECK_EQ(database_id_
, database_id
);
4468 BlobChangeMap::iterator it
= blob_change_map_
.find(object_store_data_key
);
4469 BlobChangeRecord
* record
= NULL
;
4470 if (it
== blob_change_map_
.end()) {
4471 record
= new BlobChangeRecord(object_store_data_key
, object_store_id
);
4472 blob_change_map_
[object_store_data_key
] = record
;
4474 record
= it
->second
;
4476 DCHECK_EQ(record
->object_store_id(), object_store_id
);
4477 record
->SetBlobInfo(blob_info
);
4478 record
->SetHandles(handles
);
4479 DCHECK(!handles
|| !handles
->size());
4482 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4486 base::Time last_modified
)
4491 last_modified_(last_modified
) {
4494 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4495 const FilePath
& file_path
,
4498 base::Time last_modified
)
4500 file_path_(file_path
),
4503 last_modified_(last_modified
) {
4506 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4507 const WriteDescriptor
& other
) = default;
4508 IndexedDBBackingStore::Transaction::WriteDescriptor::~WriteDescriptor() =
4510 IndexedDBBackingStore::Transaction::WriteDescriptor
&
4511 IndexedDBBackingStore::Transaction::WriteDescriptor::
4512 operator=(const WriteDescriptor
& other
) = default;
4514 } // namespace content