1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/indexed_db/indexed_db_backing_store.h"
7 #include "base/file_util.h"
8 #include "base/files/file_path.h"
9 #include "base/format_macros.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "content/browser/child_process_security_policy_impl.h"
18 #include "content/browser/indexed_db/indexed_db_blob_info.h"
19 #include "content/browser/indexed_db/indexed_db_class_factory.h"
20 #include "content/browser/indexed_db/indexed_db_database_error.h"
21 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
22 #include "content/browser/indexed_db/indexed_db_metadata.h"
23 #include "content/browser/indexed_db/indexed_db_tracing.h"
24 #include "content/browser/indexed_db/indexed_db_value.h"
25 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
26 #include "content/browser/indexed_db/leveldb/leveldb_database.h"
27 #include "content/browser/indexed_db/leveldb/leveldb_factory.h"
28 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
29 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
30 #include "content/common/indexed_db/indexed_db_key.h"
31 #include "content/common/indexed_db/indexed_db_key_path.h"
32 #include "content/common/indexed_db/indexed_db_key_range.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "net/url_request/url_request_context.h"
35 #include "third_party/WebKit/public/platform/WebIDBTypes.h"
36 #include "third_party/WebKit/public/web/WebSerializedScriptValueVersion.h"
37 #include "third_party/leveldatabase/env_chromium.h"
38 #include "webkit/browser/blob/blob_data_handle.h"
39 #include "webkit/browser/fileapi/file_stream_writer.h"
40 #include "webkit/browser/fileapi/file_writer_delegate.h"
41 #include "webkit/browser/fileapi/local_file_stream_writer.h"
42 #include "webkit/common/database/database_identifier.h"
45 using base::StringPiece
;
46 using storage::FileWriterDelegate
;
52 FilePath
GetBlobDirectoryName(const FilePath
& pathBase
, int64 database_id
) {
53 return pathBase
.AppendASCII(base::StringPrintf("%" PRIx64
, database_id
));
56 FilePath
GetBlobDirectoryNameForKey(const FilePath
& pathBase
,
59 FilePath path
= GetBlobDirectoryName(pathBase
, database_id
);
60 path
= path
.AppendASCII(base::StringPrintf(
61 "%02x", static_cast<int>(key
& 0x000000000000ff00) >> 8));
65 FilePath
GetBlobFileNameForKey(const FilePath
& pathBase
,
68 FilePath path
= GetBlobDirectoryNameForKey(pathBase
, database_id
, key
);
69 path
= path
.AppendASCII(base::StringPrintf("%" PRIx64
, key
));
73 bool MakeIDBBlobDirectory(const FilePath
& pathBase
,
76 FilePath path
= GetBlobDirectoryNameForKey(pathBase
, database_id
, key
);
77 return base::CreateDirectory(path
);
80 static std::string
ComputeOriginIdentifier(const GURL
& origin_url
) {
81 return storage::GetIdentifierFromOrigin(origin_url
) + "@1";
84 static base::FilePath
ComputeFileName(const GURL
& origin_url
) {
85 return base::FilePath()
86 .AppendASCII(storage::GetIdentifierFromOrigin(origin_url
))
87 .AddExtension(FILE_PATH_LITERAL(".indexeddb.leveldb"));
90 static base::FilePath
ComputeBlobPath(const GURL
& origin_url
) {
91 return base::FilePath()
92 .AppendASCII(storage::GetIdentifierFromOrigin(origin_url
))
93 .AddExtension(FILE_PATH_LITERAL(".indexeddb.blob"));
96 static base::FilePath
ComputeCorruptionFileName(const GURL
& origin_url
) {
97 return ComputeFileName(origin_url
)
98 .Append(FILE_PATH_LITERAL("corruption_info.json"));
103 static const int64 kKeyGeneratorInitialNumber
=
104 1; // From the IndexedDB specification.
106 enum IndexedDBBackingStoreErrorSource
{
107 // 0 - 2 are no longer used.
108 FIND_KEY_IN_INDEX
= 3,
109 GET_IDBDATABASE_METADATA
,
111 GET_KEY_GENERATOR_CURRENT_NUMBER
,
114 KEY_EXISTS_IN_OBJECT_STORE
,
117 GET_PRIMARY_KEY_VIA_INDEX
,
121 SET_MAX_OBJECT_STORE_ID
,
124 GET_NEW_VERSION_NUMBER
,
125 CREATE_IDBDATABASE_METADATA
,
127 TRANSACTION_COMMIT_METHOD
, // TRANSACTION_COMMIT is a WinNT.h macro
133 GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER
,
134 GET_BLOB_INFO_FOR_RECORD
,
138 static void RecordInternalError(const char* type
,
139 IndexedDBBackingStoreErrorSource location
) {
141 name
.append("WebCore.IndexedDB.BackingStore.").append(type
).append("Error");
142 base::Histogram::FactoryGet(name
,
145 INTERNAL_ERROR_MAX
+ 1,
146 base::HistogramBase::kUmaTargetedHistogramFlag
)
150 // Use to signal conditions caused by data corruption.
151 // A macro is used instead of an inline function so that the assert and log
152 // report the line number.
153 #define REPORT_ERROR(type, location) \
155 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
156 RecordInternalError(type, location); \
159 #define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location)
160 #define INTERNAL_CONSISTENCY_ERROR(location) \
161 REPORT_ERROR("Consistency", location)
162 #define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
164 // Use to signal conditions that usually indicate developer error, but
165 // could be caused by data corruption. A macro is used instead of an
166 // inline function so that the assert and log report the line number.
167 // TODO(cmumford): Improve test coverage so that all error conditions are
168 // "tested" and then delete this macro.
169 #define REPORT_ERROR_UNTESTED(type, location) \
171 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
173 RecordInternalError(type, location); \
176 #define INTERNAL_READ_ERROR_UNTESTED(location) \
177 REPORT_ERROR_UNTESTED("Read", location)
178 #define INTERNAL_CONSISTENCY_ERROR_UNTESTED(location) \
179 REPORT_ERROR_UNTESTED("Consistency", location)
180 #define INTERNAL_WRITE_ERROR_UNTESTED(location) \
181 REPORT_ERROR_UNTESTED("Write", location)
183 static void PutBool(LevelDBTransaction
* transaction
,
184 const StringPiece
& key
,
187 EncodeBool(value
, &buffer
);
188 transaction
->Put(key
, &buffer
);
191 // Was able to use LevelDB to read the data w/o error, but the data read was not
192 // in the expected format.
193 static leveldb::Status
InternalInconsistencyStatus() {
194 return leveldb::Status::Corruption("Internal inconsistency");
197 static leveldb::Status
InvalidDBKeyStatus() {
198 return leveldb::Status::InvalidArgument("Invalid database key ID");
201 static leveldb::Status
IOErrorStatus() {
202 return leveldb::Status::IOError("IO Error");
205 template <typename DBOrTransaction
>
206 static leveldb::Status
GetInt(DBOrTransaction
* db
,
207 const StringPiece
& key
,
211 leveldb::Status s
= db
->Get(key
, &result
, found
);
215 return leveldb::Status::OK();
216 StringPiece
slice(result
);
217 if (DecodeInt(&slice
, found_int
) && slice
.empty())
219 return InternalInconsistencyStatus();
222 static void PutInt(LevelDBTransaction
* transaction
,
223 const StringPiece
& key
,
227 EncodeInt(value
, &buffer
);
228 transaction
->Put(key
, &buffer
);
231 template <typename DBOrTransaction
>
232 WARN_UNUSED_RESULT
static leveldb::Status
GetVarInt(DBOrTransaction
* db
,
233 const StringPiece
& key
,
237 leveldb::Status s
= db
->Get(key
, &result
, found
);
241 return leveldb::Status::OK();
242 StringPiece
slice(result
);
243 if (DecodeVarInt(&slice
, found_int
) && slice
.empty())
245 return InternalInconsistencyStatus();
248 static void PutVarInt(LevelDBTransaction
* transaction
,
249 const StringPiece
& key
,
252 EncodeVarInt(value
, &buffer
);
253 transaction
->Put(key
, &buffer
);
256 template <typename DBOrTransaction
>
257 WARN_UNUSED_RESULT
static leveldb::Status
GetString(
259 const StringPiece
& key
,
260 base::string16
* found_string
,
264 leveldb::Status s
= db
->Get(key
, &result
, found
);
268 return leveldb::Status::OK();
269 StringPiece
slice(result
);
270 if (DecodeString(&slice
, found_string
) && slice
.empty())
272 return InternalInconsistencyStatus();
275 static void PutString(LevelDBTransaction
* transaction
,
276 const StringPiece
& key
,
277 const base::string16
& value
) {
279 EncodeString(value
, &buffer
);
280 transaction
->Put(key
, &buffer
);
283 static void PutIDBKeyPath(LevelDBTransaction
* transaction
,
284 const StringPiece
& key
,
285 const IndexedDBKeyPath
& value
) {
287 EncodeIDBKeyPath(value
, &buffer
);
288 transaction
->Put(key
, &buffer
);
291 static int CompareKeys(const StringPiece
& a
, const StringPiece
& b
) {
292 return Compare(a
, b
, false /*index_keys*/);
295 static int CompareIndexKeys(const StringPiece
& a
, const StringPiece
& b
) {
296 return Compare(a
, b
, true /*index_keys*/);
299 int IndexedDBBackingStore::Comparator::Compare(const StringPiece
& a
,
300 const StringPiece
& b
) const {
301 return content::Compare(a
, b
, false /*index_keys*/);
304 const char* IndexedDBBackingStore::Comparator::Name() const {
308 // 0 - Initial version.
309 // 1 - Adds UserIntVersion to DatabaseMetaData.
310 // 2 - Adds DataVersion to to global metadata.
311 // 3 - Adds metadata needed for blob support.
312 static const int64 kLatestKnownSchemaVersion
= 3;
313 WARN_UNUSED_RESULT
static bool IsSchemaKnown(LevelDBDatabase
* db
, bool* known
) {
314 int64 db_schema_version
= 0;
317 GetInt(db
, SchemaVersionKey::Encode(), &db_schema_version
, &found
);
324 if (db_schema_version
> kLatestKnownSchemaVersion
) {
329 const uint32 latest_known_data_version
=
330 blink::kSerializedScriptValueVersion
;
331 int64 db_data_version
= 0;
332 s
= GetInt(db
, DataVersionKey::Encode(), &db_data_version
, &found
);
340 if (db_data_version
> latest_known_data_version
) {
349 // TODO(ericu): Move this down into the member section of this file. I'm
350 // leaving it here for this CL as it's easier to see the diffs in place.
351 WARN_UNUSED_RESULT
leveldb::Status
IndexedDBBackingStore::SetUpMetadata() {
352 const uint32 latest_known_data_version
=
353 blink::kSerializedScriptValueVersion
;
354 const std::string schema_version_key
= SchemaVersionKey::Encode();
355 const std::string data_version_key
= DataVersionKey::Encode();
357 scoped_refptr
<LevelDBTransaction
> transaction
=
358 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
360 int64 db_schema_version
= 0;
361 int64 db_data_version
= 0;
364 GetInt(transaction
.get(), schema_version_key
, &db_schema_version
, &found
);
366 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
370 // Initialize new backing store.
371 db_schema_version
= kLatestKnownSchemaVersion
;
372 PutInt(transaction
.get(), schema_version_key
, db_schema_version
);
373 db_data_version
= latest_known_data_version
;
374 PutInt(transaction
.get(), data_version_key
, db_data_version
);
375 // If a blob directory already exists for this database, blow it away. It's
376 // leftover from a partially-purged previous generation of data.
377 if (!base::DeleteFile(blob_path_
, true)) {
378 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA
);
379 return IOErrorStatus();
382 // Upgrade old backing store.
383 DCHECK_LE(db_schema_version
, kLatestKnownSchemaVersion
);
384 if (db_schema_version
< 1) {
385 db_schema_version
= 1;
386 PutInt(transaction
.get(), schema_version_key
, db_schema_version
);
387 const std::string start_key
=
388 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_
);
389 const std::string stop_key
=
390 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_
);
391 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
392 for (s
= it
->Seek(start_key
);
393 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
395 int64 database_id
= 0;
397 s
= GetInt(transaction
.get(), it
->Key(), &database_id
, &found
);
399 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
403 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA
);
404 return InternalInconsistencyStatus();
406 std::string int_version_key
= DatabaseMetaDataKey::Encode(
407 database_id
, DatabaseMetaDataKey::USER_INT_VERSION
);
408 PutVarInt(transaction
.get(),
410 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
413 if (s
.ok() && db_schema_version
< 2) {
414 db_schema_version
= 2;
415 PutInt(transaction
.get(), schema_version_key
, db_schema_version
);
416 db_data_version
= blink::kSerializedScriptValueVersion
;
417 PutInt(transaction
.get(), data_version_key
, db_data_version
);
419 if (db_schema_version
< 3) {
420 db_schema_version
= 3;
421 if (!base::DeleteFile(blob_path_
, true)) {
422 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA
);
423 return IOErrorStatus();
429 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
433 // All new values will be written using this serialization version.
435 s
= GetInt(transaction
.get(), data_version_key
, &db_data_version
, &found
);
437 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
441 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA
);
442 return InternalInconsistencyStatus();
444 if (db_data_version
< latest_known_data_version
) {
445 db_data_version
= latest_known_data_version
;
446 PutInt(transaction
.get(), data_version_key
, db_data_version
);
449 DCHECK_EQ(db_schema_version
, kLatestKnownSchemaVersion
);
450 DCHECK_EQ(db_data_version
, latest_known_data_version
);
452 s
= transaction
->Commit();
454 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA
);
458 template <typename DBOrTransaction
>
459 WARN_UNUSED_RESULT
static leveldb::Status
GetMaxObjectStoreId(
462 int64
* max_object_store_id
) {
463 const std::string max_object_store_id_key
= DatabaseMetaDataKey::Encode(
464 database_id
, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID
);
465 return GetMaxObjectStoreId(db
, max_object_store_id_key
, max_object_store_id
);
468 template <typename DBOrTransaction
>
469 WARN_UNUSED_RESULT
static leveldb::Status
GetMaxObjectStoreId(
471 const std::string
& max_object_store_id_key
,
472 int64
* max_object_store_id
) {
473 *max_object_store_id
= -1;
476 GetInt(db
, max_object_store_id_key
, max_object_store_id
, &found
);
480 *max_object_store_id
= 0;
482 DCHECK_GE(*max_object_store_id
, 0);
486 class DefaultLevelDBFactory
: public LevelDBFactory
{
488 DefaultLevelDBFactory() {}
489 virtual leveldb::Status
OpenLevelDB(const base::FilePath
& file_name
,
490 const LevelDBComparator
* comparator
,
491 scoped_ptr
<LevelDBDatabase
>* db
,
492 bool* is_disk_full
) OVERRIDE
{
493 return LevelDBDatabase::Open(file_name
, comparator
, db
, is_disk_full
);
495 virtual leveldb::Status
DestroyLevelDB(const base::FilePath
& file_name
)
497 return LevelDBDatabase::Destroy(file_name
);
501 DISALLOW_COPY_AND_ASSIGN(DefaultLevelDBFactory
);
504 static bool GetBlobKeyGeneratorCurrentNumber(
505 LevelDBTransaction
* leveldb_transaction
,
507 int64
* blob_key_generator_current_number
) {
508 const std::string key_gen_key
= DatabaseMetaDataKey::Encode(
509 database_id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
511 // Default to initial number if not found.
512 int64 cur_number
= DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber
;
516 bool ok
= leveldb_transaction
->Get(key_gen_key
, &data
, &found
).ok();
518 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
522 StringPiece
slice(data
);
523 if (!DecodeVarInt(&slice
, &cur_number
) || !slice
.empty() ||
524 !DatabaseMetaDataKey::IsValidBlobKey(cur_number
)) {
525 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
529 *blob_key_generator_current_number
= cur_number
;
533 static bool UpdateBlobKeyGeneratorCurrentNumber(
534 LevelDBTransaction
* leveldb_transaction
,
536 int64 blob_key_generator_current_number
) {
539 if (!GetBlobKeyGeneratorCurrentNumber(
540 leveldb_transaction
, database_id
, &old_number
))
542 DCHECK_LT(old_number
, blob_key_generator_current_number
);
545 DatabaseMetaDataKey::IsValidBlobKey(blob_key_generator_current_number
));
546 const std::string key
= DatabaseMetaDataKey::Encode(
547 database_id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
);
549 PutVarInt(leveldb_transaction
, key
, blob_key_generator_current_number
);
553 // TODO(ericu): Error recovery. If we persistently can't read the
554 // blob journal, the safe thing to do is to clear it and leak the blobs,
555 // though that may be costly. Still, database/directory deletion should always
556 // clean things up, and we can write an fsck that will do a full correction if
558 template <typename T
>
559 static leveldb::Status
GetBlobJournal(const StringPiece
& leveldb_key
,
560 T
* leveldb_transaction
,
561 BlobJournalType
* journal
) {
564 leveldb::Status s
= leveldb_transaction
->Get(leveldb_key
, &data
, &found
);
566 INTERNAL_READ_ERROR(READ_BLOB_JOURNAL
);
570 if (!found
|| !data
.size())
571 return leveldb::Status::OK();
572 StringPiece
slice(data
);
573 if (!DecodeBlobJournal(&slice
, journal
)) {
574 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DECODE_BLOB_JOURNAL
);
575 s
= InternalInconsistencyStatus();
580 static void ClearBlobJournal(LevelDBTransaction
* leveldb_transaction
,
581 const std::string
& level_db_key
) {
582 leveldb_transaction
->Remove(level_db_key
);
585 static void UpdatePrimaryJournalWithBlobList(
586 LevelDBTransaction
* leveldb_transaction
,
587 const BlobJournalType
& journal
) {
588 const std::string leveldb_key
= BlobJournalKey::Encode();
590 EncodeBlobJournal(journal
, &data
);
591 leveldb_transaction
->Put(leveldb_key
, &data
);
594 static void UpdateLiveBlobJournalWithBlobList(
595 LevelDBTransaction
* leveldb_transaction
,
596 const BlobJournalType
& journal
) {
597 const std::string leveldb_key
= LiveBlobJournalKey::Encode();
599 EncodeBlobJournal(journal
, &data
);
600 leveldb_transaction
->Put(leveldb_key
, &data
);
603 static leveldb::Status
MergeBlobsIntoLiveBlobJournal(
604 LevelDBTransaction
* leveldb_transaction
,
605 const BlobJournalType
& journal
) {
606 BlobJournalType old_journal
;
607 const std::string key
= LiveBlobJournalKey::Encode();
608 leveldb::Status s
= GetBlobJournal(key
, leveldb_transaction
, &old_journal
);
612 old_journal
.insert(old_journal
.end(), journal
.begin(), journal
.end());
614 UpdateLiveBlobJournalWithBlobList(leveldb_transaction
, old_journal
);
615 return leveldb::Status::OK();
618 static void UpdateBlobJournalWithDatabase(
619 LevelDBDirectTransaction
* leveldb_transaction
,
621 BlobJournalType journal
;
623 std::make_pair(database_id
, DatabaseMetaDataKey::kAllBlobsKey
));
624 const std::string key
= BlobJournalKey::Encode();
626 EncodeBlobJournal(journal
, &data
);
627 leveldb_transaction
->Put(key
, &data
);
630 static leveldb::Status
MergeDatabaseIntoLiveBlobJournal(
631 LevelDBDirectTransaction
* leveldb_transaction
,
633 BlobJournalType journal
;
634 const std::string key
= LiveBlobJournalKey::Encode();
635 leveldb::Status s
= GetBlobJournal(key
, leveldb_transaction
, &journal
);
639 std::make_pair(database_id
, DatabaseMetaDataKey::kAllBlobsKey
));
641 EncodeBlobJournal(journal
, &data
);
642 leveldb_transaction
->Put(key
, &data
);
643 return leveldb::Status::OK();
646 // Blob Data is encoded as a series of:
647 // { is_file [bool], key [int64 as varInt],
648 // type [string-with-length, may be empty],
649 // (for Blobs only) size [int64 as varInt]
650 // (for Files only) fileName [string-with-length]
652 // There is no length field; just read until you run out of data.
653 static std::string
EncodeBlobData(
654 const std::vector
<IndexedDBBlobInfo
*>& blob_info
) {
656 std::vector
<IndexedDBBlobInfo
*>::const_iterator iter
;
657 for (iter
= blob_info
.begin(); iter
!= blob_info
.end(); ++iter
) {
658 const IndexedDBBlobInfo
& info
= **iter
;
659 EncodeBool(info
.is_file(), &ret
);
660 EncodeVarInt(info
.key(), &ret
);
661 EncodeStringWithLength(info
.type(), &ret
);
663 EncodeStringWithLength(info
.file_name(), &ret
);
665 EncodeVarInt(info
.size(), &ret
);
670 static bool DecodeBlobData(const std::string
& data
,
671 std::vector
<IndexedDBBlobInfo
>* output
) {
672 std::vector
<IndexedDBBlobInfo
> ret
;
674 StringPiece
slice(data
);
675 while (!slice
.empty()) {
680 base::string16 file_name
;
682 if (!DecodeBool(&slice
, &is_file
))
684 if (!DecodeVarInt(&slice
, &key
) ||
685 !DatabaseMetaDataKey::IsValidBlobKey(key
))
687 if (!DecodeStringWithLength(&slice
, &type
))
690 if (!DecodeStringWithLength(&slice
, &file_name
))
692 ret
.push_back(IndexedDBBlobInfo(key
, type
, file_name
));
694 if (!DecodeVarInt(&slice
, &size
) || size
< 0)
696 ret
.push_back(IndexedDBBlobInfo(type
, static_cast<uint64
>(size
), key
));
704 IndexedDBBackingStore::IndexedDBBackingStore(
705 IndexedDBFactory
* indexed_db_factory
,
706 const GURL
& origin_url
,
707 const base::FilePath
& blob_path
,
708 net::URLRequestContext
* request_context
,
709 scoped_ptr
<LevelDBDatabase
> db
,
710 scoped_ptr
<LevelDBComparator
> comparator
,
711 base::SequencedTaskRunner
* task_runner
)
712 : indexed_db_factory_(indexed_db_factory
),
713 origin_url_(origin_url
),
714 blob_path_(blob_path
),
715 origin_identifier_(ComputeOriginIdentifier(origin_url
)),
716 request_context_(request_context
),
717 task_runner_(task_runner
),
719 comparator_(comparator
.Pass()),
720 active_blob_registry_(this) {
723 IndexedDBBackingStore::~IndexedDBBackingStore() {
724 if (!blob_path_
.empty() && !child_process_ids_granted_
.empty()) {
725 ChildProcessSecurityPolicyImpl
* policy
=
726 ChildProcessSecurityPolicyImpl::GetInstance();
727 std::set
<int>::const_iterator iter
;
728 for (iter
= child_process_ids_granted_
.begin();
729 iter
!= child_process_ids_granted_
.end();
731 policy
->RevokeAllPermissionsForFile(*iter
, blob_path_
);
734 STLDeleteContainerPairSecondPointers(incognito_blob_map_
.begin(),
735 incognito_blob_map_
.end());
736 // db_'s destructor uses comparator_. The order of destruction is important.
741 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
742 const std::string
& primary_key
,
744 : primary_key_(primary_key
), version_(version
) {
745 DCHECK(!primary_key
.empty());
747 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
748 : primary_key_(), version_(-1) {}
749 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
751 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
752 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
754 enum IndexedDBBackingStoreOpenResult
{
755 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS
,
756 INDEXED_DB_BACKING_STORE_OPEN_SUCCESS
,
757 INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY
,
758 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA
,
759 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED
,
760 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED
,
761 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS
,
762 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA
,
763 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR
,
764 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED
,
765 INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII
,
766 INDEXED_DB_BACKING_STORE_OPEN_DISK_FULL_DEPRECATED
,
767 INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG
,
768 INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY
,
769 INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION
,
770 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR
,
771 INDEXED_DB_BACKING_STORE_OPEN_MAX
,
775 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::Open(
776 IndexedDBFactory
* indexed_db_factory
,
777 const GURL
& origin_url
,
778 const base::FilePath
& path_base
,
779 net::URLRequestContext
* request_context
,
780 blink::WebIDBDataLoss
* data_loss
,
781 std::string
* data_loss_message
,
783 base::SequencedTaskRunner
* task_runner
,
785 leveldb::Status
* status
) {
786 *data_loss
= blink::WebIDBDataLossNone
;
787 DefaultLevelDBFactory leveldb_factory
;
788 return IndexedDBBackingStore::Open(indexed_db_factory
,
801 static std::string
OriginToCustomHistogramSuffix(const GURL
& origin_url
) {
802 if (origin_url
.host() == "docs.google.com")
804 return std::string();
807 static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result
,
808 const GURL
& origin_url
) {
809 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.BackingStore.OpenStatus",
811 INDEXED_DB_BACKING_STORE_OPEN_MAX
);
812 const std::string suffix
= OriginToCustomHistogramSuffix(origin_url
);
813 // Data from the WebCore.IndexedDB.BackingStore.OpenStatus histogram is used
814 // to generate a graph. So as not to alter the meaning of that graph,
815 // continue to collect all stats there (above) but also now collect docs stats
816 // separately (below).
817 if (!suffix
.empty()) {
818 base::LinearHistogram::FactoryGet(
819 "WebCore.IndexedDB.BackingStore.OpenStatus" + suffix
,
821 INDEXED_DB_BACKING_STORE_OPEN_MAX
,
822 INDEXED_DB_BACKING_STORE_OPEN_MAX
+ 1,
823 base::HistogramBase::kUmaTargetedHistogramFlag
)->Add(result
);
827 static bool IsPathTooLong(const base::FilePath
& leveldb_dir
) {
828 int limit
= base::GetMaximumPathComponentLength(leveldb_dir
.DirName());
830 DLOG(WARNING
) << "GetMaximumPathComponentLength returned -1";
831 // In limited testing, ChromeOS returns 143, other OSes 255.
832 #if defined(OS_CHROMEOS)
838 size_t component_length
= leveldb_dir
.BaseName().value().length();
839 if (component_length
> static_cast<uint32_t>(limit
)) {
840 DLOG(WARNING
) << "Path component length (" << component_length
841 << ") exceeds maximum (" << limit
842 << ") allowed by this filesystem.";
845 const int num_buckets
= 12;
846 UMA_HISTOGRAM_CUSTOM_COUNTS(
847 "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
857 leveldb::Status
IndexedDBBackingStore::DestroyBackingStore(
858 const base::FilePath
& path_base
,
859 const GURL
& origin_url
) {
860 const base::FilePath file_path
=
861 path_base
.Append(ComputeFileName(origin_url
));
862 DefaultLevelDBFactory leveldb_factory
;
863 return leveldb_factory
.DestroyLevelDB(file_path
);
866 bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath
& path_base
,
867 const GURL
& origin_url
,
868 std::string
* message
) {
869 const base::FilePath info_path
=
870 path_base
.Append(ComputeCorruptionFileName(origin_url
));
872 if (IsPathTooLong(info_path
))
875 const int64 max_json_len
= 4096;
877 if (!GetFileSize(info_path
, &file_size
) || file_size
> max_json_len
)
884 base::File
file(info_path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
885 bool success
= false;
886 if (file
.IsValid()) {
887 std::vector
<char> bytes(file_size
);
888 if (file_size
== file
.Read(0, &bytes
[0], file_size
)) {
889 std::string
input_js(&bytes
[0], file_size
);
890 base::JSONReader reader
;
891 scoped_ptr
<base::Value
> val(reader
.ReadToValue(input_js
));
892 if (val
&& val
->GetType() == base::Value::TYPE_DICTIONARY
) {
893 base::DictionaryValue
* dict_val
=
894 static_cast<base::DictionaryValue
*>(val
.get());
895 success
= dict_val
->GetString("message", message
);
901 base::DeleteFile(info_path
, false);
906 bool IndexedDBBackingStore::RecordCorruptionInfo(
907 const base::FilePath
& path_base
,
908 const GURL
& origin_url
,
909 const std::string
& message
) {
910 const base::FilePath info_path
=
911 path_base
.Append(ComputeCorruptionFileName(origin_url
));
912 if (IsPathTooLong(info_path
))
915 base::DictionaryValue root_dict
;
916 root_dict
.SetString("message", message
);
917 std::string output_js
;
918 base::JSONWriter::Write(&root_dict
, &output_js
);
920 base::File
file(info_path
,
921 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
924 int written
= file
.Write(0, output_js
.c_str(), output_js
.length());
925 return size_t(written
) == output_js
.length();
929 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::Open(
930 IndexedDBFactory
* indexed_db_factory
,
931 const GURL
& origin_url
,
932 const base::FilePath
& path_base
,
933 net::URLRequestContext
* request_context
,
934 blink::WebIDBDataLoss
* data_loss
,
935 std::string
* data_loss_message
,
937 LevelDBFactory
* leveldb_factory
,
938 base::SequencedTaskRunner
* task_runner
,
940 leveldb::Status
* status
) {
941 IDB_TRACE("IndexedDBBackingStore::Open");
942 DCHECK(!path_base
.empty());
943 *data_loss
= blink::WebIDBDataLossNone
;
944 *data_loss_message
= "";
945 *is_disk_full
= false;
947 *status
= leveldb::Status::OK();
949 scoped_ptr
<LevelDBComparator
> comparator(new Comparator());
951 if (!base::IsStringASCII(path_base
.AsUTF8Unsafe())) {
952 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII
,
955 if (!base::CreateDirectory(path_base
)) {
957 leveldb::Status::IOError("Unable to create IndexedDB database path");
958 LOG(ERROR
) << status
->ToString() << ": \"" << path_base
.AsUTF8Unsafe()
960 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY
,
962 return scoped_refptr
<IndexedDBBackingStore
>();
965 const base::FilePath file_path
=
966 path_base
.Append(ComputeFileName(origin_url
));
967 const base::FilePath blob_path
=
968 path_base
.Append(ComputeBlobPath(origin_url
));
970 if (IsPathTooLong(file_path
)) {
971 *status
= leveldb::Status::IOError("File path too long");
972 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG
,
974 return scoped_refptr
<IndexedDBBackingStore
>();
977 scoped_ptr
<LevelDBDatabase
> db
;
978 *status
= leveldb_factory
->OpenLevelDB(
979 file_path
, comparator
.get(), &db
, is_disk_full
);
981 DCHECK(!db
== !status
->ok());
983 if (leveldb_env::IndicatesDiskFull(*status
)) {
984 *is_disk_full
= true;
985 } else if (leveldb_env::IsCorruption(*status
)) {
986 *data_loss
= blink::WebIDBDataLossTotal
;
987 *data_loss_message
= leveldb_env::GetCorruptionMessage(*status
);
991 bool is_schema_known
= false;
993 std::string corruption_message
;
994 if (ReadCorruptionInfo(path_base
, origin_url
, &corruption_message
)) {
995 LOG(ERROR
) << "IndexedDB recovering from a corrupted (and deleted) "
997 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION
,
1000 *data_loss
= blink::WebIDBDataLossTotal
;
1001 *data_loss_message
=
1002 "IndexedDB (database was corrupt): " + corruption_message
;
1003 } else if (!IsSchemaKnown(db
.get(), &is_schema_known
)) {
1004 LOG(ERROR
) << "IndexedDB had IO error checking schema, treating it as "
1006 HistogramOpenStatus(
1007 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA
,
1010 *data_loss
= blink::WebIDBDataLossTotal
;
1011 *data_loss_message
= "I/O error checking schema";
1012 } else if (!is_schema_known
) {
1013 LOG(ERROR
) << "IndexedDB backing store had unknown schema, treating it "
1014 "as failure to open";
1015 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA
,
1018 *data_loss
= blink::WebIDBDataLossTotal
;
1019 *data_loss_message
= "Unknown schema";
1023 DCHECK(status
->ok() || !is_schema_known
|| leveldb_env::IsIOError(*status
) ||
1024 leveldb_env::IsCorruption(*status
));
1027 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_SUCCESS
, origin_url
);
1028 } else if (leveldb_env::IsIOError(*status
)) {
1029 LOG(ERROR
) << "Unable to open backing store, not trying to recover - "
1030 << status
->ToString();
1031 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY
, origin_url
);
1032 return scoped_refptr
<IndexedDBBackingStore
>();
1034 DCHECK(!is_schema_known
|| leveldb_env::IsCorruption(*status
));
1035 LOG(ERROR
) << "IndexedDB backing store open failed, attempting cleanup";
1036 *status
= leveldb_factory
->DestroyLevelDB(file_path
);
1037 if (!status
->ok()) {
1038 LOG(ERROR
) << "IndexedDB backing store cleanup failed";
1039 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED
,
1041 return scoped_refptr
<IndexedDBBackingStore
>();
1044 LOG(ERROR
) << "IndexedDB backing store cleanup succeeded, reopening";
1045 leveldb_factory
->OpenLevelDB(file_path
, comparator
.get(), &db
, NULL
);
1047 LOG(ERROR
) << "IndexedDB backing store reopen after recovery failed";
1048 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED
,
1050 return scoped_refptr
<IndexedDBBackingStore
>();
1052 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS
,
1058 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR
,
1060 return scoped_refptr
<IndexedDBBackingStore
>();
1063 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
1064 Create(indexed_db_factory
,
1073 if (clean_journal
&& backing_store
.get() &&
1074 !backing_store
->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
1075 HistogramOpenStatus(
1076 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR
, origin_url
);
1077 return scoped_refptr
<IndexedDBBackingStore
>();
1079 return backing_store
;
1083 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::OpenInMemory(
1084 const GURL
& origin_url
,
1085 base::SequencedTaskRunner
* task_runner
,
1086 leveldb::Status
* status
) {
1087 DefaultLevelDBFactory leveldb_factory
;
1088 return IndexedDBBackingStore::OpenInMemory(
1089 origin_url
, &leveldb_factory
, task_runner
, status
);
1093 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::OpenInMemory(
1094 const GURL
& origin_url
,
1095 LevelDBFactory
* leveldb_factory
,
1096 base::SequencedTaskRunner
* task_runner
,
1097 leveldb::Status
* status
) {
1098 IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
1100 scoped_ptr
<LevelDBComparator
> comparator(new Comparator());
1101 scoped_ptr
<LevelDBDatabase
> db
=
1102 LevelDBDatabase::OpenInMemory(comparator
.get());
1104 LOG(ERROR
) << "LevelDBDatabase::OpenInMemory failed.";
1105 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED
,
1107 return scoped_refptr
<IndexedDBBackingStore
>();
1109 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS
, origin_url
);
1111 return Create(NULL
/* indexed_db_factory */,
1114 NULL
/* request_context */,
1122 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::Create(
1123 IndexedDBFactory
* indexed_db_factory
,
1124 const GURL
& origin_url
,
1125 const base::FilePath
& blob_path
,
1126 net::URLRequestContext
* request_context
,
1127 scoped_ptr
<LevelDBDatabase
> db
,
1128 scoped_ptr
<LevelDBComparator
> comparator
,
1129 base::SequencedTaskRunner
* task_runner
,
1130 leveldb::Status
* status
) {
1131 // TODO(jsbell): Handle comparator name changes.
1132 scoped_refptr
<IndexedDBBackingStore
> backing_store(
1133 new IndexedDBBackingStore(indexed_db_factory
,
1140 *status
= backing_store
->SetUpMetadata();
1142 return scoped_refptr
<IndexedDBBackingStore
>();
1144 return backing_store
;
1147 void IndexedDBBackingStore::GrantChildProcessPermissions(int child_process_id
) {
1148 if (!child_process_ids_granted_
.count(child_process_id
)) {
1149 child_process_ids_granted_
.insert(child_process_id
);
1150 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
1151 child_process_id
, blob_path_
);
1155 std::vector
<base::string16
> IndexedDBBackingStore::GetDatabaseNames(
1156 leveldb::Status
* s
) {
1157 *s
= leveldb::Status::OK();
1158 std::vector
<base::string16
> found_names
;
1159 const std::string start_key
=
1160 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_
);
1161 const std::string stop_key
=
1162 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_
);
1164 DCHECK(found_names
.empty());
1166 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
1167 for (*s
= it
->Seek(start_key
);
1168 s
->ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
1170 // Decode database name (in iterator key).
1171 StringPiece
slice(it
->Key());
1172 DatabaseNameKey database_name_key
;
1173 if (!DatabaseNameKey::Decode(&slice
, &database_name_key
) ||
1175 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES
);
1179 // Decode database id (in iterator value).
1180 int64 database_id
= 0;
1181 StringPiece
valueSlice(it
->Value());
1182 if (!DecodeInt(&valueSlice
, &database_id
) || !valueSlice
.empty()) {
1183 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES
);
1187 // Look up version by id.
1189 int64 database_version
= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
;
1190 *s
= GetVarInt(db_
.get(),
1191 DatabaseMetaDataKey::Encode(
1192 database_id
, DatabaseMetaDataKey::USER_INT_VERSION
),
1195 if (!s
->ok() || !found
) {
1196 INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES
);
1200 // Ignore stale metadata from failed initial opens.
1201 if (database_version
!= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
)
1202 found_names
.push_back(database_name_key
.database_name());
1206 INTERNAL_READ_ERROR(GET_DATABASE_NAMES
);
1211 leveldb::Status
IndexedDBBackingStore::GetIDBDatabaseMetaData(
1212 const base::string16
& name
,
1213 IndexedDBDatabaseMetadata
* metadata
,
1215 const std::string key
= DatabaseNameKey::Encode(origin_identifier_
, name
);
1218 leveldb::Status s
= GetInt(db_
.get(), key
, &metadata
->id
, found
);
1220 INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA
);
1224 return leveldb::Status::OK();
1226 s
= GetString(db_
.get(),
1227 DatabaseMetaDataKey::Encode(metadata
->id
,
1228 DatabaseMetaDataKey::USER_VERSION
),
1232 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1236 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1237 return InternalInconsistencyStatus();
1240 s
= GetVarInt(db_
.get(),
1241 DatabaseMetaDataKey::Encode(
1242 metadata
->id
, DatabaseMetaDataKey::USER_INT_VERSION
),
1243 &metadata
->int_version
,
1246 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1250 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1251 return InternalInconsistencyStatus();
1254 if (metadata
->int_version
== IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
)
1255 metadata
->int_version
= IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1257 s
= GetMaxObjectStoreId(
1258 db_
.get(), metadata
->id
, &metadata
->max_object_store_id
);
1260 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1263 // We don't cache this, we just check it if it's there.
1264 int64 blob_key_generator_current_number
=
1265 DatabaseMetaDataKey::kInvalidBlobKey
;
1269 DatabaseMetaDataKey::Encode(
1270 metadata
->id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
),
1271 &blob_key_generator_current_number
,
1274 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1278 // This database predates blob support.
1280 } else if (!DatabaseMetaDataKey::IsValidBlobKey(
1281 blob_key_generator_current_number
)) {
1282 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1283 return InternalInconsistencyStatus();
1289 WARN_UNUSED_RESULT
static leveldb::Status
GetNewDatabaseId(
1290 LevelDBTransaction
* transaction
,
1293 int64 max_database_id
= -1;
1296 GetInt(transaction
, MaxDatabaseIdKey::Encode(), &max_database_id
, &found
);
1298 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID
);
1302 max_database_id
= 0;
1304 DCHECK_GE(max_database_id
, 0);
1306 int64 database_id
= max_database_id
+ 1;
1307 PutInt(transaction
, MaxDatabaseIdKey::Encode(), database_id
);
1308 *new_id
= database_id
;
1309 return leveldb::Status::OK();
1312 leveldb::Status
IndexedDBBackingStore::CreateIDBDatabaseMetaData(
1313 const base::string16
& name
,
1314 const base::string16
& version
,
1317 // TODO(jsbell): Don't persist metadata if open fails. http://crbug.com/395472
1318 scoped_refptr
<LevelDBTransaction
> transaction
=
1319 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
1321 leveldb::Status s
= GetNewDatabaseId(transaction
.get(), row_id
);
1324 DCHECK_GE(*row_id
, 0);
1326 if (int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
)
1327 int_version
= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
;
1329 PutInt(transaction
.get(),
1330 DatabaseNameKey::Encode(origin_identifier_
, name
),
1334 DatabaseMetaDataKey::Encode(*row_id
, DatabaseMetaDataKey::USER_VERSION
),
1336 PutVarInt(transaction
.get(),
1337 DatabaseMetaDataKey::Encode(*row_id
,
1338 DatabaseMetaDataKey::USER_INT_VERSION
),
1342 DatabaseMetaDataKey::Encode(
1343 *row_id
, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER
),
1344 DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber
);
1346 s
= transaction
->Commit();
1348 INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA
);
1352 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
1353 IndexedDBBackingStore::Transaction
* transaction
,
1355 int64 int_version
) {
1356 if (int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
)
1357 int_version
= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
;
1358 DCHECK_GE(int_version
, 0) << "int_version was " << int_version
;
1359 PutVarInt(transaction
->transaction(),
1360 DatabaseMetaDataKey::Encode(row_id
,
1361 DatabaseMetaDataKey::USER_INT_VERSION
),
1366 // If you're deleting a range that contains user keys that have blob info, this
1367 // won't clean up the blobs.
1368 static leveldb::Status
DeleteRangeBasic(LevelDBTransaction
* transaction
,
1369 const std::string
& begin
,
1370 const std::string
& end
,
1372 scoped_ptr
<LevelDBIterator
> it
= transaction
->CreateIterator();
1374 for (s
= it
->Seek(begin
); s
.ok() && it
->IsValid() &&
1375 (upper_open
? CompareKeys(it
->Key(), end
) < 0
1376 : CompareKeys(it
->Key(), end
) <= 0);
1378 transaction
->Remove(it
->Key());
1382 static leveldb::Status
DeleteBlobsInRange(
1383 IndexedDBBackingStore::Transaction
* transaction
,
1385 int64 object_store_id
,
1386 const std::string
& start_key
,
1387 const std::string
& end_key
,
1389 scoped_ptr
<LevelDBIterator
> it
= transaction
->transaction()->CreateIterator();
1390 leveldb::Status s
= it
->Seek(start_key
);
1391 for (; s
.ok() && it
->IsValid() &&
1392 (upper_open
? CompareKeys(it
->Key(), end_key
) < 0
1393 : CompareKeys(it
->Key(), end_key
) <= 0);
1395 StringPiece
key_piece(it
->Key());
1396 std::string user_key
=
1397 BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece
);
1398 if (!user_key
.size()) {
1399 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1400 return InternalInconsistencyStatus();
1402 transaction
->PutBlobInfo(
1403 database_id
, object_store_id
, user_key
, NULL
, NULL
);
1408 static leveldb::Status
DeleteBlobsInObjectStore(
1409 IndexedDBBackingStore::Transaction
* transaction
,
1411 int64 object_store_id
) {
1412 std::string start_key
, stop_key
;
1414 BlobEntryKey::EncodeMinKeyForObjectStore(database_id
, object_store_id
);
1416 BlobEntryKey::EncodeStopKeyForObjectStore(database_id
, object_store_id
);
1417 return DeleteBlobsInRange(
1418 transaction
, database_id
, object_store_id
, start_key
, stop_key
, true);
1421 leveldb::Status
IndexedDBBackingStore::DeleteDatabase(
1422 const base::string16
& name
) {
1423 IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
1424 scoped_ptr
<LevelDBDirectTransaction
> transaction
=
1425 LevelDBDirectTransaction::Create(db_
.get());
1428 s
= CleanUpBlobJournal(BlobJournalKey::Encode());
1432 IndexedDBDatabaseMetadata metadata
;
1433 bool success
= false;
1434 s
= GetIDBDatabaseMetaData(name
, &metadata
, &success
);
1438 return leveldb::Status::OK();
1440 const std::string start_key
= DatabaseMetaDataKey::Encode(
1441 metadata
.id
, DatabaseMetaDataKey::ORIGIN_NAME
);
1442 const std::string stop_key
= DatabaseMetaDataKey::Encode(
1443 metadata
.id
+ 1, DatabaseMetaDataKey::ORIGIN_NAME
);
1444 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
1445 for (s
= it
->Seek(start_key
);
1446 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
1448 transaction
->Remove(it
->Key());
1450 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE
);
1454 const std::string key
= DatabaseNameKey::Encode(origin_identifier_
, name
);
1455 transaction
->Remove(key
);
1457 bool need_cleanup
= false;
1458 if (active_blob_registry()->MarkDeletedCheckIfUsed(
1459 metadata
.id
, DatabaseMetaDataKey::kAllBlobsKey
)) {
1460 s
= MergeDatabaseIntoLiveBlobJournal(transaction
.get(), metadata
.id
);
1464 UpdateBlobJournalWithDatabase(transaction
.get(), metadata
.id
);
1465 need_cleanup
= true;
1468 s
= transaction
->Commit();
1470 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE
);
1475 CleanUpBlobJournal(BlobJournalKey::Encode());
1477 db_
->Compact(start_key
, stop_key
);
1481 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator
* it
,
1482 const std::string
& stop_key
,
1483 int64 object_store_id
,
1484 int64 meta_data_type
) {
1485 if (!it
->IsValid() || CompareKeys(it
->Key(), stop_key
) >= 0)
1488 StringPiece
slice(it
->Key());
1489 ObjectStoreMetaDataKey meta_data_key
;
1491 ObjectStoreMetaDataKey::Decode(&slice
, &meta_data_key
) && slice
.empty();
1493 if (meta_data_key
.ObjectStoreId() != object_store_id
)
1495 if (meta_data_key
.MetaDataType() != meta_data_type
)
1500 // TODO(jsbell): This should do some error handling rather than
1501 // plowing ahead when bad data is encountered.
1502 leveldb::Status
IndexedDBBackingStore::GetObjectStores(
1504 IndexedDBDatabaseMetadata::ObjectStoreMap
* object_stores
) {
1505 IDB_TRACE("IndexedDBBackingStore::GetObjectStores");
1506 if (!KeyPrefix::IsValidDatabaseId(database_id
))
1507 return InvalidDBKeyStatus();
1508 const std::string start_key
=
1509 ObjectStoreMetaDataKey::Encode(database_id
, 1, 0);
1510 const std::string stop_key
=
1511 ObjectStoreMetaDataKey::EncodeMaxKey(database_id
);
1513 DCHECK(object_stores
->empty());
1515 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
1516 leveldb::Status s
= it
->Seek(start_key
);
1517 while (s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0) {
1518 StringPiece
slice(it
->Key());
1519 ObjectStoreMetaDataKey meta_data_key
;
1521 ObjectStoreMetaDataKey::Decode(&slice
, &meta_data_key
) && slice
.empty();
1523 if (!ok
|| meta_data_key
.MetaDataType() != ObjectStoreMetaDataKey::NAME
) {
1524 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1525 // Possible stale metadata, but don't fail the load.
1532 int64 object_store_id
= meta_data_key
.ObjectStoreId();
1534 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
1536 base::string16 object_store_name
;
1538 StringPiece
slice(it
->Value());
1539 if (!DecodeString(&slice
, &object_store_name
) || !slice
.empty())
1540 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1546 if (!CheckObjectStoreAndMetaDataType(it
.get(),
1549 ObjectStoreMetaDataKey::KEY_PATH
)) {
1550 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1553 IndexedDBKeyPath key_path
;
1555 StringPiece
slice(it
->Value());
1556 if (!DecodeIDBKeyPath(&slice
, &key_path
) || !slice
.empty())
1557 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1563 if (!CheckObjectStoreAndMetaDataType(
1567 ObjectStoreMetaDataKey::AUTO_INCREMENT
)) {
1568 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1571 bool auto_increment
;
1573 StringPiece
slice(it
->Value());
1574 if (!DecodeBool(&slice
, &auto_increment
) || !slice
.empty())
1575 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1578 s
= it
->Next(); // Is evictable.
1581 if (!CheckObjectStoreAndMetaDataType(it
.get(),
1584 ObjectStoreMetaDataKey::EVICTABLE
)) {
1585 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1589 s
= it
->Next(); // Last version.
1592 if (!CheckObjectStoreAndMetaDataType(
1596 ObjectStoreMetaDataKey::LAST_VERSION
)) {
1597 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1601 s
= it
->Next(); // Maximum index id allocated.
1604 if (!CheckObjectStoreAndMetaDataType(
1608 ObjectStoreMetaDataKey::MAX_INDEX_ID
)) {
1609 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1614 StringPiece
slice(it
->Value());
1615 if (!DecodeInt(&slice
, &max_index_id
) || !slice
.empty())
1616 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1619 s
= it
->Next(); // [optional] has key path (is not null)
1622 if (CheckObjectStoreAndMetaDataType(it
.get(),
1625 ObjectStoreMetaDataKey::HAS_KEY_PATH
)) {
1628 StringPiece
slice(it
->Value());
1629 if (!DecodeBool(&slice
, &has_key_path
))
1630 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1632 // This check accounts for two layers of legacy coding:
1633 // (1) Initially, has_key_path was added to distinguish null vs. string.
1634 // (2) Later, null vs. string vs. array was stored in the key_path itself.
1635 // So this check is only relevant for string-type key_paths.
1636 if (!has_key_path
&&
1637 (key_path
.type() == blink::WebIDBKeyPathTypeString
&&
1638 !key_path
.string().empty())) {
1639 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1643 key_path
= IndexedDBKeyPath();
1649 int64 key_generator_current_number
= -1;
1650 if (CheckObjectStoreAndMetaDataType(
1654 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
)) {
1655 StringPiece
slice(it
->Value());
1656 if (!DecodeInt(&slice
, &key_generator_current_number
) || !slice
.empty())
1657 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1659 // TODO(jsbell): Return key_generator_current_number, cache in
1660 // object store, and write lazily to backing store. For now,
1661 // just assert that if it was written it was valid.
1662 DCHECK_GE(key_generator_current_number
, kKeyGeneratorInitialNumber
);
1668 IndexedDBObjectStoreMetadata
metadata(object_store_name
,
1673 s
= GetIndexes(database_id
, object_store_id
, &metadata
.indexes
);
1676 (*object_stores
)[object_store_id
] = metadata
;
1680 INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES
);
1685 WARN_UNUSED_RESULT
static leveldb::Status
SetMaxObjectStoreId(
1686 LevelDBTransaction
* transaction
,
1688 int64 object_store_id
) {
1689 const std::string max_object_store_id_key
= DatabaseMetaDataKey::Encode(
1690 database_id
, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID
);
1691 int64 max_object_store_id
= -1;
1692 leveldb::Status s
= GetMaxObjectStoreId(
1693 transaction
, max_object_store_id_key
, &max_object_store_id
);
1695 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID
);
1699 if (object_store_id
<= max_object_store_id
) {
1700 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID
);
1701 return InternalInconsistencyStatus();
1703 PutInt(transaction
, max_object_store_id_key
, object_store_id
);
1707 void IndexedDBBackingStore::Compact() { db_
->CompactAll(); }
1709 leveldb::Status
IndexedDBBackingStore::CreateObjectStore(
1710 IndexedDBBackingStore::Transaction
* transaction
,
1712 int64 object_store_id
,
1713 const base::string16
& name
,
1714 const IndexedDBKeyPath
& key_path
,
1715 bool auto_increment
) {
1716 IDB_TRACE("IndexedDBBackingStore::CreateObjectStore");
1717 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1718 return InvalidDBKeyStatus();
1719 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1721 SetMaxObjectStoreId(leveldb_transaction
, database_id
, object_store_id
);
1725 const std::string name_key
= ObjectStoreMetaDataKey::Encode(
1726 database_id
, object_store_id
, ObjectStoreMetaDataKey::NAME
);
1727 const std::string key_path_key
= ObjectStoreMetaDataKey::Encode(
1728 database_id
, object_store_id
, ObjectStoreMetaDataKey::KEY_PATH
);
1729 const std::string auto_increment_key
= ObjectStoreMetaDataKey::Encode(
1730 database_id
, object_store_id
, ObjectStoreMetaDataKey::AUTO_INCREMENT
);
1731 const std::string evictable_key
= ObjectStoreMetaDataKey::Encode(
1732 database_id
, object_store_id
, ObjectStoreMetaDataKey::EVICTABLE
);
1733 const std::string last_version_key
= ObjectStoreMetaDataKey::Encode(
1734 database_id
, object_store_id
, ObjectStoreMetaDataKey::LAST_VERSION
);
1735 const std::string max_index_id_key
= ObjectStoreMetaDataKey::Encode(
1736 database_id
, object_store_id
, ObjectStoreMetaDataKey::MAX_INDEX_ID
);
1737 const std::string has_key_path_key
= ObjectStoreMetaDataKey::Encode(
1738 database_id
, object_store_id
, ObjectStoreMetaDataKey::HAS_KEY_PATH
);
1739 const std::string key_generator_current_number_key
=
1740 ObjectStoreMetaDataKey::Encode(
1743 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
1744 const std::string names_key
= ObjectStoreNamesKey::Encode(database_id
, name
);
1746 PutString(leveldb_transaction
, name_key
, name
);
1747 PutIDBKeyPath(leveldb_transaction
, key_path_key
, key_path
);
1748 PutInt(leveldb_transaction
, auto_increment_key
, auto_increment
);
1749 PutInt(leveldb_transaction
, evictable_key
, false);
1750 PutInt(leveldb_transaction
, last_version_key
, 1);
1751 PutInt(leveldb_transaction
, max_index_id_key
, kMinimumIndexId
);
1752 PutBool(leveldb_transaction
, has_key_path_key
, !key_path
.IsNull());
1753 PutInt(leveldb_transaction
,
1754 key_generator_current_number_key
,
1755 kKeyGeneratorInitialNumber
);
1756 PutInt(leveldb_transaction
, names_key
, object_store_id
);
1760 leveldb::Status
IndexedDBBackingStore::DeleteObjectStore(
1761 IndexedDBBackingStore::Transaction
* transaction
,
1763 int64 object_store_id
) {
1764 IDB_TRACE("IndexedDBBackingStore::DeleteObjectStore");
1765 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1766 return InvalidDBKeyStatus();
1767 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1769 base::string16 object_store_name
;
1772 GetString(leveldb_transaction
,
1773 ObjectStoreMetaDataKey::Encode(
1774 database_id
, object_store_id
, ObjectStoreMetaDataKey::NAME
),
1778 INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1782 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1783 return InternalInconsistencyStatus();
1786 s
= DeleteBlobsInObjectStore(transaction
, database_id
, object_store_id
);
1788 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1792 s
= DeleteRangeBasic(
1793 leveldb_transaction
,
1794 ObjectStoreMetaDataKey::Encode(database_id
, object_store_id
, 0),
1795 ObjectStoreMetaDataKey::EncodeMaxKey(database_id
, object_store_id
),
1799 leveldb_transaction
->Remove(
1800 ObjectStoreNamesKey::Encode(database_id
, object_store_name
));
1802 s
= DeleteRangeBasic(
1803 leveldb_transaction
,
1804 IndexFreeListKey::Encode(database_id
, object_store_id
, 0),
1805 IndexFreeListKey::EncodeMaxKey(database_id
, object_store_id
),
1810 s
= DeleteRangeBasic(
1811 leveldb_transaction
,
1812 IndexMetaDataKey::Encode(database_id
, object_store_id
, 0, 0),
1813 IndexMetaDataKey::EncodeMaxKey(database_id
, object_store_id
),
1818 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1822 return ClearObjectStore(transaction
, database_id
, object_store_id
);
1825 leveldb::Status
IndexedDBBackingStore::GetRecord(
1826 IndexedDBBackingStore::Transaction
* transaction
,
1828 int64 object_store_id
,
1829 const IndexedDBKey
& key
,
1830 IndexedDBValue
* record
) {
1831 IDB_TRACE("IndexedDBBackingStore::GetRecord");
1832 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1833 return InvalidDBKeyStatus();
1834 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1836 const std::string leveldb_key
=
1837 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
1843 leveldb::Status s
= leveldb_transaction
->Get(leveldb_key
, &data
, &found
);
1845 INTERNAL_READ_ERROR(GET_RECORD
);
1851 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD
);
1852 return leveldb::Status::NotFound("Record contained no data");
1856 StringPiece
slice(data
);
1857 if (!DecodeVarInt(&slice
, &version
)) {
1858 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD
);
1859 return InternalInconsistencyStatus();
1862 record
->bits
= slice
.as_string();
1863 return transaction
->GetBlobInfoForRecord(database_id
, leveldb_key
, record
);
1866 WARN_UNUSED_RESULT
static leveldb::Status
GetNewVersionNumber(
1867 LevelDBTransaction
* transaction
,
1869 int64 object_store_id
,
1870 int64
* new_version_number
) {
1871 const std::string last_version_key
= ObjectStoreMetaDataKey::Encode(
1872 database_id
, object_store_id
, ObjectStoreMetaDataKey::LAST_VERSION
);
1874 *new_version_number
= -1;
1875 int64 last_version
= -1;
1878 GetInt(transaction
, last_version_key
, &last_version
, &found
);
1880 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER
);
1886 DCHECK_GE(last_version
, 0);
1888 int64 version
= last_version
+ 1;
1889 PutInt(transaction
, last_version_key
, version
);
1891 // TODO(jsbell): Think about how we want to handle the overflow scenario.
1892 DCHECK(version
> last_version
);
1894 *new_version_number
= version
;
1898 leveldb::Status
IndexedDBBackingStore::PutRecord(
1899 IndexedDBBackingStore::Transaction
* transaction
,
1901 int64 object_store_id
,
1902 const IndexedDBKey
& key
,
1903 IndexedDBValue
* value
,
1904 ScopedVector
<storage::BlobDataHandle
>* handles
,
1905 RecordIdentifier
* record_identifier
) {
1906 IDB_TRACE("IndexedDBBackingStore::PutRecord");
1907 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1908 return InvalidDBKeyStatus();
1909 DCHECK(key
.IsValid());
1911 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1913 leveldb::Status s
= GetNewVersionNumber(
1914 leveldb_transaction
, database_id
, object_store_id
, &version
);
1917 DCHECK_GE(version
, 0);
1918 const std::string object_store_data_key
=
1919 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
1922 EncodeVarInt(version
, &v
);
1923 v
.append(value
->bits
);
1925 leveldb_transaction
->Put(object_store_data_key
, &v
);
1926 s
= transaction
->PutBlobInfoIfNeeded(database_id
,
1928 object_store_data_key
,
1933 DCHECK(!handles
->size());
1935 const std::string exists_entry_key
=
1936 ExistsEntryKey::Encode(database_id
, object_store_id
, key
);
1937 std::string version_encoded
;
1938 EncodeInt(version
, &version_encoded
);
1939 leveldb_transaction
->Put(exists_entry_key
, &version_encoded
);
1941 std::string key_encoded
;
1942 EncodeIDBKey(key
, &key_encoded
);
1943 record_identifier
->Reset(key_encoded
, version
);
1947 leveldb::Status
IndexedDBBackingStore::ClearObjectStore(
1948 IndexedDBBackingStore::Transaction
* transaction
,
1950 int64 object_store_id
) {
1951 IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
1952 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1953 return InvalidDBKeyStatus();
1954 const std::string start_key
=
1955 KeyPrefix(database_id
, object_store_id
).Encode();
1956 const std::string stop_key
=
1957 KeyPrefix(database_id
, object_store_id
+ 1).Encode();
1960 DeleteRangeBasic(transaction
->transaction(), start_key
, stop_key
, true);
1962 INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE
);
1965 return DeleteBlobsInObjectStore(transaction
, database_id
, object_store_id
);
1968 leveldb::Status
IndexedDBBackingStore::DeleteRecord(
1969 IndexedDBBackingStore::Transaction
* transaction
,
1971 int64 object_store_id
,
1972 const RecordIdentifier
& record_identifier
) {
1973 IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
1974 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1975 return InvalidDBKeyStatus();
1976 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1978 const std::string object_store_data_key
= ObjectStoreDataKey::Encode(
1979 database_id
, object_store_id
, record_identifier
.primary_key());
1980 leveldb_transaction
->Remove(object_store_data_key
);
1981 leveldb::Status s
= transaction
->PutBlobInfoIfNeeded(
1982 database_id
, object_store_id
, object_store_data_key
, NULL
, NULL
);
1986 const std::string exists_entry_key
= ExistsEntryKey::Encode(
1987 database_id
, object_store_id
, record_identifier
.primary_key());
1988 leveldb_transaction
->Remove(exists_entry_key
);
1989 return leveldb::Status::OK();
1992 leveldb::Status
IndexedDBBackingStore::DeleteRange(
1993 IndexedDBBackingStore::Transaction
* transaction
,
1995 int64 object_store_id
,
1996 const IndexedDBKeyRange
& key_range
) {
1998 scoped_ptr
<IndexedDBBackingStore::Cursor
> start_cursor
=
1999 OpenObjectStoreCursor(transaction
,
2003 blink::WebIDBCursorDirectionNext
,
2008 return leveldb::Status::OK(); // Empty range == delete success.
2010 scoped_ptr
<IndexedDBBackingStore::Cursor
> end_cursor
=
2011 OpenObjectStoreCursor(transaction
,
2015 blink::WebIDBCursorDirectionPrev
,
2021 return leveldb::Status::OK(); // Empty range == delete success.
2023 BlobEntryKey start_blob_key
, end_blob_key
;
2025 std::string start_key
= ObjectStoreDataKey::Encode(
2026 database_id
, object_store_id
, start_cursor
->key());
2027 base::StringPiece
start_key_piece(start_key
);
2028 if (!BlobEntryKey::FromObjectStoreDataKey(&start_key_piece
, &start_blob_key
))
2029 return InternalInconsistencyStatus();
2030 std::string stop_key
= ObjectStoreDataKey::Encode(
2031 database_id
, object_store_id
, end_cursor
->key());
2032 base::StringPiece
stop_key_piece(stop_key
);
2033 if (!BlobEntryKey::FromObjectStoreDataKey(&stop_key_piece
, &end_blob_key
))
2034 return InternalInconsistencyStatus();
2036 s
= DeleteBlobsInRange(transaction
,
2039 start_blob_key
.Encode(),
2040 end_blob_key
.Encode(),
2044 s
= DeleteRangeBasic(transaction
->transaction(), start_key
, stop_key
, false);
2048 ExistsEntryKey::Encode(database_id
, object_store_id
, start_cursor
->key());
2050 ExistsEntryKey::Encode(database_id
, object_store_id
, end_cursor
->key());
2051 return DeleteRangeBasic(
2052 transaction
->transaction(), start_key
, stop_key
, false);
2055 leveldb::Status
IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
2056 IndexedDBBackingStore::Transaction
* transaction
,
2058 int64 object_store_id
,
2059 int64
* key_generator_current_number
) {
2060 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2061 return InvalidDBKeyStatus();
2062 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2064 const std::string key_generator_current_number_key
=
2065 ObjectStoreMetaDataKey::Encode(
2068 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
2070 *key_generator_current_number
= -1;
2075 leveldb_transaction
->Get(key_generator_current_number_key
, &data
, &found
);
2077 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2080 if (found
&& !data
.empty()) {
2081 StringPiece
slice(data
);
2082 if (!DecodeInt(&slice
, key_generator_current_number
) || !slice
.empty()) {
2083 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2084 return InternalInconsistencyStatus();
2089 // Previously, the key generator state was not stored explicitly
2090 // but derived from the maximum numeric key present in existing
2091 // data. This violates the spec as the data may be cleared but the
2092 // key generator state must be preserved.
2093 // TODO(jsbell): Fix this for all stores on database open?
2094 const std::string start_key
=
2095 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MinIDBKey());
2096 const std::string stop_key
=
2097 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MaxIDBKey());
2099 scoped_ptr
<LevelDBIterator
> it
= leveldb_transaction
->CreateIterator();
2100 int64 max_numeric_key
= 0;
2102 for (s
= it
->Seek(start_key
);
2103 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
2105 StringPiece
slice(it
->Key());
2106 ObjectStoreDataKey data_key
;
2107 if (!ObjectStoreDataKey::Decode(&slice
, &data_key
) || !slice
.empty()) {
2108 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2109 return InternalInconsistencyStatus();
2111 scoped_ptr
<IndexedDBKey
> user_key
= data_key
.user_key();
2112 if (user_key
->type() == blink::WebIDBKeyTypeNumber
) {
2113 int64 n
= static_cast<int64
>(user_key
->number());
2114 if (n
> max_numeric_key
)
2115 max_numeric_key
= n
;
2120 *key_generator_current_number
= max_numeric_key
+ 1;
2122 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
2127 leveldb::Status
IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
2128 IndexedDBBackingStore::Transaction
* transaction
,
2130 int64 object_store_id
,
2132 bool check_current
) {
2133 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2134 return InvalidDBKeyStatus();
2136 if (check_current
) {
2137 int64 current_number
;
2138 leveldb::Status s
= GetKeyGeneratorCurrentNumber(
2139 transaction
, database_id
, object_store_id
, ¤t_number
);
2142 if (new_number
<= current_number
)
2146 const std::string key_generator_current_number_key
=
2147 ObjectStoreMetaDataKey::Encode(
2150 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
2152 transaction
->transaction(), key_generator_current_number_key
, new_number
);
2153 return leveldb::Status::OK();
2156 leveldb::Status
IndexedDBBackingStore::KeyExistsInObjectStore(
2157 IndexedDBBackingStore::Transaction
* transaction
,
2159 int64 object_store_id
,
2160 const IndexedDBKey
& key
,
2161 RecordIdentifier
* found_record_identifier
,
2163 IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
2164 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2165 return InvalidDBKeyStatus();
2167 const std::string leveldb_key
=
2168 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
2172 transaction
->transaction()->Get(leveldb_key
, &data
, found
);
2174 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE
);
2178 return leveldb::Status::OK();
2180 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE
);
2181 return InternalInconsistencyStatus();
2185 StringPiece
slice(data
);
2186 if (!DecodeVarInt(&slice
, &version
))
2187 return InternalInconsistencyStatus();
2189 std::string encoded_key
;
2190 EncodeIDBKey(key
, &encoded_key
);
2191 found_record_identifier
->Reset(encoded_key
, version
);
2195 class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
2196 : public IndexedDBBackingStore::Transaction::ChainedBlobWriter
{
2198 typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
2200 ChainedBlobWriterImpl(
2202 IndexedDBBackingStore
* backing_store
,
2203 WriteDescriptorVec
* blobs
,
2204 scoped_refptr
<IndexedDBBackingStore::BlobWriteCallback
> callback
)
2205 : waiting_for_callback_(false),
2206 database_id_(database_id
),
2207 backing_store_(backing_store
),
2208 callback_(callback
),
2210 blobs_
.swap(*blobs
);
2211 iter_
= blobs_
.begin();
2212 backing_store
->task_runner()->PostTask(
2213 FROM_HERE
, base::Bind(&ChainedBlobWriterImpl::WriteNextFile
, this));
2216 virtual void set_delegate(scoped_ptr
<FileWriterDelegate
> delegate
) OVERRIDE
{
2217 delegate_
.reset(delegate
.release());
2220 virtual void ReportWriteCompletion(bool succeeded
,
2221 int64 bytes_written
) OVERRIDE
{
2222 DCHECK(waiting_for_callback_
);
2223 DCHECK(!succeeded
|| bytes_written
>= 0);
2224 waiting_for_callback_
= false;
2225 if (delegate_
.get()) // Only present for Blob, not File.
2226 content::BrowserThread::DeleteSoon(
2227 content::BrowserThread::IO
, FROM_HERE
, delegate_
.release());
2232 if (iter_
->size() != -1 && iter_
->size() != bytes_written
)
2238 callback_
->Run(false);
2242 virtual void Abort() OVERRIDE
{
2243 if (!waiting_for_callback_
)
2250 virtual ~ChainedBlobWriterImpl() {}
2252 void WriteNextFile() {
2253 DCHECK(!waiting_for_callback_
);
2255 if (iter_
== blobs_
.end()) {
2256 DCHECK(!self_ref_
.get());
2257 callback_
->Run(true);
2260 if (!backing_store_
->WriteBlobFile(database_id_
, *iter_
, this)) {
2261 callback_
->Run(false);
2264 waiting_for_callback_
= true;
2268 bool waiting_for_callback_
;
2269 scoped_refptr
<ChainedBlobWriterImpl
> self_ref_
;
2270 WriteDescriptorVec blobs_
;
2271 WriteDescriptorVec::const_iterator iter_
;
2273 IndexedDBBackingStore
* backing_store_
;
2274 scoped_refptr
<IndexedDBBackingStore::BlobWriteCallback
> callback_
;
2275 scoped_ptr
<FileWriterDelegate
> delegate_
;
2278 DISALLOW_COPY_AND_ASSIGN(ChainedBlobWriterImpl
);
2281 class LocalWriteClosure
: public FileWriterDelegate::DelegateWriteCallback
,
2282 public base::RefCountedThreadSafe
<LocalWriteClosure
> {
2284 LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter
*
2285 chained_blob_writer
,
2286 base::SequencedTaskRunner
* task_runner
)
2287 : chained_blob_writer_(chained_blob_writer
),
2288 task_runner_(task_runner
),
2289 bytes_written_(0) {}
2291 void Run(base::File::Error rv
,
2293 FileWriterDelegate::WriteProgressStatus write_status
) {
2294 DCHECK_GE(bytes
, 0);
2295 bytes_written_
+= bytes
;
2296 if (write_status
== FileWriterDelegate::SUCCESS_IO_PENDING
)
2297 return; // We don't care about progress events.
2298 if (rv
== base::File::FILE_OK
) {
2299 DCHECK_EQ(write_status
, FileWriterDelegate::SUCCESS_COMPLETED
);
2301 DCHECK(write_status
== FileWriterDelegate::ERROR_WRITE_STARTED
||
2302 write_status
== FileWriterDelegate::ERROR_WRITE_NOT_STARTED
);
2304 task_runner_
->PostTask(
2306 base::Bind(&IndexedDBBackingStore::Transaction::ChainedBlobWriter::
2307 ReportWriteCompletion
,
2308 chained_blob_writer_
,
2309 write_status
== FileWriterDelegate::SUCCESS_COMPLETED
,
2313 void writeBlobToFileOnIOThread(const FilePath
& file_path
,
2314 const GURL
& blob_url
,
2315 net::URLRequestContext
* request_context
) {
2316 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
2317 scoped_ptr
<storage::FileStreamWriter
> writer(
2318 storage::FileStreamWriter::CreateForLocalFile(
2322 storage::FileStreamWriter::CREATE_NEW_FILE
));
2323 scoped_ptr
<FileWriterDelegate
> delegate(
2324 new FileWriterDelegate(writer
.Pass(),
2325 FileWriterDelegate::FLUSH_ON_COMPLETION
));
2327 DCHECK(blob_url
.is_valid());
2328 scoped_ptr
<net::URLRequest
> blob_request(request_context
->CreateRequest(
2329 blob_url
, net::DEFAULT_PRIORITY
, delegate
.get(), NULL
));
2331 delegate
->Start(blob_request
.Pass(),
2332 base::Bind(&LocalWriteClosure::Run
, this));
2333 chained_blob_writer_
->set_delegate(delegate
.Pass());
2337 virtual ~LocalWriteClosure() {
2338 // Make sure the last reference to a ChainedBlobWriter is released (and
2339 // deleted) on the IDB thread since it owns a transaction which has thread
2341 IndexedDBBackingStore::Transaction::ChainedBlobWriter
* raw_tmp
=
2342 chained_blob_writer_
.get();
2344 chained_blob_writer_
= NULL
;
2345 task_runner_
->ReleaseSoon(FROM_HERE
, raw_tmp
);
2347 friend class base::RefCountedThreadSafe
<LocalWriteClosure
>;
2349 scoped_refptr
<IndexedDBBackingStore::Transaction::ChainedBlobWriter
>
2350 chained_blob_writer_
;
2351 scoped_refptr
<base::SequencedTaskRunner
> task_runner_
;
2352 int64 bytes_written_
;
2354 DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure
);
2357 bool IndexedDBBackingStore::WriteBlobFile(
2359 const Transaction::WriteDescriptor
& descriptor
,
2360 Transaction::ChainedBlobWriter
* chained_blob_writer
) {
2362 if (!MakeIDBBlobDirectory(blob_path_
, database_id
, descriptor
.key()))
2365 FilePath path
= GetBlobFileName(database_id
, descriptor
.key());
2367 if (descriptor
.is_file()) {
2368 DCHECK(!descriptor
.file_path().empty());
2369 if (!base::CopyFile(descriptor
.file_path(), path
))
2372 base::File::Info info
;
2373 if (base::GetFileInfo(descriptor
.file_path(), &info
)) {
2374 if (descriptor
.size() != -1) {
2375 if (descriptor
.size() != info
.size
)
2377 // The round-trip can be lossy; round to nearest millisecond.
2378 int64 delta
= (descriptor
.last_modified() -
2379 info
.last_modified
).InMilliseconds();
2380 if (std::abs(delta
) > 1)
2383 if (!base::TouchFile(path
, info
.last_accessed
, info
.last_modified
)) {
2384 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2387 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2390 task_runner_
->PostTask(
2392 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion
,
2393 chained_blob_writer
,
2397 DCHECK(descriptor
.url().is_valid());
2398 scoped_refptr
<LocalWriteClosure
> write_closure(
2399 new LocalWriteClosure(chained_blob_writer
, task_runner_
.get()));
2400 content::BrowserThread::PostTask(
2401 content::BrowserThread::IO
,
2403 base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread
,
2404 write_closure
.get(),
2412 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id
,
2414 DCHECK(KeyPrefix::IsValidDatabaseId(database_id
));
2415 bool all_blobs
= blob_key
== DatabaseMetaDataKey::kAllBlobsKey
;
2416 DCHECK(all_blobs
|| DatabaseMetaDataKey::IsValidBlobKey(blob_key
));
2417 scoped_refptr
<LevelDBTransaction
> transaction
=
2418 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
2420 std::string live_blob_key
= LiveBlobJournalKey::Encode();
2421 BlobJournalType live_blob_journal
;
2422 if (!GetBlobJournal(live_blob_key
, transaction
.get(), &live_blob_journal
)
2425 DCHECK(live_blob_journal
.size());
2427 std::string primary_key
= BlobJournalKey::Encode();
2428 BlobJournalType primary_journal
;
2429 if (!GetBlobJournal(primary_key
, transaction
.get(), &primary_journal
).ok())
2432 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2433 // remove all entries with database_id from the live_blob journal and add only
2434 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2435 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2436 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2437 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2438 // matching (database_id, blob_key) tuple, we should move it to the primary
2440 BlobJournalType new_live_blob_journal
;
2441 for (BlobJournalType::iterator journal_iter
= live_blob_journal
.begin();
2442 journal_iter
!= live_blob_journal
.end();
2444 int64 current_database_id
= journal_iter
->first
;
2445 int64 current_blob_key
= journal_iter
->second
;
2446 bool current_all_blobs
=
2447 current_blob_key
== DatabaseMetaDataKey::kAllBlobsKey
;
2448 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id
) ||
2450 if (current_database_id
== database_id
&&
2451 (all_blobs
|| current_all_blobs
|| blob_key
== current_blob_key
)) {
2453 primary_journal
.push_back(
2454 std::make_pair(database_id
, current_blob_key
));
2455 if (current_all_blobs
)
2456 new_live_blob_journal
.push_back(*journal_iter
);
2457 new_live_blob_journal
.insert(new_live_blob_journal
.end(),
2459 live_blob_journal
.end()); // All the rest.
2463 new_live_blob_journal
.push_back(*journal_iter
);
2467 primary_journal
.push_back(
2468 std::make_pair(database_id
, DatabaseMetaDataKey::kAllBlobsKey
));
2470 UpdatePrimaryJournalWithBlobList(transaction
.get(), primary_journal
);
2471 UpdateLiveBlobJournalWithBlobList(transaction
.get(), new_live_blob_journal
);
2472 transaction
->Commit();
2473 // We could just do the deletions/cleaning here, but if there are a lot of
2474 // blobs about to be garbage collected, it'd be better to wait and do them all
2476 StartJournalCleaningTimer();
2479 // The this reference is a raw pointer that's declared Unretained inside the
2480 // timer code, so this won't confuse IndexedDBFactory's check for
2481 // HasLastBackingStoreReference. It's safe because if the backing store is
2482 // deleted, the timer will automatically be canceled on destruction.
2483 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2484 journal_cleaning_timer_
.Start(
2486 base::TimeDelta::FromSeconds(5),
2488 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn
);
2491 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
2492 FilePath
IndexedDBBackingStore::GetBlobFileName(int64 database_id
, int64 key
) {
2493 return GetBlobFileNameForKey(blob_path_
, database_id
, key
);
2496 static bool CheckIndexAndMetaDataKey(const LevelDBIterator
* it
,
2497 const std::string
& stop_key
,
2499 unsigned char meta_data_type
) {
2500 if (!it
->IsValid() || CompareKeys(it
->Key(), stop_key
) >= 0)
2503 StringPiece
slice(it
->Key());
2504 IndexMetaDataKey meta_data_key
;
2505 bool ok
= IndexMetaDataKey::Decode(&slice
, &meta_data_key
);
2507 if (meta_data_key
.IndexId() != index_id
)
2509 if (meta_data_key
.meta_data_type() != meta_data_type
)
2514 // TODO(jsbell): This should do some error handling rather than plowing ahead
2515 // when bad data is encountered.
2516 leveldb::Status
IndexedDBBackingStore::GetIndexes(
2518 int64 object_store_id
,
2519 IndexedDBObjectStoreMetadata::IndexMap
* indexes
) {
2520 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2521 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2522 return InvalidDBKeyStatus();
2523 const std::string start_key
=
2524 IndexMetaDataKey::Encode(database_id
, object_store_id
, 0, 0);
2525 const std::string stop_key
=
2526 IndexMetaDataKey::Encode(database_id
, object_store_id
+ 1, 0, 0);
2528 DCHECK(indexes
->empty());
2530 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
2531 leveldb::Status s
= it
->Seek(start_key
);
2532 while (s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0) {
2533 StringPiece
slice(it
->Key());
2534 IndexMetaDataKey meta_data_key
;
2535 bool ok
= IndexMetaDataKey::Decode(&slice
, &meta_data_key
);
2537 if (meta_data_key
.meta_data_type() != IndexMetaDataKey::NAME
) {
2538 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2539 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2547 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2549 int64 index_id
= meta_data_key
.IndexId();
2550 base::string16 index_name
;
2552 StringPiece
slice(it
->Value());
2553 if (!DecodeString(&slice
, &index_name
) || !slice
.empty())
2554 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2557 s
= it
->Next(); // unique flag
2560 if (!CheckIndexAndMetaDataKey(
2561 it
.get(), stop_key
, index_id
, IndexMetaDataKey::UNIQUE
)) {
2562 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2567 StringPiece
slice(it
->Value());
2568 if (!DecodeBool(&slice
, &index_unique
) || !slice
.empty())
2569 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2572 s
= it
->Next(); // key_path
2575 if (!CheckIndexAndMetaDataKey(
2576 it
.get(), stop_key
, index_id
, IndexMetaDataKey::KEY_PATH
)) {
2577 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2580 IndexedDBKeyPath key_path
;
2582 StringPiece
slice(it
->Value());
2583 if (!DecodeIDBKeyPath(&slice
, &key_path
) || !slice
.empty())
2584 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2587 s
= it
->Next(); // [optional] multi_entry flag
2590 bool index_multi_entry
= false;
2591 if (CheckIndexAndMetaDataKey(
2592 it
.get(), stop_key
, index_id
, IndexMetaDataKey::MULTI_ENTRY
)) {
2593 StringPiece
slice(it
->Value());
2594 if (!DecodeBool(&slice
, &index_multi_entry
) || !slice
.empty())
2595 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2602 (*indexes
)[index_id
] = IndexedDBIndexMetadata(
2603 index_name
, index_id
, key_path
, index_unique
, index_multi_entry
);
2607 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES
);
2612 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id
, int64 key
) {
2613 FilePath fileName
= GetBlobFileName(database_id
, key
);
2614 return base::DeleteFile(fileName
, false);
2617 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id
) {
2618 FilePath dirName
= GetBlobDirectoryName(blob_path_
, database_id
);
2619 return base::DeleteFile(dirName
, true);
2622 leveldb::Status
IndexedDBBackingStore::CleanUpBlobJournal(
2623 const std::string
& level_db_key
) {
2624 scoped_refptr
<LevelDBTransaction
> journal_transaction
=
2625 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_
.get());
2626 BlobJournalType journal
;
2628 GetBlobJournal(level_db_key
, journal_transaction
.get(), &journal
);
2631 if (!journal
.size())
2632 return leveldb::Status::OK();
2633 BlobJournalType::iterator journal_iter
;
2634 for (journal_iter
= journal
.begin(); journal_iter
!= journal
.end();
2636 int64 database_id
= journal_iter
->first
;
2637 int64 blob_key
= journal_iter
->second
;
2638 DCHECK(KeyPrefix::IsValidDatabaseId(database_id
));
2639 if (blob_key
== DatabaseMetaDataKey::kAllBlobsKey
) {
2640 if (!RemoveBlobDirectory(database_id
))
2641 return IOErrorStatus();
2643 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key
));
2644 if (!RemoveBlobFile(database_id
, blob_key
))
2645 return IOErrorStatus();
2648 ClearBlobJournal(journal_transaction
.get(), level_db_key
);
2649 return journal_transaction
->Commit();
2652 leveldb::Status
IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
2654 const std::string
& object_store_data_key
,
2655 IndexedDBValue
* value
) {
2656 BlobChangeRecord
* change_record
= NULL
;
2657 BlobChangeMap::const_iterator blob_iter
=
2658 blob_change_map_
.find(object_store_data_key
);
2659 if (blob_iter
!= blob_change_map_
.end()) {
2660 change_record
= blob_iter
->second
;
2662 blob_iter
= incognito_blob_map_
.find(object_store_data_key
);
2663 if (blob_iter
!= incognito_blob_map_
.end())
2664 change_record
= blob_iter
->second
;
2666 if (change_record
) {
2667 // Either we haven't written the blob to disk yet or we're in incognito
2668 // mode, so we have to send back the one they sent us. This change record
2669 // includes the original UUID.
2670 value
->blob_info
= change_record
->blob_info();
2671 return leveldb::Status::OK();
2674 BlobEntryKey blob_entry_key
;
2675 StringPiece
leveldb_key_piece(object_store_data_key
);
2676 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece
,
2679 return InternalInconsistencyStatus();
2681 scoped_ptr
<LevelDBIterator
> it
= transaction()->CreateIterator();
2682 std::string encoded_key
= blob_entry_key
.Encode();
2683 leveldb::Status s
= it
->Seek(encoded_key
);
2686 if (it
->IsValid() && CompareKeys(it
->Key(), encoded_key
) == 0) {
2687 if (!DecodeBlobData(it
->Value().as_string(), &value
->blob_info
)) {
2688 INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD
);
2689 return InternalInconsistencyStatus();
2691 std::vector
<IndexedDBBlobInfo
>::iterator iter
;
2692 for (iter
= value
->blob_info
.begin(); iter
!= value
->blob_info
.end();
2694 iter
->set_file_path(
2695 backing_store_
->GetBlobFileName(database_id
, iter
->key()));
2696 iter
->set_mark_used_callback(
2697 backing_store_
->active_blob_registry()->GetAddBlobRefCallback(
2698 database_id
, iter
->key()));
2699 iter
->set_release_callback(
2700 backing_store_
->active_blob_registry()->GetFinalReleaseCallback(
2701 database_id
, iter
->key()));
2702 if (iter
->is_file()) {
2703 base::File::Info info
;
2704 if (base::GetFileInfo(iter
->file_path(), &info
)) {
2705 // This should always work, but it isn't fatal if it doesn't; it just
2706 // means a potential slow synchronous call from the renderer later.
2707 iter
->set_last_modified(info
.last_modified
);
2708 iter
->set_size(info
.size
);
2713 return leveldb::Status::OK();
2716 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2717 CleanUpBlobJournal(BlobJournalKey::Encode());
2720 WARN_UNUSED_RESULT
static leveldb::Status
SetMaxIndexId(
2721 LevelDBTransaction
* transaction
,
2723 int64 object_store_id
,
2725 int64 max_index_id
= -1;
2726 const std::string max_index_id_key
= ObjectStoreMetaDataKey::Encode(
2727 database_id
, object_store_id
, ObjectStoreMetaDataKey::MAX_INDEX_ID
);
2730 GetInt(transaction
, max_index_id_key
, &max_index_id
, &found
);
2732 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID
);
2736 max_index_id
= kMinimumIndexId
;
2738 if (index_id
<= max_index_id
) {
2739 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID
);
2740 return InternalInconsistencyStatus();
2743 PutInt(transaction
, max_index_id_key
, index_id
);
2747 leveldb::Status
IndexedDBBackingStore::CreateIndex(
2748 IndexedDBBackingStore::Transaction
* transaction
,
2750 int64 object_store_id
,
2752 const base::string16
& name
,
2753 const IndexedDBKeyPath
& key_path
,
2755 bool is_multi_entry
) {
2756 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2757 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2758 return InvalidDBKeyStatus();
2759 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2760 leveldb::Status s
= SetMaxIndexId(
2761 leveldb_transaction
, database_id
, object_store_id
, index_id
);
2766 const std::string name_key
= IndexMetaDataKey::Encode(
2767 database_id
, object_store_id
, index_id
, IndexMetaDataKey::NAME
);
2768 const std::string unique_key
= IndexMetaDataKey::Encode(
2769 database_id
, object_store_id
, index_id
, IndexMetaDataKey::UNIQUE
);
2770 const std::string key_path_key
= IndexMetaDataKey::Encode(
2771 database_id
, object_store_id
, index_id
, IndexMetaDataKey::KEY_PATH
);
2772 const std::string multi_entry_key
= IndexMetaDataKey::Encode(
2773 database_id
, object_store_id
, index_id
, IndexMetaDataKey::MULTI_ENTRY
);
2775 PutString(leveldb_transaction
, name_key
, name
);
2776 PutBool(leveldb_transaction
, unique_key
, is_unique
);
2777 PutIDBKeyPath(leveldb_transaction
, key_path_key
, key_path
);
2778 PutBool(leveldb_transaction
, multi_entry_key
, is_multi_entry
);
2782 leveldb::Status
IndexedDBBackingStore::DeleteIndex(
2783 IndexedDBBackingStore::Transaction
* transaction
,
2785 int64 object_store_id
,
2787 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2788 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2789 return InvalidDBKeyStatus();
2790 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2792 const std::string index_meta_data_start
=
2793 IndexMetaDataKey::Encode(database_id
, object_store_id
, index_id
, 0);
2794 const std::string index_meta_data_end
=
2795 IndexMetaDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
2796 leveldb::Status s
= DeleteRangeBasic(
2797 leveldb_transaction
, index_meta_data_start
, index_meta_data_end
, true);
2800 const std::string index_data_start
=
2801 IndexDataKey::EncodeMinKey(database_id
, object_store_id
, index_id
);
2802 const std::string index_data_end
=
2803 IndexDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
2804 s
= DeleteRangeBasic(
2805 leveldb_transaction
, index_data_start
, index_data_end
, true);
2809 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX
);
2814 leveldb::Status
IndexedDBBackingStore::PutIndexDataForRecord(
2815 IndexedDBBackingStore::Transaction
* transaction
,
2817 int64 object_store_id
,
2819 const IndexedDBKey
& key
,
2820 const RecordIdentifier
& record_identifier
) {
2821 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2822 DCHECK(key
.IsValid());
2823 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2824 return InvalidDBKeyStatus();
2826 std::string encoded_key
;
2827 EncodeIDBKey(key
, &encoded_key
);
2829 const std::string index_data_key
=
2830 IndexDataKey::Encode(database_id
,
2834 record_identifier
.primary_key(),
2838 EncodeVarInt(record_identifier
.version(), &data
);
2839 data
.append(record_identifier
.primary_key());
2841 transaction
->transaction()->Put(index_data_key
, &data
);
2842 return leveldb::Status::OK();
2845 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction
* transaction
,
2846 const std::string
& target
,
2847 std::string
* found_key
,
2848 leveldb::Status
* s
) {
2849 scoped_ptr
<LevelDBIterator
> it
= transaction
->CreateIterator();
2850 *s
= it
->Seek(target
);
2854 if (!it
->IsValid()) {
2855 *s
= it
->SeekToLast();
2856 if (!s
->ok() || !it
->IsValid())
2860 while (CompareIndexKeys(it
->Key(), target
) > 0) {
2862 if (!s
->ok() || !it
->IsValid())
2867 *found_key
= it
->Key().as_string();
2869 // There can be several index keys that compare equal. We want the last one.
2871 } while (s
->ok() && it
->IsValid() && !CompareIndexKeys(it
->Key(), target
));
2876 static leveldb::Status
VersionExists(LevelDBTransaction
* transaction
,
2878 int64 object_store_id
,
2880 const std::string
& encoded_primary_key
,
2882 const std::string key
=
2883 ExistsEntryKey::Encode(database_id
, object_store_id
, encoded_primary_key
);
2886 leveldb::Status s
= transaction
->Get(key
, &data
, exists
);
2888 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS
);
2894 StringPiece
slice(data
);
2896 if (!DecodeInt(&slice
, &decoded
) || !slice
.empty())
2897 return InternalInconsistencyStatus();
2898 *exists
= (decoded
== version
);
2902 leveldb::Status
IndexedDBBackingStore::FindKeyInIndex(
2903 IndexedDBBackingStore::Transaction
* transaction
,
2905 int64 object_store_id
,
2907 const IndexedDBKey
& key
,
2908 std::string
* found_encoded_primary_key
,
2910 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
2911 DCHECK(KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
));
2913 DCHECK(found_encoded_primary_key
->empty());
2916 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2917 const std::string leveldb_key
=
2918 IndexDataKey::Encode(database_id
, object_store_id
, index_id
, key
);
2919 scoped_ptr
<LevelDBIterator
> it
= leveldb_transaction
->CreateIterator();
2920 leveldb::Status s
= it
->Seek(leveldb_key
);
2922 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX
);
2928 return leveldb::Status::OK();
2929 if (CompareIndexKeys(it
->Key(), leveldb_key
) > 0)
2930 return leveldb::Status::OK();
2932 StringPiece
slice(it
->Value());
2935 if (!DecodeVarInt(&slice
, &version
)) {
2936 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX
);
2937 return InternalInconsistencyStatus();
2939 *found_encoded_primary_key
= slice
.as_string();
2941 bool exists
= false;
2942 s
= VersionExists(leveldb_transaction
,
2946 *found_encoded_primary_key
,
2951 // Delete stale index data entry and continue.
2952 leveldb_transaction
->Remove(it
->Key());
2961 leveldb::Status
IndexedDBBackingStore::GetPrimaryKeyViaIndex(
2962 IndexedDBBackingStore::Transaction
* transaction
,
2964 int64 object_store_id
,
2966 const IndexedDBKey
& key
,
2967 scoped_ptr
<IndexedDBKey
>* primary_key
) {
2968 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
2969 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2970 return InvalidDBKeyStatus();
2973 std::string found_encoded_primary_key
;
2974 leveldb::Status s
= FindKeyInIndex(transaction
,
2979 &found_encoded_primary_key
,
2982 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX
);
2987 if (!found_encoded_primary_key
.size()) {
2988 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX
);
2989 return InvalidDBKeyStatus();
2992 StringPiece
slice(found_encoded_primary_key
);
2993 if (DecodeIDBKey(&slice
, primary_key
) && slice
.empty())
2996 return InvalidDBKeyStatus();
2999 leveldb::Status
IndexedDBBackingStore::KeyExistsInIndex(
3000 IndexedDBBackingStore::Transaction
* transaction
,
3002 int64 object_store_id
,
3004 const IndexedDBKey
& index_key
,
3005 scoped_ptr
<IndexedDBKey
>* found_primary_key
,
3007 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
3008 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
3009 return InvalidDBKeyStatus();
3012 std::string found_encoded_primary_key
;
3013 leveldb::Status s
= FindKeyInIndex(transaction
,
3018 &found_encoded_primary_key
,
3021 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX
);
3025 return leveldb::Status::OK();
3026 if (found_encoded_primary_key
.empty()) {
3027 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX
);
3028 return InvalidDBKeyStatus();
3031 StringPiece
slice(found_encoded_primary_key
);
3032 if (DecodeIDBKey(&slice
, found_primary_key
) && slice
.empty())
3035 return InvalidDBKeyStatus();
3038 IndexedDBBackingStore::Cursor::Cursor(
3039 const IndexedDBBackingStore::Cursor
* other
)
3040 : backing_store_(other
->backing_store_
),
3041 transaction_(other
->transaction_
),
3042 database_id_(other
->database_id_
),
3043 cursor_options_(other
->cursor_options_
),
3044 current_key_(new IndexedDBKey(*other
->current_key_
)) {
3045 if (other
->iterator_
) {
3046 iterator_
= transaction_
->transaction()->CreateIterator();
3048 if (other
->iterator_
->IsValid()) {
3049 leveldb::Status s
= iterator_
->Seek(other
->iterator_
->Key());
3050 // TODO(cmumford): Handle this error (crbug.com/363397)
3051 DCHECK(iterator_
->IsValid());
3056 IndexedDBBackingStore::Cursor::Cursor(
3057 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3058 IndexedDBBackingStore::Transaction
* transaction
,
3060 const CursorOptions
& cursor_options
)
3061 : backing_store_(backing_store
.get()),
3062 transaction_(transaction
),
3063 database_id_(database_id
),
3064 cursor_options_(cursor_options
) {
3066 IndexedDBBackingStore::Cursor::~Cursor() {}
3068 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status
* s
) {
3069 iterator_
= transaction_
->transaction()->CreateIterator();
3070 if (cursor_options_
.forward
)
3071 *s
= iterator_
->Seek(cursor_options_
.low_key
);
3073 *s
= iterator_
->Seek(cursor_options_
.high_key
);
3077 return Continue(0, READY
, s
);
3080 bool IndexedDBBackingStore::Cursor::Advance(uint32 count
, leveldb::Status
* s
) {
3081 *s
= leveldb::Status::OK();
3089 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey
* key
,
3090 const IndexedDBKey
* primary_key
,
3091 IteratorState next_state
,
3092 leveldb::Status
* s
) {
3093 DCHECK(!key
|| key
->IsValid());
3094 DCHECK(!primary_key
|| primary_key
->IsValid());
3095 *s
= leveldb::Status::OK();
3097 // TODO(alecflett): avoid a copy here?
3098 IndexedDBKey previous_key
= current_key_
? *current_key_
: IndexedDBKey();
3100 // When iterating with PrevNoDuplicate, spec requires that the
3101 // value we yield for each key is the first duplicate in forwards
3103 IndexedDBKey last_duplicate_key
;
3105 bool forward
= cursor_options_
.forward
;
3106 bool first_iteration_forward
= forward
;
3107 bool flipped
= false;
3110 if (next_state
== SEEK
) {
3111 // TODO(jsbell): Optimize seeking for reverse cursors as well.
3112 if (first_iteration_forward
&& key
) {
3113 first_iteration_forward
= false;
3114 std::string leveldb_key
;
3116 leveldb_key
= EncodeKey(*key
, *primary_key
);
3118 leveldb_key
= EncodeKey(*key
);
3120 *s
= iterator_
->Seek(leveldb_key
);
3121 } else if (forward
) {
3122 *s
= iterator_
->Next();
3124 *s
= iterator_
->Prev();
3129 next_state
= SEEK
; // for subsequent iterations
3132 if (!iterator_
->IsValid()) {
3133 if (!forward
&& last_duplicate_key
.IsValid()) {
3134 // We need to walk forward because we hit the end of
3144 if (IsPastBounds()) {
3145 if (!forward
&& last_duplicate_key
.IsValid()) {
3146 // We need to walk forward because now we're beyond the
3147 // bounds defined by the cursor.
3156 if (!HaveEnteredRange())
3159 // The row may not load because there's a stale entry in the
3160 // index. This is not fatal.
3161 if (!LoadCurrentRow())
3166 if (primary_key
&& current_key_
->Equals(*key
) &&
3167 this->primary_key().IsLessThan(*primary_key
))
3169 if (!flipped
&& current_key_
->IsLessThan(*key
))
3172 if (primary_key
&& key
->Equals(*current_key_
) &&
3173 primary_key
->IsLessThan(this->primary_key()))
3175 if (key
->IsLessThan(*current_key_
))
3180 if (cursor_options_
.unique
) {
3181 if (previous_key
.IsValid() && current_key_
->Equals(previous_key
)) {
3182 // We should never be able to walk forward all the way
3183 // to the previous key.
3184 DCHECK(!last_duplicate_key
.IsValid());
3189 if (!last_duplicate_key
.IsValid()) {
3190 last_duplicate_key
= *current_key_
;
3194 // We need to walk forward because we hit the boundary
3195 // between key ranges.
3196 if (!last_duplicate_key
.Equals(*current_key_
)) {
3208 DCHECK(!last_duplicate_key
.IsValid() ||
3209 (forward
&& last_duplicate_key
.Equals(*current_key_
)));
3213 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3214 if (cursor_options_
.forward
) {
3215 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.low_key
);
3216 if (cursor_options_
.low_open
) {
3219 return compare
>= 0;
3221 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.high_key
);
3222 if (cursor_options_
.high_open
) {
3225 return compare
<= 0;
3228 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
3229 if (cursor_options_
.forward
) {
3230 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.high_key
);
3231 if (cursor_options_
.high_open
) {
3232 return compare
>= 0;
3236 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.low_key
);
3237 if (cursor_options_
.low_open
) {
3238 return compare
<= 0;
3243 const IndexedDBKey
& IndexedDBBackingStore::Cursor::primary_key() const {
3244 return *current_key_
;
3247 const IndexedDBBackingStore::RecordIdentifier
&
3248 IndexedDBBackingStore::Cursor::record_identifier() const {
3249 return record_identifier_
;
3252 class ObjectStoreKeyCursorImpl
: public IndexedDBBackingStore::Cursor
{
3254 ObjectStoreKeyCursorImpl(
3255 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3256 IndexedDBBackingStore::Transaction
* transaction
,
3258 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3259 : IndexedDBBackingStore::Cursor(backing_store
,
3264 virtual Cursor
* Clone() OVERRIDE
{
3265 return new ObjectStoreKeyCursorImpl(this);
3268 // IndexedDBBackingStore::Cursor
3269 virtual IndexedDBValue
* value() OVERRIDE
{
3273 virtual bool LoadCurrentRow() OVERRIDE
;
3276 virtual std::string
EncodeKey(const IndexedDBKey
& key
) OVERRIDE
{
3277 return ObjectStoreDataKey::Encode(
3278 cursor_options_
.database_id
, cursor_options_
.object_store_id
, key
);
3280 virtual std::string
EncodeKey(const IndexedDBKey
& key
,
3281 const IndexedDBKey
& primary_key
) OVERRIDE
{
3283 return std::string();
3287 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl
* other
)
3288 : IndexedDBBackingStore::Cursor(other
) {}
3290 DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl
);
3293 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
3294 StringPiece
slice(iterator_
->Key());
3295 ObjectStoreDataKey object_store_data_key
;
3296 if (!ObjectStoreDataKey::Decode(&slice
, &object_store_data_key
)) {
3297 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3301 current_key_
= object_store_data_key
.user_key();
3304 slice
= StringPiece(iterator_
->Value());
3305 if (!DecodeVarInt(&slice
, &version
)) {
3306 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3310 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3311 std::string encoded_key
;
3312 EncodeIDBKey(*current_key_
, &encoded_key
);
3313 record_identifier_
.Reset(encoded_key
, version
);
3318 class ObjectStoreCursorImpl
: public IndexedDBBackingStore::Cursor
{
3320 ObjectStoreCursorImpl(
3321 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3322 IndexedDBBackingStore::Transaction
* transaction
,
3324 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3325 : IndexedDBBackingStore::Cursor(backing_store
,
3330 virtual Cursor
* Clone() OVERRIDE
{ return new ObjectStoreCursorImpl(this); }
3332 // IndexedDBBackingStore::Cursor
3333 virtual IndexedDBValue
* value() OVERRIDE
{ return ¤t_value_
; }
3334 virtual bool LoadCurrentRow() OVERRIDE
;
3337 virtual std::string
EncodeKey(const IndexedDBKey
& key
) OVERRIDE
{
3338 return ObjectStoreDataKey::Encode(
3339 cursor_options_
.database_id
, cursor_options_
.object_store_id
, key
);
3341 virtual std::string
EncodeKey(const IndexedDBKey
& key
,
3342 const IndexedDBKey
& primary_key
) OVERRIDE
{
3344 return std::string();
3348 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl
* other
)
3349 : IndexedDBBackingStore::Cursor(other
),
3350 current_value_(other
->current_value_
) {}
3352 IndexedDBValue current_value_
;
3354 DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl
);
3357 bool ObjectStoreCursorImpl::LoadCurrentRow() {
3358 StringPiece
key_slice(iterator_
->Key());
3359 ObjectStoreDataKey object_store_data_key
;
3360 if (!ObjectStoreDataKey::Decode(&key_slice
, &object_store_data_key
)) {
3361 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3365 current_key_
= object_store_data_key
.user_key();
3368 StringPiece value_slice
= StringPiece(iterator_
->Value());
3369 if (!DecodeVarInt(&value_slice
, &version
)) {
3370 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3374 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3375 std::string encoded_key
;
3376 EncodeIDBKey(*current_key_
, &encoded_key
);
3377 record_identifier_
.Reset(encoded_key
, version
);
3379 if (!transaction_
->GetBlobInfoForRecord(database_id_
,
3380 iterator_
->Key().as_string(),
3381 ¤t_value_
).ok()) {
3384 current_value_
.bits
= value_slice
.as_string();
3388 class IndexKeyCursorImpl
: public IndexedDBBackingStore::Cursor
{
3391 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3392 IndexedDBBackingStore::Transaction
* transaction
,
3394 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3395 : IndexedDBBackingStore::Cursor(backing_store
,
3400 virtual Cursor
* Clone() OVERRIDE
{ return new IndexKeyCursorImpl(this); }
3402 // IndexedDBBackingStore::Cursor
3403 virtual IndexedDBValue
* value() OVERRIDE
{
3407 virtual const IndexedDBKey
& primary_key() const OVERRIDE
{
3408 return *primary_key_
;
3410 virtual const IndexedDBBackingStore::RecordIdentifier
& record_identifier()
3413 return record_identifier_
;
3415 virtual bool LoadCurrentRow() OVERRIDE
;
3418 virtual std::string
EncodeKey(const IndexedDBKey
& key
) OVERRIDE
{
3419 return IndexDataKey::Encode(cursor_options_
.database_id
,
3420 cursor_options_
.object_store_id
,
3421 cursor_options_
.index_id
,
3424 virtual std::string
EncodeKey(const IndexedDBKey
& key
,
3425 const IndexedDBKey
& primary_key
) OVERRIDE
{
3426 return IndexDataKey::Encode(cursor_options_
.database_id
,
3427 cursor_options_
.object_store_id
,
3428 cursor_options_
.index_id
,
3434 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl
* other
)
3435 : IndexedDBBackingStore::Cursor(other
),
3436 primary_key_(new IndexedDBKey(*other
->primary_key_
)) {}
3438 scoped_ptr
<IndexedDBKey
> primary_key_
;
3440 DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl
);
3443 bool IndexKeyCursorImpl::LoadCurrentRow() {
3444 StringPiece
slice(iterator_
->Key());
3445 IndexDataKey index_data_key
;
3446 if (!IndexDataKey::Decode(&slice
, &index_data_key
)) {
3447 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3451 current_key_
= index_data_key
.user_key();
3452 DCHECK(current_key_
);
3454 slice
= StringPiece(iterator_
->Value());
3455 int64 index_data_version
;
3456 if (!DecodeVarInt(&slice
, &index_data_version
)) {
3457 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3461 if (!DecodeIDBKey(&slice
, &primary_key_
) || !slice
.empty()) {
3462 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3466 std::string primary_leveldb_key
=
3467 ObjectStoreDataKey::Encode(index_data_key
.DatabaseId(),
3468 index_data_key
.ObjectStoreId(),
3474 transaction_
->transaction()->Get(primary_leveldb_key
, &result
, &found
);
3476 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3480 transaction_
->transaction()->Remove(iterator_
->Key());
3483 if (!result
.size()) {
3484 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3488 int64 object_store_data_version
;
3489 slice
= StringPiece(result
);
3490 if (!DecodeVarInt(&slice
, &object_store_data_version
)) {
3491 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3495 if (object_store_data_version
!= index_data_version
) {
3496 transaction_
->transaction()->Remove(iterator_
->Key());
3503 class IndexCursorImpl
: public IndexedDBBackingStore::Cursor
{
3506 scoped_refptr
<IndexedDBBackingStore
> backing_store
,
3507 IndexedDBBackingStore::Transaction
* transaction
,
3509 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3510 : IndexedDBBackingStore::Cursor(backing_store
,
3515 virtual Cursor
* Clone() OVERRIDE
{ return new IndexCursorImpl(this); }
3517 // IndexedDBBackingStore::Cursor
3518 virtual IndexedDBValue
* value() OVERRIDE
{ return ¤t_value_
; }
3519 virtual const IndexedDBKey
& primary_key() const OVERRIDE
{
3520 return *primary_key_
;
3522 virtual const IndexedDBBackingStore::RecordIdentifier
& record_identifier()
3525 return record_identifier_
;
3527 virtual bool LoadCurrentRow() OVERRIDE
;
3530 virtual std::string
EncodeKey(const IndexedDBKey
& key
) OVERRIDE
{
3531 return IndexDataKey::Encode(cursor_options_
.database_id
,
3532 cursor_options_
.object_store_id
,
3533 cursor_options_
.index_id
,
3536 virtual std::string
EncodeKey(const IndexedDBKey
& key
,
3537 const IndexedDBKey
& primary_key
) OVERRIDE
{
3538 return IndexDataKey::Encode(cursor_options_
.database_id
,
3539 cursor_options_
.object_store_id
,
3540 cursor_options_
.index_id
,
3546 explicit IndexCursorImpl(const IndexCursorImpl
* other
)
3547 : IndexedDBBackingStore::Cursor(other
),
3548 primary_key_(new IndexedDBKey(*other
->primary_key_
)),
3549 current_value_(other
->current_value_
),
3550 primary_leveldb_key_(other
->primary_leveldb_key_
) {}
3552 scoped_ptr
<IndexedDBKey
> primary_key_
;
3553 IndexedDBValue current_value_
;
3554 std::string primary_leveldb_key_
;
3556 DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl
);
3559 bool IndexCursorImpl::LoadCurrentRow() {
3560 StringPiece
slice(iterator_
->Key());
3561 IndexDataKey index_data_key
;
3562 if (!IndexDataKey::Decode(&slice
, &index_data_key
)) {
3563 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3567 current_key_
= index_data_key
.user_key();
3568 DCHECK(current_key_
);
3570 slice
= StringPiece(iterator_
->Value());
3571 int64 index_data_version
;
3572 if (!DecodeVarInt(&slice
, &index_data_version
)) {
3573 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3576 if (!DecodeIDBKey(&slice
, &primary_key_
)) {
3577 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3581 DCHECK_EQ(index_data_key
.DatabaseId(), database_id_
);
3582 primary_leveldb_key_
=
3583 ObjectStoreDataKey::Encode(index_data_key
.DatabaseId(),
3584 index_data_key
.ObjectStoreId(),
3590 transaction_
->transaction()->Get(primary_leveldb_key_
, &result
, &found
);
3592 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3596 transaction_
->transaction()->Remove(iterator_
->Key());
3599 if (!result
.size()) {
3600 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3604 int64 object_store_data_version
;
3605 slice
= StringPiece(result
);
3606 if (!DecodeVarInt(&slice
, &object_store_data_version
)) {
3607 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3611 if (object_store_data_version
!= index_data_version
) {
3612 transaction_
->transaction()->Remove(iterator_
->Key());
3616 current_value_
.bits
= slice
.as_string();
3617 return transaction_
->GetBlobInfoForRecord(database_id_
,
3618 primary_leveldb_key_
,
3619 ¤t_value_
).ok();
3622 bool ObjectStoreCursorOptions(
3623 LevelDBTransaction
* transaction
,
3625 int64 object_store_id
,
3626 const IndexedDBKeyRange
& range
,
3627 blink::WebIDBCursorDirection direction
,
3628 IndexedDBBackingStore::Cursor::CursorOptions
* cursor_options
) {
3629 cursor_options
->database_id
= database_id
;
3630 cursor_options
->object_store_id
= object_store_id
;
3632 bool lower_bound
= range
.lower().IsValid();
3633 bool upper_bound
= range
.upper().IsValid();
3634 cursor_options
->forward
=
3635 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3636 direction
== blink::WebIDBCursorDirectionNext
);
3637 cursor_options
->unique
=
3638 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3639 direction
== blink::WebIDBCursorDirectionPrevNoDuplicate
);
3642 cursor_options
->low_key
=
3643 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MinIDBKey());
3644 cursor_options
->low_open
= true; // Not included.
3646 cursor_options
->low_key
=
3647 ObjectStoreDataKey::Encode(database_id
, object_store_id
, range
.lower());
3648 cursor_options
->low_open
= range
.lowerOpen();
3654 cursor_options
->high_key
=
3655 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MaxIDBKey());
3657 if (cursor_options
->forward
) {
3658 cursor_options
->high_open
= true; // Not included.
3660 // We need a key that exists.
3661 // TODO(cmumford): Handle this error (crbug.com/363397)
3662 if (!FindGreatestKeyLessThanOrEqual(transaction
,
3663 cursor_options
->high_key
,
3664 &cursor_options
->high_key
,
3667 cursor_options
->high_open
= false;
3670 cursor_options
->high_key
=
3671 ObjectStoreDataKey::Encode(database_id
, object_store_id
, range
.upper());
3672 cursor_options
->high_open
= range
.upperOpen();
3674 if (!cursor_options
->forward
) {
3675 // For reverse cursors, we need a key that exists.
3676 std::string found_high_key
;
3677 // TODO(cmumford): Handle this error (crbug.com/363397)
3678 if (!FindGreatestKeyLessThanOrEqual(
3679 transaction
, cursor_options
->high_key
, &found_high_key
, &s
))
3682 // If the target key should not be included, but we end up with a smaller
3683 // key, we should include that.
3684 if (cursor_options
->high_open
&&
3685 CompareIndexKeys(found_high_key
, cursor_options
->high_key
) < 0)
3686 cursor_options
->high_open
= false;
3688 cursor_options
->high_key
= found_high_key
;
3695 bool IndexCursorOptions(
3696 LevelDBTransaction
* transaction
,
3698 int64 object_store_id
,
3700 const IndexedDBKeyRange
& range
,
3701 blink::WebIDBCursorDirection direction
,
3702 IndexedDBBackingStore::Cursor::CursorOptions
* cursor_options
) {
3703 DCHECK(transaction
);
3704 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
3707 cursor_options
->database_id
= database_id
;
3708 cursor_options
->object_store_id
= object_store_id
;
3709 cursor_options
->index_id
= index_id
;
3711 bool lower_bound
= range
.lower().IsValid();
3712 bool upper_bound
= range
.upper().IsValid();
3713 cursor_options
->forward
=
3714 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3715 direction
== blink::WebIDBCursorDirectionNext
);
3716 cursor_options
->unique
=
3717 (direction
== blink::WebIDBCursorDirectionNextNoDuplicate
||
3718 direction
== blink::WebIDBCursorDirectionPrevNoDuplicate
);
3721 cursor_options
->low_key
=
3722 IndexDataKey::EncodeMinKey(database_id
, object_store_id
, index_id
);
3723 cursor_options
->low_open
= false; // Included.
3725 cursor_options
->low_key
= IndexDataKey::Encode(
3726 database_id
, object_store_id
, index_id
, range
.lower());
3727 cursor_options
->low_open
= range
.lowerOpen();
3733 cursor_options
->high_key
=
3734 IndexDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
3735 cursor_options
->high_open
= false; // Included.
3737 if (!cursor_options
->forward
) { // We need a key that exists.
3738 if (!FindGreatestKeyLessThanOrEqual(transaction
,
3739 cursor_options
->high_key
,
3740 &cursor_options
->high_key
,
3743 cursor_options
->high_open
= false;
3746 cursor_options
->high_key
= IndexDataKey::Encode(
3747 database_id
, object_store_id
, index_id
, range
.upper());
3748 cursor_options
->high_open
= range
.upperOpen();
3750 std::string found_high_key
;
3751 // Seek to the *last* key in the set of non-unique keys
3752 // TODO(cmumford): Handle this error (crbug.com/363397)
3753 if (!FindGreatestKeyLessThanOrEqual(
3754 transaction
, cursor_options
->high_key
, &found_high_key
, &s
))
3757 // If the target key should not be included, but we end up with a smaller
3758 // key, we should include that.
3759 if (cursor_options
->high_open
&&
3760 CompareIndexKeys(found_high_key
, cursor_options
->high_key
) < 0)
3761 cursor_options
->high_open
= false;
3763 cursor_options
->high_key
= found_high_key
;
3769 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3770 IndexedDBBackingStore::OpenObjectStoreCursor(
3771 IndexedDBBackingStore::Transaction
* transaction
,
3773 int64 object_store_id
,
3774 const IndexedDBKeyRange
& range
,
3775 blink::WebIDBCursorDirection direction
,
3776 leveldb::Status
* s
) {
3777 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3778 *s
= leveldb::Status::OK();
3779 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3780 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3781 if (!ObjectStoreCursorOptions(leveldb_transaction
,
3787 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3788 scoped_ptr
<ObjectStoreCursorImpl
> cursor(new ObjectStoreCursorImpl(
3789 this, transaction
, database_id
, cursor_options
));
3790 if (!cursor
->FirstSeek(s
))
3791 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3793 return cursor
.PassAs
<IndexedDBBackingStore::Cursor
>();
3796 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3797 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3798 IndexedDBBackingStore::Transaction
* transaction
,
3800 int64 object_store_id
,
3801 const IndexedDBKeyRange
& range
,
3802 blink::WebIDBCursorDirection direction
,
3803 leveldb::Status
* s
) {
3804 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3805 *s
= leveldb::Status::OK();
3806 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3807 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3808 if (!ObjectStoreCursorOptions(leveldb_transaction
,
3814 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3815 scoped_ptr
<ObjectStoreKeyCursorImpl
> cursor(new ObjectStoreKeyCursorImpl(
3816 this, transaction
, database_id
, cursor_options
));
3817 if (!cursor
->FirstSeek(s
))
3818 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3820 return cursor
.PassAs
<IndexedDBBackingStore::Cursor
>();
3823 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3824 IndexedDBBackingStore::OpenIndexKeyCursor(
3825 IndexedDBBackingStore::Transaction
* transaction
,
3827 int64 object_store_id
,
3829 const IndexedDBKeyRange
& range
,
3830 blink::WebIDBCursorDirection direction
,
3831 leveldb::Status
* s
) {
3832 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3833 *s
= leveldb::Status::OK();
3834 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3835 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3836 if (!IndexCursorOptions(leveldb_transaction
,
3843 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3844 scoped_ptr
<IndexKeyCursorImpl
> cursor(
3845 new IndexKeyCursorImpl(this, transaction
, database_id
, cursor_options
));
3846 if (!cursor
->FirstSeek(s
))
3847 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3849 return cursor
.PassAs
<IndexedDBBackingStore::Cursor
>();
3852 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3853 IndexedDBBackingStore::OpenIndexCursor(
3854 IndexedDBBackingStore::Transaction
* transaction
,
3856 int64 object_store_id
,
3858 const IndexedDBKeyRange
& range
,
3859 blink::WebIDBCursorDirection direction
,
3860 leveldb::Status
* s
) {
3861 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
3862 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3863 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3864 if (!IndexCursorOptions(leveldb_transaction
,
3871 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3872 scoped_ptr
<IndexCursorImpl
> cursor(
3873 new IndexCursorImpl(this, transaction
, database_id
, cursor_options
));
3874 if (!cursor
->FirstSeek(s
))
3875 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3877 return cursor
.PassAs
<IndexedDBBackingStore::Cursor
>();
3880 IndexedDBBackingStore::Transaction::Transaction(
3881 IndexedDBBackingStore
* backing_store
)
3882 : backing_store_(backing_store
), database_id_(-1) {
3885 IndexedDBBackingStore::Transaction::~Transaction() {
3886 STLDeleteContainerPairSecondPointers(
3887 blob_change_map_
.begin(), blob_change_map_
.end());
3888 STLDeleteContainerPairSecondPointers(incognito_blob_map_
.begin(),
3889 incognito_blob_map_
.end());
3892 void IndexedDBBackingStore::Transaction::Begin() {
3893 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
3894 DCHECK(!transaction_
.get());
3895 transaction_
= IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3896 backing_store_
->db_
.get());
3898 // If incognito, this snapshots blobs just as the above transaction_
3899 // constructor snapshots the leveldb.
3900 BlobChangeMap::const_iterator iter
;
3901 for (iter
= backing_store_
->incognito_blob_map_
.begin();
3902 iter
!= backing_store_
->incognito_blob_map_
.end();
3904 incognito_blob_map_
[iter
->first
] = iter
->second
->Clone().release();
3907 static GURL
getURLFromUUID(const string
& uuid
) {
3908 return GURL("blob:uuid/" + uuid
);
3911 leveldb::Status
IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
3912 BlobEntryKeyValuePairVec
* new_blob_entries
,
3913 WriteDescriptorVec
* new_files_to_write
) {
3914 if (backing_store_
->is_incognito())
3915 return leveldb::Status::OK();
3917 BlobChangeMap::iterator iter
= blob_change_map_
.begin();
3918 new_blob_entries
->clear();
3919 new_files_to_write
->clear();
3920 if (iter
!= blob_change_map_
.end()) {
3921 // Create LevelDBTransaction for the name generator seed and add-journal.
3922 scoped_refptr
<LevelDBTransaction
> pre_transaction
=
3923 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3924 backing_store_
->db_
.get());
3925 BlobJournalType journal
;
3926 for (; iter
!= blob_change_map_
.end(); ++iter
) {
3927 std::vector
<IndexedDBBlobInfo
>::iterator info_iter
;
3928 std::vector
<IndexedDBBlobInfo
*> new_blob_keys
;
3929 for (info_iter
= iter
->second
->mutable_blob_info().begin();
3930 info_iter
!= iter
->second
->mutable_blob_info().end();
3932 int64 next_blob_key
= -1;
3933 bool result
= GetBlobKeyGeneratorCurrentNumber(
3934 pre_transaction
.get(), database_id_
, &next_blob_key
);
3935 if (!result
|| next_blob_key
< 0)
3936 return InternalInconsistencyStatus();
3937 BlobJournalEntryType journal_entry
=
3938 std::make_pair(database_id_
, next_blob_key
);
3939 journal
.push_back(journal_entry
);
3940 if (info_iter
->is_file()) {
3941 new_files_to_write
->push_back(
3942 WriteDescriptor(info_iter
->file_path(),
3945 info_iter
->last_modified()));
3947 new_files_to_write
->push_back(
3948 WriteDescriptor(getURLFromUUID(info_iter
->uuid()),
3950 info_iter
->size()));
3952 info_iter
->set_key(next_blob_key
);
3953 new_blob_keys
.push_back(&*info_iter
);
3954 result
= UpdateBlobKeyGeneratorCurrentNumber(
3955 pre_transaction
.get(), database_id_
, next_blob_key
+ 1);
3957 return InternalInconsistencyStatus();
3959 BlobEntryKey blob_entry_key
;
3960 StringPiece
key_piece(iter
->second
->key());
3961 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece
, &blob_entry_key
)) {
3963 return InternalInconsistencyStatus();
3965 new_blob_entries
->push_back(
3966 std::make_pair(blob_entry_key
, EncodeBlobData(new_blob_keys
)));
3968 UpdatePrimaryJournalWithBlobList(pre_transaction
.get(), journal
);
3969 leveldb::Status s
= pre_transaction
->Commit();
3971 return InternalInconsistencyStatus();
3973 return leveldb::Status::OK();
3976 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
3977 if (backing_store_
->is_incognito())
3980 BlobChangeMap::const_iterator iter
= blob_change_map_
.begin();
3981 // Look up all old files to remove as part of the transaction, store their
3982 // names in blobs_to_remove_, and remove their old blob data entries.
3983 if (iter
!= blob_change_map_
.end()) {
3984 scoped_ptr
<LevelDBIterator
> db_iter
= transaction_
->CreateIterator();
3985 for (; iter
!= blob_change_map_
.end(); ++iter
) {
3986 BlobEntryKey blob_entry_key
;
3987 StringPiece
key_piece(iter
->second
->key());
3988 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece
, &blob_entry_key
)) {
3990 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
3991 transaction_
= NULL
;
3994 if (database_id_
< 0)
3995 database_id_
= blob_entry_key
.database_id();
3997 DCHECK_EQ(database_id_
, blob_entry_key
.database_id());
3998 std::string blob_entry_key_bytes
= blob_entry_key
.Encode();
3999 db_iter
->Seek(blob_entry_key_bytes
);
4000 if (db_iter
->IsValid() &&
4001 !CompareKeys(db_iter
->Key(), blob_entry_key_bytes
)) {
4002 std::vector
<IndexedDBBlobInfo
> blob_info
;
4003 if (!DecodeBlobData(db_iter
->Value().as_string(), &blob_info
)) {
4004 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4005 transaction_
= NULL
;
4008 std::vector
<IndexedDBBlobInfo
>::iterator blob_info_iter
;
4009 for (blob_info_iter
= blob_info
.begin();
4010 blob_info_iter
!= blob_info
.end();
4012 blobs_to_remove_
.push_back(
4013 std::make_pair(database_id_
, blob_info_iter
->key()));
4014 transaction_
->Remove(blob_entry_key_bytes
);
4021 leveldb::Status
IndexedDBBackingStore::Transaction::SortBlobsToRemove() {
4022 IndexedDBActiveBlobRegistry
* registry
=
4023 backing_store_
->active_blob_registry();
4024 BlobJournalType::iterator iter
;
4025 BlobJournalType primary_journal
, live_blob_journal
;
4026 for (iter
= blobs_to_remove_
.begin(); iter
!= blobs_to_remove_
.end();
4028 if (registry
->MarkDeletedCheckIfUsed(iter
->first
, iter
->second
))
4029 live_blob_journal
.push_back(*iter
);
4031 primary_journal
.push_back(*iter
);
4033 UpdatePrimaryJournalWithBlobList(transaction_
.get(), primary_journal
);
4035 MergeBlobsIntoLiveBlobJournal(transaction_
.get(), live_blob_journal
);
4038 // To signal how many blobs need attention right now.
4039 blobs_to_remove_
.swap(primary_journal
);
4040 return leveldb::Status::OK();
4043 leveldb::Status
IndexedDBBackingStore::Transaction::CommitPhaseOne(
4044 scoped_refptr
<BlobWriteCallback
> callback
) {
4045 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
4046 DCHECK(transaction_
.get());
4047 DCHECK(backing_store_
->task_runner()->RunsTasksOnCurrentThread());
4051 s
= backing_store_
->CleanUpBlobJournal(BlobJournalKey::Encode());
4053 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD
);
4054 transaction_
= NULL
;
4058 BlobEntryKeyValuePairVec new_blob_entries
;
4059 WriteDescriptorVec new_files_to_write
;
4060 s
= HandleBlobPreTransaction(&new_blob_entries
, &new_files_to_write
);
4062 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4063 transaction_
= NULL
;
4067 DCHECK(!new_files_to_write
.size() ||
4068 KeyPrefix::IsValidDatabaseId(database_id_
));
4069 if (!CollectBlobFilesToRemove()) {
4070 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4071 transaction_
= NULL
;
4072 return InternalInconsistencyStatus();
4075 if (new_files_to_write
.size()) {
4076 // This kicks off the writes of the new blobs, if any.
4077 // This call will zero out new_blob_entries and new_files_to_write.
4078 WriteNewBlobs(&new_blob_entries
, &new_files_to_write
, callback
);
4079 // Remove the add journal, if any; once the blobs are written, and we
4080 // commit, this will do the cleanup.
4081 ClearBlobJournal(transaction_
.get(), BlobJournalKey::Encode());
4083 callback
->Run(true);
4086 return leveldb::Status::OK();
4089 leveldb::Status
IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
4090 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
4092 if (blobs_to_remove_
.size()) {
4093 s
= SortBlobsToRemove();
4095 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
4096 transaction_
= NULL
;
4101 s
= transaction_
->Commit();
4102 transaction_
= NULL
;
4104 if (s
.ok() && backing_store_
->is_incognito() && !blob_change_map_
.empty()) {
4105 BlobChangeMap
& target_map
= backing_store_
->incognito_blob_map_
;
4106 BlobChangeMap::iterator iter
;
4107 for (iter
= blob_change_map_
.begin(); iter
!= blob_change_map_
.end();
4109 BlobChangeMap::iterator target_record
= target_map
.find(iter
->first
);
4110 if (target_record
!= target_map
.end()) {
4111 delete target_record
->second
;
4112 target_map
.erase(target_record
);
4115 target_map
[iter
->first
] = iter
->second
;
4116 iter
->second
= NULL
;
4121 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD
);
4122 else if (blobs_to_remove_
.size())
4123 s
= backing_store_
->CleanUpBlobJournal(BlobJournalKey::Encode());
4129 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
4130 : public IndexedDBBackingStore::BlobWriteCallback
{
4132 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction
* transaction
,
4133 scoped_refptr
<BlobWriteCallback
> callback
)
4134 : transaction_(transaction
), callback_(callback
) {}
4135 virtual void Run(bool succeeded
) OVERRIDE
{
4136 callback_
->Run(succeeded
);
4137 if (succeeded
) // Else it's already been deleted during rollback.
4138 transaction_
->chained_blob_writer_
= NULL
;
4142 virtual ~BlobWriteCallbackWrapper() {}
4143 friend class base::RefCounted
<IndexedDBBackingStore::BlobWriteCallback
>;
4145 IndexedDBBackingStore::Transaction
* transaction_
;
4146 scoped_refptr
<BlobWriteCallback
> callback_
;
4148 DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper
);
4151 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
4152 BlobEntryKeyValuePairVec
* new_blob_entries
,
4153 WriteDescriptorVec
* new_files_to_write
,
4154 scoped_refptr
<BlobWriteCallback
> callback
) {
4155 DCHECK_GT(new_files_to_write
->size(), 0UL);
4156 DCHECK_GT(database_id_
, 0);
4157 BlobEntryKeyValuePairVec::iterator blob_entry_iter
;
4158 for (blob_entry_iter
= new_blob_entries
->begin();
4159 blob_entry_iter
!= new_blob_entries
->end();
4160 ++blob_entry_iter
) {
4161 // Add the new blob-table entry for each blob to the main transaction, or
4162 // remove any entry that may exist if there's no new one.
4163 if (!blob_entry_iter
->second
.size())
4164 transaction_
->Remove(blob_entry_iter
->first
.Encode());
4166 transaction_
->Put(blob_entry_iter
->first
.Encode(),
4167 &blob_entry_iter
->second
);
4169 // Creating the writer will start it going asynchronously.
4170 chained_blob_writer_
=
4171 new ChainedBlobWriterImpl(database_id_
,
4174 new BlobWriteCallbackWrapper(this, callback
));
4177 void IndexedDBBackingStore::Transaction::Rollback() {
4178 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
4179 if (chained_blob_writer_
.get()) {
4180 chained_blob_writer_
->Abort();
4181 chained_blob_writer_
= NULL
;
4183 if (transaction_
.get() == NULL
)
4185 transaction_
->Rollback();
4186 transaction_
= NULL
;
4189 IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
4190 const std::string
& key
,
4191 int64 object_store_id
)
4192 : key_(key
), object_store_id_(object_store_id
) {
4195 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
4198 void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
4199 std::vector
<IndexedDBBlobInfo
>* blob_info
) {
4202 blob_info_
.swap(*blob_info
);
4205 void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
4206 ScopedVector
<storage::BlobDataHandle
>* handles
) {
4209 handles_
.swap(*handles
);
4212 scoped_ptr
<IndexedDBBackingStore::BlobChangeRecord
>
4213 IndexedDBBackingStore::BlobChangeRecord::Clone() const {
4214 scoped_ptr
<IndexedDBBackingStore::BlobChangeRecord
> record(
4215 new BlobChangeRecord(key_
, object_store_id_
));
4216 record
->blob_info_
= blob_info_
;
4218 ScopedVector
<storage::BlobDataHandle
>::const_iterator iter
;
4219 for (iter
= handles_
.begin(); iter
!= handles_
.end(); ++iter
)
4220 record
->handles_
.push_back(new storage::BlobDataHandle(**iter
));
4221 return record
.Pass();
4224 leveldb::Status
IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
4226 int64 object_store_id
,
4227 const std::string
& object_store_data_key
,
4228 std::vector
<IndexedDBBlobInfo
>* blob_info
,
4229 ScopedVector
<storage::BlobDataHandle
>* handles
) {
4230 if (!blob_info
|| blob_info
->empty()) {
4231 blob_change_map_
.erase(object_store_data_key
);
4232 incognito_blob_map_
.erase(object_store_data_key
);
4234 BlobEntryKey blob_entry_key
;
4235 StringPiece
leveldb_key_piece(object_store_data_key
);
4236 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece
,
4239 return InternalInconsistencyStatus();
4244 transaction()->Get(blob_entry_key
.Encode(), &value
, &found
);
4248 return leveldb::Status::OK();
4251 database_id
, object_store_id
, object_store_data_key
, blob_info
, handles
);
4252 return leveldb::Status::OK();
4255 // This is storing an info, even if empty, even if the previous key had no blob
4256 // info that we know of. It duplicates a bunch of information stored in the
4257 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
4258 // changes to exists or index keys here.
4259 void IndexedDBBackingStore::Transaction::PutBlobInfo(
4261 int64 object_store_id
,
4262 const std::string
& object_store_data_key
,
4263 std::vector
<IndexedDBBlobInfo
>* blob_info
,
4264 ScopedVector
<storage::BlobDataHandle
>* handles
) {
4265 DCHECK_GT(object_store_data_key
.size(), 0UL);
4266 if (database_id_
< 0)
4267 database_id_
= database_id
;
4268 DCHECK_EQ(database_id_
, database_id
);
4270 BlobChangeMap::iterator it
= blob_change_map_
.find(object_store_data_key
);
4271 BlobChangeRecord
* record
= NULL
;
4272 if (it
== blob_change_map_
.end()) {
4273 record
= new BlobChangeRecord(object_store_data_key
, object_store_id
);
4274 blob_change_map_
[object_store_data_key
] = record
;
4276 record
= it
->second
;
4278 DCHECK_EQ(record
->object_store_id(), object_store_id
);
4279 record
->SetBlobInfo(blob_info
);
4280 record
->SetHandles(handles
);
4281 DCHECK(!handles
|| !handles
->size());
4284 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4288 : is_file_(false), url_(url
), key_(key
), size_(size
) {
4291 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4292 const FilePath
& file_path
,
4295 base::Time last_modified
)
4297 file_path_(file_path
),
4300 last_modified_(last_modified
) {
4303 } // namespace content