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 fileapi::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 webkit_database::GetIdentifierFromOrigin(origin_url) + "@1";
84 static base::FilePath ComputeFileName(const GURL& origin_url) {
85 return base::FilePath()
86 .AppendASCII(webkit_database::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(webkit_database::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 &&
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<webkit_blob::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()) {
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<fileapi::FileStreamWriter> writer(
2318 fileapi::FileStreamWriter::CreateForLocalFile(
2319 task_runner_, file_path, 0,
2320 fileapi::FileStreamWriter::CREATE_NEW_FILE));
2321 scoped_ptr<FileWriterDelegate> delegate(
2322 new FileWriterDelegate(writer.Pass(),
2323 FileWriterDelegate::FLUSH_ON_COMPLETION));
2325 DCHECK(blob_url.is_valid());
2326 scoped_ptr<net::URLRequest> blob_request(request_context->CreateRequest(
2327 blob_url, net::DEFAULT_PRIORITY, delegate.get(), NULL));
2329 delegate->Start(blob_request.Pass(),
2330 base::Bind(&LocalWriteClosure::Run, this));
2331 chained_blob_writer_->set_delegate(delegate.Pass());
2335 virtual ~LocalWriteClosure() {
2336 // Make sure the last reference to a ChainedBlobWriter is released (and
2337 // deleted) on the IDB thread since it owns a transaction which has thread
2339 IndexedDBBackingStore::Transaction::ChainedBlobWriter* raw_tmp =
2340 chained_blob_writer_.get();
2342 chained_blob_writer_ = NULL;
2343 task_runner_->ReleaseSoon(FROM_HERE, raw_tmp);
2345 friend class base::RefCountedThreadSafe<LocalWriteClosure>;
2347 scoped_refptr<IndexedDBBackingStore::Transaction::ChainedBlobWriter>
2348 chained_blob_writer_;
2349 scoped_refptr<base::SequencedTaskRunner> task_runner_;
2350 int64 bytes_written_;
2352 DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure);
2355 bool IndexedDBBackingStore::WriteBlobFile(
2357 const Transaction::WriteDescriptor& descriptor,
2358 Transaction::ChainedBlobWriter* chained_blob_writer) {
2360 if (!MakeIDBBlobDirectory(blob_path_, database_id, descriptor.key()))
2363 FilePath path = GetBlobFileName(database_id, descriptor.key());
2365 if (descriptor.is_file()) {
2366 DCHECK(!descriptor.file_path().empty());
2367 if (!base::CopyFile(descriptor.file_path(), path))
2370 base::File::Info info;
2371 if (base::GetFileInfo(descriptor.file_path(), &info)) {
2372 if (descriptor.size() != -1) {
2373 if (descriptor.size() != info.size)
2375 // The round-trip can be lossy; round to nearest millisecond.
2376 int64 delta = (descriptor.last_modified() -
2377 info.last_modified).InMilliseconds();
2378 if (std::abs(delta) > 1)
2381 if (!base::TouchFile(path, info.last_accessed, info.last_modified)) {
2382 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2385 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2388 task_runner_->PostTask(
2390 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion,
2391 chained_blob_writer,
2395 DCHECK(descriptor.url().is_valid());
2396 scoped_refptr<LocalWriteClosure> write_closure(
2397 new LocalWriteClosure(chained_blob_writer, task_runner_));
2398 content::BrowserThread::PostTask(
2399 content::BrowserThread::IO,
2401 base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread,
2402 write_closure.get(),
2410 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
2412 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2413 bool all_blobs = blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2414 DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2415 scoped_refptr<LevelDBTransaction> transaction =
2416 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2418 std::string live_blob_key = LiveBlobJournalKey::Encode();
2419 BlobJournalType live_blob_journal;
2420 if (!GetBlobJournal(live_blob_key, transaction.get(), &live_blob_journal)
2423 DCHECK(live_blob_journal.size());
2425 std::string primary_key = BlobJournalKey::Encode();
2426 BlobJournalType primary_journal;
2427 if (!GetBlobJournal(primary_key, transaction.get(), &primary_journal).ok())
2430 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2431 // remove all entries with database_id from the live_blob journal and add only
2432 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2433 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2434 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2435 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2436 // matching (database_id, blob_key) tuple, we should move it to the primary
2438 BlobJournalType new_live_blob_journal;
2439 for (BlobJournalType::iterator journal_iter = live_blob_journal.begin();
2440 journal_iter != live_blob_journal.end();
2442 int64 current_database_id = journal_iter->first;
2443 int64 current_blob_key = journal_iter->second;
2444 bool current_all_blobs =
2445 current_blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2446 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id) ||
2448 if (current_database_id == database_id &&
2449 (all_blobs || current_all_blobs || blob_key == current_blob_key)) {
2451 primary_journal.push_back(
2452 std::make_pair(database_id, current_blob_key));
2453 if (current_all_blobs)
2454 new_live_blob_journal.push_back(*journal_iter);
2455 new_live_blob_journal.insert(new_live_blob_journal.end(),
2457 live_blob_journal.end()); // All the rest.
2461 new_live_blob_journal.push_back(*journal_iter);
2465 primary_journal.push_back(
2466 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
2468 UpdatePrimaryJournalWithBlobList(transaction.get(), primary_journal);
2469 UpdateLiveBlobJournalWithBlobList(transaction.get(), new_live_blob_journal);
2470 transaction->Commit();
2471 // We could just do the deletions/cleaning here, but if there are a lot of
2472 // blobs about to be garbage collected, it'd be better to wait and do them all
2474 StartJournalCleaningTimer();
2477 // The this reference is a raw pointer that's declared Unretained inside the
2478 // timer code, so this won't confuse IndexedDBFactory's check for
2479 // HasLastBackingStoreReference. It's safe because if the backing store is
2480 // deleted, the timer will automatically be canceled on destruction.
2481 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2482 journal_cleaning_timer_.Start(
2484 base::TimeDelta::FromSeconds(5),
2486 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn);
2489 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
2490 FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, int64 key) {
2491 return GetBlobFileNameForKey(blob_path_, database_id, key);
2494 static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
2495 const std::string& stop_key,
2497 unsigned char meta_data_type) {
2498 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
2501 StringPiece slice(it->Key());
2502 IndexMetaDataKey meta_data_key;
2503 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2505 if (meta_data_key.IndexId() != index_id)
2507 if (meta_data_key.meta_data_type() != meta_data_type)
2512 // TODO(jsbell): This should do some error handling rather than plowing ahead
2513 // when bad data is encountered.
2514 leveldb::Status IndexedDBBackingStore::GetIndexes(
2516 int64 object_store_id,
2517 IndexedDBObjectStoreMetadata::IndexMap* indexes) {
2518 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2519 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2520 return InvalidDBKeyStatus();
2521 const std::string start_key =
2522 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0);
2523 const std::string stop_key =
2524 IndexMetaDataKey::Encode(database_id, object_store_id + 1, 0, 0);
2526 DCHECK(indexes->empty());
2528 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
2529 leveldb::Status s = it->Seek(start_key);
2530 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
2531 StringPiece slice(it->Key());
2532 IndexMetaDataKey meta_data_key;
2533 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2535 if (meta_data_key.meta_data_type() != IndexMetaDataKey::NAME) {
2536 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2537 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2545 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2547 int64 index_id = meta_data_key.IndexId();
2548 base::string16 index_name;
2550 StringPiece slice(it->Value());
2551 if (!DecodeString(&slice, &index_name) || !slice.empty())
2552 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2555 s = it->Next(); // unique flag
2558 if (!CheckIndexAndMetaDataKey(
2559 it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
2560 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2565 StringPiece slice(it->Value());
2566 if (!DecodeBool(&slice, &index_unique) || !slice.empty())
2567 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2570 s = it->Next(); // key_path
2573 if (!CheckIndexAndMetaDataKey(
2574 it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
2575 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2578 IndexedDBKeyPath key_path;
2580 StringPiece slice(it->Value());
2581 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
2582 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2585 s = it->Next(); // [optional] multi_entry flag
2588 bool index_multi_entry = false;
2589 if (CheckIndexAndMetaDataKey(
2590 it.get(), stop_key, index_id, IndexMetaDataKey::MULTI_ENTRY)) {
2591 StringPiece slice(it->Value());
2592 if (!DecodeBool(&slice, &index_multi_entry) || !slice.empty())
2593 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2600 (*indexes)[index_id] = IndexedDBIndexMetadata(
2601 index_name, index_id, key_path, index_unique, index_multi_entry);
2605 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES);
2610 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) {
2611 FilePath fileName = GetBlobFileName(database_id, key);
2612 return base::DeleteFile(fileName, false);
2615 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) {
2616 FilePath dirName = GetBlobDirectoryName(blob_path_, database_id);
2617 return base::DeleteFile(dirName, true);
2620 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal(
2621 const std::string& level_db_key) {
2622 scoped_refptr<LevelDBTransaction> journal_transaction =
2623 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2624 BlobJournalType journal;
2626 GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
2629 if (!journal.size())
2630 return leveldb::Status::OK();
2631 BlobJournalType::iterator journal_iter;
2632 for (journal_iter = journal.begin(); journal_iter != journal.end();
2634 int64 database_id = journal_iter->first;
2635 int64 blob_key = journal_iter->second;
2636 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2637 if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
2638 if (!RemoveBlobDirectory(database_id))
2639 return IOErrorStatus();
2641 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2642 if (!RemoveBlobFile(database_id, blob_key))
2643 return IOErrorStatus();
2646 ClearBlobJournal(journal_transaction.get(), level_db_key);
2647 return journal_transaction->Commit();
2650 leveldb::Status IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
2652 const std::string& object_store_data_key,
2653 IndexedDBValue* value) {
2654 BlobChangeRecord* change_record = NULL;
2655 BlobChangeMap::const_iterator blob_iter =
2656 blob_change_map_.find(object_store_data_key);
2657 if (blob_iter != blob_change_map_.end()) {
2658 change_record = blob_iter->second;
2660 blob_iter = incognito_blob_map_.find(object_store_data_key);
2661 if (blob_iter != incognito_blob_map_.end())
2662 change_record = blob_iter->second;
2664 if (change_record) {
2665 // Either we haven't written the blob to disk yet or we're in incognito
2666 // mode, so we have to send back the one they sent us. This change record
2667 // includes the original UUID.
2668 value->blob_info = change_record->blob_info();
2669 return leveldb::Status::OK();
2672 BlobEntryKey blob_entry_key;
2673 StringPiece leveldb_key_piece(object_store_data_key);
2674 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
2677 return InternalInconsistencyStatus();
2679 scoped_ptr<LevelDBIterator> it = transaction()->CreateIterator();
2680 std::string encoded_key = blob_entry_key.Encode();
2681 leveldb::Status s = it->Seek(encoded_key);
2684 if (it->IsValid() && CompareKeys(it->Key(), encoded_key) == 0) {
2685 if (!DecodeBlobData(it->Value().as_string(), &value->blob_info)) {
2686 INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD);
2687 return InternalInconsistencyStatus();
2689 std::vector<IndexedDBBlobInfo>::iterator iter;
2690 for (iter = value->blob_info.begin(); iter != value->blob_info.end();
2692 iter->set_file_path(
2693 backing_store_->GetBlobFileName(database_id, iter->key()));
2694 iter->set_mark_used_callback(
2695 backing_store_->active_blob_registry()->GetAddBlobRefCallback(
2696 database_id, iter->key()));
2697 iter->set_release_callback(
2698 backing_store_->active_blob_registry()->GetFinalReleaseCallback(
2699 database_id, iter->key()));
2700 if (iter->is_file()) {
2701 base::File::Info info;
2702 if (base::GetFileInfo(iter->file_path(), &info)) {
2703 // This should always work, but it isn't fatal if it doesn't; it just
2704 // means a potential slow synchronous call from the renderer later.
2705 iter->set_last_modified(info.last_modified);
2706 iter->set_size(info.size);
2711 return leveldb::Status::OK();
2714 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2715 CleanUpBlobJournal(BlobJournalKey::Encode());
2718 WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId(
2719 LevelDBTransaction* transaction,
2721 int64 object_store_id,
2723 int64 max_index_id = -1;
2724 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
2725 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
2728 GetInt(transaction, max_index_id_key, &max_index_id, &found);
2730 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2734 max_index_id = kMinimumIndexId;
2736 if (index_id <= max_index_id) {
2737 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2738 return InternalInconsistencyStatus();
2741 PutInt(transaction, max_index_id_key, index_id);
2745 leveldb::Status IndexedDBBackingStore::CreateIndex(
2746 IndexedDBBackingStore::Transaction* transaction,
2748 int64 object_store_id,
2750 const base::string16& name,
2751 const IndexedDBKeyPath& key_path,
2753 bool is_multi_entry) {
2754 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2755 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2756 return InvalidDBKeyStatus();
2757 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2758 leveldb::Status s = SetMaxIndexId(
2759 leveldb_transaction, database_id, object_store_id, index_id);
2764 const std::string name_key = IndexMetaDataKey::Encode(
2765 database_id, object_store_id, index_id, IndexMetaDataKey::NAME);
2766 const std::string unique_key = IndexMetaDataKey::Encode(
2767 database_id, object_store_id, index_id, IndexMetaDataKey::UNIQUE);
2768 const std::string key_path_key = IndexMetaDataKey::Encode(
2769 database_id, object_store_id, index_id, IndexMetaDataKey::KEY_PATH);
2770 const std::string multi_entry_key = IndexMetaDataKey::Encode(
2771 database_id, object_store_id, index_id, IndexMetaDataKey::MULTI_ENTRY);
2773 PutString(leveldb_transaction, name_key, name);
2774 PutBool(leveldb_transaction, unique_key, is_unique);
2775 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
2776 PutBool(leveldb_transaction, multi_entry_key, is_multi_entry);
2780 leveldb::Status IndexedDBBackingStore::DeleteIndex(
2781 IndexedDBBackingStore::Transaction* transaction,
2783 int64 object_store_id,
2785 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2786 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2787 return InvalidDBKeyStatus();
2788 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2790 const std::string index_meta_data_start =
2791 IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0);
2792 const std::string index_meta_data_end =
2793 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2794 leveldb::Status s = DeleteRangeBasic(
2795 leveldb_transaction, index_meta_data_start, index_meta_data_end, true);
2798 const std::string index_data_start =
2799 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
2800 const std::string index_data_end =
2801 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2802 s = DeleteRangeBasic(
2803 leveldb_transaction, index_data_start, index_data_end, true);
2807 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX);
2812 leveldb::Status IndexedDBBackingStore::PutIndexDataForRecord(
2813 IndexedDBBackingStore::Transaction* transaction,
2815 int64 object_store_id,
2817 const IndexedDBKey& key,
2818 const RecordIdentifier& record_identifier) {
2819 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2820 DCHECK(key.IsValid());
2821 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2822 return InvalidDBKeyStatus();
2824 std::string encoded_key;
2825 EncodeIDBKey(key, &encoded_key);
2827 const std::string index_data_key =
2828 IndexDataKey::Encode(database_id,
2832 record_identifier.primary_key(),
2836 EncodeVarInt(record_identifier.version(), &data);
2837 data.append(record_identifier.primary_key());
2839 transaction->transaction()->Put(index_data_key, &data);
2840 return leveldb::Status::OK();
2843 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
2844 const std::string& target,
2845 std::string* found_key,
2846 leveldb::Status* s) {
2847 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
2848 *s = it->Seek(target);
2852 if (!it->IsValid()) {
2853 *s = it->SeekToLast();
2854 if (!s->ok() || !it->IsValid())
2858 while (CompareIndexKeys(it->Key(), target) > 0) {
2860 if (!s->ok() || !it->IsValid())
2865 *found_key = it->Key().as_string();
2867 // There can be several index keys that compare equal. We want the last one.
2869 } while (s->ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
2874 static leveldb::Status VersionExists(LevelDBTransaction* transaction,
2876 int64 object_store_id,
2878 const std::string& encoded_primary_key,
2880 const std::string key =
2881 ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
2884 leveldb::Status s = transaction->Get(key, &data, exists);
2886 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS);
2892 StringPiece slice(data);
2894 if (!DecodeInt(&slice, &decoded) || !slice.empty())
2895 return InternalInconsistencyStatus();
2896 *exists = (decoded == version);
2900 leveldb::Status IndexedDBBackingStore::FindKeyInIndex(
2901 IndexedDBBackingStore::Transaction* transaction,
2903 int64 object_store_id,
2905 const IndexedDBKey& key,
2906 std::string* found_encoded_primary_key,
2908 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
2909 DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id));
2911 DCHECK(found_encoded_primary_key->empty());
2914 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2915 const std::string leveldb_key =
2916 IndexDataKey::Encode(database_id, object_store_id, index_id, key);
2917 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
2918 leveldb::Status s = it->Seek(leveldb_key);
2920 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
2926 return leveldb::Status::OK();
2927 if (CompareIndexKeys(it->Key(), leveldb_key) > 0)
2928 return leveldb::Status::OK();
2930 StringPiece slice(it->Value());
2933 if (!DecodeVarInt(&slice, &version)) {
2934 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
2935 return InternalInconsistencyStatus();
2937 *found_encoded_primary_key = slice.as_string();
2939 bool exists = false;
2940 s = VersionExists(leveldb_transaction,
2944 *found_encoded_primary_key,
2949 // Delete stale index data entry and continue.
2950 leveldb_transaction->Remove(it->Key());
2959 leveldb::Status IndexedDBBackingStore::GetPrimaryKeyViaIndex(
2960 IndexedDBBackingStore::Transaction* transaction,
2962 int64 object_store_id,
2964 const IndexedDBKey& key,
2965 scoped_ptr<IndexedDBKey>* primary_key) {
2966 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
2967 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2968 return InvalidDBKeyStatus();
2971 std::string found_encoded_primary_key;
2972 leveldb::Status s = FindKeyInIndex(transaction,
2977 &found_encoded_primary_key,
2980 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
2985 if (!found_encoded_primary_key.size()) {
2986 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
2987 return InvalidDBKeyStatus();
2990 StringPiece slice(found_encoded_primary_key);
2991 if (DecodeIDBKey(&slice, primary_key) && slice.empty())
2994 return InvalidDBKeyStatus();
2997 leveldb::Status IndexedDBBackingStore::KeyExistsInIndex(
2998 IndexedDBBackingStore::Transaction* transaction,
3000 int64 object_store_id,
3002 const IndexedDBKey& index_key,
3003 scoped_ptr<IndexedDBKey>* found_primary_key,
3005 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
3006 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3007 return InvalidDBKeyStatus();
3010 std::string found_encoded_primary_key;
3011 leveldb::Status s = FindKeyInIndex(transaction,
3016 &found_encoded_primary_key,
3019 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
3023 return leveldb::Status::OK();
3024 if (found_encoded_primary_key.empty()) {
3025 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
3026 return InvalidDBKeyStatus();
3029 StringPiece slice(found_encoded_primary_key);
3030 if (DecodeIDBKey(&slice, found_primary_key) && slice.empty())
3033 return InvalidDBKeyStatus();
3036 IndexedDBBackingStore::Cursor::Cursor(
3037 const IndexedDBBackingStore::Cursor* other)
3038 : backing_store_(other->backing_store_),
3039 transaction_(other->transaction_),
3040 database_id_(other->database_id_),
3041 cursor_options_(other->cursor_options_),
3042 current_key_(new IndexedDBKey(*other->current_key_)) {
3043 if (other->iterator_) {
3044 iterator_ = transaction_->transaction()->CreateIterator();
3046 if (other->iterator_->IsValid()) {
3047 leveldb::Status s = iterator_->Seek(other->iterator_->Key());
3048 // TODO(cmumford): Handle this error (crbug.com/363397)
3049 DCHECK(iterator_->IsValid());
3054 IndexedDBBackingStore::Cursor::Cursor(
3055 scoped_refptr<IndexedDBBackingStore> backing_store,
3056 IndexedDBBackingStore::Transaction* transaction,
3058 const CursorOptions& cursor_options)
3059 : backing_store_(backing_store),
3060 transaction_(transaction),
3061 database_id_(database_id),
3062 cursor_options_(cursor_options) {
3064 IndexedDBBackingStore::Cursor::~Cursor() {}
3066 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) {
3067 iterator_ = transaction_->transaction()->CreateIterator();
3068 if (cursor_options_.forward)
3069 *s = iterator_->Seek(cursor_options_.low_key);
3071 *s = iterator_->Seek(cursor_options_.high_key);
3075 return Continue(0, READY, s);
3078 bool IndexedDBBackingStore::Cursor::Advance(uint32 count, leveldb::Status* s) {
3079 *s = leveldb::Status::OK();
3087 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
3088 const IndexedDBKey* primary_key,
3089 IteratorState next_state,
3090 leveldb::Status* s) {
3091 DCHECK(!key || key->IsValid());
3092 DCHECK(!primary_key || primary_key->IsValid());
3093 *s = leveldb::Status::OK();
3095 // TODO(alecflett): avoid a copy here?
3096 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3098 // When iterating with PrevNoDuplicate, spec requires that the
3099 // value we yield for each key is the first duplicate in forwards
3101 IndexedDBKey last_duplicate_key;
3103 bool forward = cursor_options_.forward;
3104 bool first_iteration_forward = forward;
3105 bool flipped = false;
3108 if (next_state == SEEK) {
3109 // TODO(jsbell): Optimize seeking for reverse cursors as well.
3110 if (first_iteration_forward && key) {
3111 first_iteration_forward = false;
3112 std::string leveldb_key;
3114 leveldb_key = EncodeKey(*key, *primary_key);
3116 leveldb_key = EncodeKey(*key);
3118 *s = iterator_->Seek(leveldb_key);
3119 } else if (forward) {
3120 *s = iterator_->Next();
3122 *s = iterator_->Prev();
3127 next_state = SEEK; // for subsequent iterations
3130 if (!iterator_->IsValid()) {
3131 if (!forward && last_duplicate_key.IsValid()) {
3132 // We need to walk forward because we hit the end of
3142 if (IsPastBounds()) {
3143 if (!forward && last_duplicate_key.IsValid()) {
3144 // We need to walk forward because now we're beyond the
3145 // bounds defined by the cursor.
3154 if (!HaveEnteredRange())
3157 // The row may not load because there's a stale entry in the
3158 // index. This is not fatal.
3159 if (!LoadCurrentRow())
3164 if (primary_key && current_key_->Equals(*key) &&
3165 this->primary_key().IsLessThan(*primary_key))
3167 if (!flipped && current_key_->IsLessThan(*key))
3170 if (primary_key && key->Equals(*current_key_) &&
3171 primary_key->IsLessThan(this->primary_key()))
3173 if (key->IsLessThan(*current_key_))
3178 if (cursor_options_.unique) {
3179 if (previous_key.IsValid() && current_key_->Equals(previous_key)) {
3180 // We should never be able to walk forward all the way
3181 // to the previous key.
3182 DCHECK(!last_duplicate_key.IsValid());
3187 if (!last_duplicate_key.IsValid()) {
3188 last_duplicate_key = *current_key_;
3192 // We need to walk forward because we hit the boundary
3193 // between key ranges.
3194 if (!last_duplicate_key.Equals(*current_key_)) {
3206 DCHECK(!last_duplicate_key.IsValid() ||
3207 (forward && last_duplicate_key.Equals(*current_key_)));
3211 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3212 if (cursor_options_.forward) {
3213 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3214 if (cursor_options_.low_open) {
3217 return compare >= 0;
3219 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3220 if (cursor_options_.high_open) {
3223 return compare <= 0;
3226 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
3227 if (cursor_options_.forward) {
3228 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3229 if (cursor_options_.high_open) {
3230 return compare >= 0;
3234 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3235 if (cursor_options_.low_open) {
3236 return compare <= 0;
3241 const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const {
3242 return *current_key_;
3245 const IndexedDBBackingStore::RecordIdentifier&
3246 IndexedDBBackingStore::Cursor::record_identifier() const {
3247 return record_identifier_;
3250 class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3252 ObjectStoreKeyCursorImpl(
3253 scoped_refptr<IndexedDBBackingStore> backing_store,
3254 IndexedDBBackingStore::Transaction* transaction,
3256 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3257 : IndexedDBBackingStore::Cursor(backing_store,
3262 virtual Cursor* Clone() OVERRIDE {
3263 return new ObjectStoreKeyCursorImpl(this);
3266 // IndexedDBBackingStore::Cursor
3267 virtual IndexedDBValue* value() OVERRIDE {
3271 virtual bool LoadCurrentRow() OVERRIDE;
3274 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3275 return ObjectStoreDataKey::Encode(
3276 cursor_options_.database_id, cursor_options_.object_store_id, key);
3278 virtual std::string EncodeKey(const IndexedDBKey& key,
3279 const IndexedDBKey& primary_key) OVERRIDE {
3281 return std::string();
3285 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other)
3286 : IndexedDBBackingStore::Cursor(other) {}
3288 DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl);
3291 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
3292 StringPiece slice(iterator_->Key());
3293 ObjectStoreDataKey object_store_data_key;
3294 if (!ObjectStoreDataKey::Decode(&slice, &object_store_data_key)) {
3295 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3299 current_key_ = object_store_data_key.user_key();
3302 slice = StringPiece(iterator_->Value());
3303 if (!DecodeVarInt(&slice, &version)) {
3304 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3308 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3309 std::string encoded_key;
3310 EncodeIDBKey(*current_key_, &encoded_key);
3311 record_identifier_.Reset(encoded_key, version);
3316 class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
3318 ObjectStoreCursorImpl(
3319 scoped_refptr<IndexedDBBackingStore> backing_store,
3320 IndexedDBBackingStore::Transaction* transaction,
3322 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3323 : IndexedDBBackingStore::Cursor(backing_store,
3328 virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); }
3330 // IndexedDBBackingStore::Cursor
3331 virtual IndexedDBValue* value() OVERRIDE { return ¤t_value_; }
3332 virtual bool LoadCurrentRow() OVERRIDE;
3335 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3336 return ObjectStoreDataKey::Encode(
3337 cursor_options_.database_id, cursor_options_.object_store_id, key);
3339 virtual std::string EncodeKey(const IndexedDBKey& key,
3340 const IndexedDBKey& primary_key) OVERRIDE {
3342 return std::string();
3346 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other)
3347 : IndexedDBBackingStore::Cursor(other),
3348 current_value_(other->current_value_) {}
3350 IndexedDBValue current_value_;
3352 DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl);
3355 bool ObjectStoreCursorImpl::LoadCurrentRow() {
3356 StringPiece key_slice(iterator_->Key());
3357 ObjectStoreDataKey object_store_data_key;
3358 if (!ObjectStoreDataKey::Decode(&key_slice, &object_store_data_key)) {
3359 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3363 current_key_ = object_store_data_key.user_key();
3366 StringPiece value_slice = StringPiece(iterator_->Value());
3367 if (!DecodeVarInt(&value_slice, &version)) {
3368 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3372 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3373 std::string encoded_key;
3374 EncodeIDBKey(*current_key_, &encoded_key);
3375 record_identifier_.Reset(encoded_key, version);
3377 if (!transaction_->GetBlobInfoForRecord(database_id_,
3378 iterator_->Key().as_string(),
3379 ¤t_value_).ok()) {
3382 current_value_.bits = value_slice.as_string();
3386 class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3389 scoped_refptr<IndexedDBBackingStore> backing_store,
3390 IndexedDBBackingStore::Transaction* transaction,
3392 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3393 : IndexedDBBackingStore::Cursor(backing_store,
3398 virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); }
3400 // IndexedDBBackingStore::Cursor
3401 virtual IndexedDBValue* value() OVERRIDE {
3405 virtual const IndexedDBKey& primary_key() const OVERRIDE {
3406 return *primary_key_;
3408 virtual const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3411 return record_identifier_;
3413 virtual bool LoadCurrentRow() OVERRIDE;
3416 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3417 return IndexDataKey::Encode(cursor_options_.database_id,
3418 cursor_options_.object_store_id,
3419 cursor_options_.index_id,
3422 virtual std::string EncodeKey(const IndexedDBKey& key,
3423 const IndexedDBKey& primary_key) OVERRIDE {
3424 return IndexDataKey::Encode(cursor_options_.database_id,
3425 cursor_options_.object_store_id,
3426 cursor_options_.index_id,
3432 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl* other)
3433 : IndexedDBBackingStore::Cursor(other),
3434 primary_key_(new IndexedDBKey(*other->primary_key_)) {}
3436 scoped_ptr<IndexedDBKey> primary_key_;
3438 DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl);
3441 bool IndexKeyCursorImpl::LoadCurrentRow() {
3442 StringPiece slice(iterator_->Key());
3443 IndexDataKey index_data_key;
3444 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3445 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3449 current_key_ = index_data_key.user_key();
3450 DCHECK(current_key_);
3452 slice = StringPiece(iterator_->Value());
3453 int64 index_data_version;
3454 if (!DecodeVarInt(&slice, &index_data_version)) {
3455 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3459 if (!DecodeIDBKey(&slice, &primary_key_) || !slice.empty()) {
3460 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3464 std::string primary_leveldb_key =
3465 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3466 index_data_key.ObjectStoreId(),
3472 transaction_->transaction()->Get(primary_leveldb_key, &result, &found);
3474 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3478 transaction_->transaction()->Remove(iterator_->Key());
3481 if (!result.size()) {
3482 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3486 int64 object_store_data_version;
3487 slice = StringPiece(result);
3488 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3489 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3493 if (object_store_data_version != index_data_version) {
3494 transaction_->transaction()->Remove(iterator_->Key());
3501 class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
3504 scoped_refptr<IndexedDBBackingStore> backing_store,
3505 IndexedDBBackingStore::Transaction* transaction,
3507 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3508 : IndexedDBBackingStore::Cursor(backing_store,
3513 virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); }
3515 // IndexedDBBackingStore::Cursor
3516 virtual IndexedDBValue* value() OVERRIDE { return ¤t_value_; }
3517 virtual const IndexedDBKey& primary_key() const OVERRIDE {
3518 return *primary_key_;
3520 virtual const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3523 return record_identifier_;
3525 virtual bool LoadCurrentRow() OVERRIDE;
3528 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3529 return IndexDataKey::Encode(cursor_options_.database_id,
3530 cursor_options_.object_store_id,
3531 cursor_options_.index_id,
3534 virtual std::string EncodeKey(const IndexedDBKey& key,
3535 const IndexedDBKey& primary_key) OVERRIDE {
3536 return IndexDataKey::Encode(cursor_options_.database_id,
3537 cursor_options_.object_store_id,
3538 cursor_options_.index_id,
3544 explicit IndexCursorImpl(const IndexCursorImpl* other)
3545 : IndexedDBBackingStore::Cursor(other),
3546 primary_key_(new IndexedDBKey(*other->primary_key_)),
3547 current_value_(other->current_value_),
3548 primary_leveldb_key_(other->primary_leveldb_key_) {}
3550 scoped_ptr<IndexedDBKey> primary_key_;
3551 IndexedDBValue current_value_;
3552 std::string primary_leveldb_key_;
3554 DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl);
3557 bool IndexCursorImpl::LoadCurrentRow() {
3558 StringPiece slice(iterator_->Key());
3559 IndexDataKey index_data_key;
3560 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3561 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3565 current_key_ = index_data_key.user_key();
3566 DCHECK(current_key_);
3568 slice = StringPiece(iterator_->Value());
3569 int64 index_data_version;
3570 if (!DecodeVarInt(&slice, &index_data_version)) {
3571 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3574 if (!DecodeIDBKey(&slice, &primary_key_)) {
3575 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3579 DCHECK_EQ(index_data_key.DatabaseId(), database_id_);
3580 primary_leveldb_key_ =
3581 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3582 index_data_key.ObjectStoreId(),
3588 transaction_->transaction()->Get(primary_leveldb_key_, &result, &found);
3590 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3594 transaction_->transaction()->Remove(iterator_->Key());
3597 if (!result.size()) {
3598 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3602 int64 object_store_data_version;
3603 slice = StringPiece(result);
3604 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3605 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3609 if (object_store_data_version != index_data_version) {
3610 transaction_->transaction()->Remove(iterator_->Key());
3614 current_value_.bits = slice.as_string();
3615 return transaction_->GetBlobInfoForRecord(database_id_,
3616 primary_leveldb_key_,
3617 ¤t_value_).ok();
3620 bool ObjectStoreCursorOptions(
3621 LevelDBTransaction* transaction,
3623 int64 object_store_id,
3624 const IndexedDBKeyRange& range,
3625 blink::WebIDBCursorDirection direction,
3626 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3627 cursor_options->database_id = database_id;
3628 cursor_options->object_store_id = object_store_id;
3630 bool lower_bound = range.lower().IsValid();
3631 bool upper_bound = range.upper().IsValid();
3632 cursor_options->forward =
3633 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3634 direction == blink::WebIDBCursorDirectionNext);
3635 cursor_options->unique =
3636 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3637 direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
3640 cursor_options->low_key =
3641 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
3642 cursor_options->low_open = true; // Not included.
3644 cursor_options->low_key =
3645 ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower());
3646 cursor_options->low_open = range.lowerOpen();
3652 cursor_options->high_key =
3653 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
3655 if (cursor_options->forward) {
3656 cursor_options->high_open = true; // Not included.
3658 // We need a key that exists.
3659 // TODO(cmumford): Handle this error (crbug.com/363397)
3660 if (!FindGreatestKeyLessThanOrEqual(transaction,
3661 cursor_options->high_key,
3662 &cursor_options->high_key,
3665 cursor_options->high_open = false;
3668 cursor_options->high_key =
3669 ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper());
3670 cursor_options->high_open = range.upperOpen();
3672 if (!cursor_options->forward) {
3673 // For reverse cursors, we need a key that exists.
3674 std::string found_high_key;
3675 // TODO(cmumford): Handle this error (crbug.com/363397)
3676 if (!FindGreatestKeyLessThanOrEqual(
3677 transaction, cursor_options->high_key, &found_high_key, &s))
3680 // If the target key should not be included, but we end up with a smaller
3681 // key, we should include that.
3682 if (cursor_options->high_open &&
3683 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3684 cursor_options->high_open = false;
3686 cursor_options->high_key = found_high_key;
3693 bool IndexCursorOptions(
3694 LevelDBTransaction* transaction,
3696 int64 object_store_id,
3698 const IndexedDBKeyRange& range,
3699 blink::WebIDBCursorDirection direction,
3700 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3701 DCHECK(transaction);
3702 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3705 cursor_options->database_id = database_id;
3706 cursor_options->object_store_id = object_store_id;
3707 cursor_options->index_id = index_id;
3709 bool lower_bound = range.lower().IsValid();
3710 bool upper_bound = range.upper().IsValid();
3711 cursor_options->forward =
3712 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3713 direction == blink::WebIDBCursorDirectionNext);
3714 cursor_options->unique =
3715 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3716 direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
3719 cursor_options->low_key =
3720 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
3721 cursor_options->low_open = false; // Included.
3723 cursor_options->low_key = IndexDataKey::Encode(
3724 database_id, object_store_id, index_id, range.lower());
3725 cursor_options->low_open = range.lowerOpen();
3731 cursor_options->high_key =
3732 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
3733 cursor_options->high_open = false; // Included.
3735 if (!cursor_options->forward) { // We need a key that exists.
3736 if (!FindGreatestKeyLessThanOrEqual(transaction,
3737 cursor_options->high_key,
3738 &cursor_options->high_key,
3741 cursor_options->high_open = false;
3744 cursor_options->high_key = IndexDataKey::Encode(
3745 database_id, object_store_id, index_id, range.upper());
3746 cursor_options->high_open = range.upperOpen();
3748 std::string found_high_key;
3749 // Seek to the *last* key in the set of non-unique keys
3750 // TODO(cmumford): Handle this error (crbug.com/363397)
3751 if (!FindGreatestKeyLessThanOrEqual(
3752 transaction, cursor_options->high_key, &found_high_key, &s))
3755 // If the target key should not be included, but we end up with a smaller
3756 // key, we should include that.
3757 if (cursor_options->high_open &&
3758 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3759 cursor_options->high_open = false;
3761 cursor_options->high_key = found_high_key;
3767 scoped_ptr<IndexedDBBackingStore::Cursor>
3768 IndexedDBBackingStore::OpenObjectStoreCursor(
3769 IndexedDBBackingStore::Transaction* transaction,
3771 int64 object_store_id,
3772 const IndexedDBKeyRange& range,
3773 blink::WebIDBCursorDirection direction,
3774 leveldb::Status* s) {
3775 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3776 *s = leveldb::Status::OK();
3777 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3778 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3779 if (!ObjectStoreCursorOptions(leveldb_transaction,
3785 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3786 scoped_ptr<ObjectStoreCursorImpl> cursor(new ObjectStoreCursorImpl(
3787 this, transaction, database_id, cursor_options));
3788 if (!cursor->FirstSeek(s))
3789 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3791 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3794 scoped_ptr<IndexedDBBackingStore::Cursor>
3795 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3796 IndexedDBBackingStore::Transaction* transaction,
3798 int64 object_store_id,
3799 const IndexedDBKeyRange& range,
3800 blink::WebIDBCursorDirection direction,
3801 leveldb::Status* s) {
3802 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3803 *s = leveldb::Status::OK();
3804 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3805 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3806 if (!ObjectStoreCursorOptions(leveldb_transaction,
3812 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3813 scoped_ptr<ObjectStoreKeyCursorImpl> cursor(new ObjectStoreKeyCursorImpl(
3814 this, transaction, database_id, cursor_options));
3815 if (!cursor->FirstSeek(s))
3816 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3818 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3821 scoped_ptr<IndexedDBBackingStore::Cursor>
3822 IndexedDBBackingStore::OpenIndexKeyCursor(
3823 IndexedDBBackingStore::Transaction* transaction,
3825 int64 object_store_id,
3827 const IndexedDBKeyRange& range,
3828 blink::WebIDBCursorDirection direction,
3829 leveldb::Status* s) {
3830 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3831 *s = leveldb::Status::OK();
3832 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3833 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3834 if (!IndexCursorOptions(leveldb_transaction,
3841 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3842 scoped_ptr<IndexKeyCursorImpl> cursor(
3843 new IndexKeyCursorImpl(this, transaction, database_id, cursor_options));
3844 if (!cursor->FirstSeek(s))
3845 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3847 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3850 scoped_ptr<IndexedDBBackingStore::Cursor>
3851 IndexedDBBackingStore::OpenIndexCursor(
3852 IndexedDBBackingStore::Transaction* transaction,
3854 int64 object_store_id,
3856 const IndexedDBKeyRange& range,
3857 blink::WebIDBCursorDirection direction,
3858 leveldb::Status* s) {
3859 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
3860 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3861 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3862 if (!IndexCursorOptions(leveldb_transaction,
3869 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3870 scoped_ptr<IndexCursorImpl> cursor(
3871 new IndexCursorImpl(this, transaction, database_id, cursor_options));
3872 if (!cursor->FirstSeek(s))
3873 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3875 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3878 IndexedDBBackingStore::Transaction::Transaction(
3879 IndexedDBBackingStore* backing_store)
3880 : backing_store_(backing_store), database_id_(-1) {
3883 IndexedDBBackingStore::Transaction::~Transaction() {
3884 STLDeleteContainerPairSecondPointers(
3885 blob_change_map_.begin(), blob_change_map_.end());
3886 STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
3887 incognito_blob_map_.end());
3890 void IndexedDBBackingStore::Transaction::Begin() {
3891 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
3892 DCHECK(!transaction_.get());
3893 transaction_ = IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3894 backing_store_->db_.get());
3896 // If incognito, this snapshots blobs just as the above transaction_
3897 // constructor snapshots the leveldb.
3898 BlobChangeMap::const_iterator iter;
3899 for (iter = backing_store_->incognito_blob_map_.begin();
3900 iter != backing_store_->incognito_blob_map_.end();
3902 incognito_blob_map_[iter->first] = iter->second->Clone().release();
3905 static GURL getURLFromUUID(const string& uuid) {
3906 return GURL("blob:uuid/" + uuid);
3909 leveldb::Status IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
3910 BlobEntryKeyValuePairVec* new_blob_entries,
3911 WriteDescriptorVec* new_files_to_write) {
3912 if (backing_store_->is_incognito())
3913 return leveldb::Status::OK();
3915 BlobChangeMap::iterator iter = blob_change_map_.begin();
3916 new_blob_entries->clear();
3917 new_files_to_write->clear();
3918 if (iter != blob_change_map_.end()) {
3919 // Create LevelDBTransaction for the name generator seed and add-journal.
3920 scoped_refptr<LevelDBTransaction> pre_transaction =
3921 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3922 backing_store_->db_.get());
3923 BlobJournalType journal;
3924 for (; iter != blob_change_map_.end(); ++iter) {
3925 std::vector<IndexedDBBlobInfo>::iterator info_iter;
3926 std::vector<IndexedDBBlobInfo*> new_blob_keys;
3927 for (info_iter = iter->second->mutable_blob_info().begin();
3928 info_iter != iter->second->mutable_blob_info().end();
3930 int64 next_blob_key = -1;
3931 bool result = GetBlobKeyGeneratorCurrentNumber(
3932 pre_transaction.get(), database_id_, &next_blob_key);
3933 if (!result || next_blob_key < 0)
3934 return InternalInconsistencyStatus();
3935 BlobJournalEntryType journal_entry =
3936 std::make_pair(database_id_, next_blob_key);
3937 journal.push_back(journal_entry);
3938 if (info_iter->is_file()) {
3939 new_files_to_write->push_back(
3940 WriteDescriptor(info_iter->file_path(),
3943 info_iter->last_modified()));
3945 new_files_to_write->push_back(
3946 WriteDescriptor(getURLFromUUID(info_iter->uuid()),
3948 info_iter->size()));
3950 info_iter->set_key(next_blob_key);
3951 new_blob_keys.push_back(&*info_iter);
3952 result = UpdateBlobKeyGeneratorCurrentNumber(
3953 pre_transaction.get(), database_id_, next_blob_key + 1);
3955 return InternalInconsistencyStatus();
3957 BlobEntryKey blob_entry_key;
3958 StringPiece key_piece(iter->second->key());
3959 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
3961 return InternalInconsistencyStatus();
3963 new_blob_entries->push_back(
3964 std::make_pair(blob_entry_key, EncodeBlobData(new_blob_keys)));
3966 UpdatePrimaryJournalWithBlobList(pre_transaction.get(), journal);
3967 leveldb::Status s = pre_transaction->Commit();
3969 return InternalInconsistencyStatus();
3971 return leveldb::Status::OK();
3974 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
3975 if (backing_store_->is_incognito())
3978 BlobChangeMap::const_iterator iter = blob_change_map_.begin();
3979 // Look up all old files to remove as part of the transaction, store their
3980 // names in blobs_to_remove_, and remove their old blob data entries.
3981 if (iter != blob_change_map_.end()) {
3982 scoped_ptr<LevelDBIterator> db_iter = transaction_->CreateIterator();
3983 for (; iter != blob_change_map_.end(); ++iter) {
3984 BlobEntryKey blob_entry_key;
3985 StringPiece key_piece(iter->second->key());
3986 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
3988 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
3989 transaction_ = NULL;
3992 if (database_id_ < 0)
3993 database_id_ = blob_entry_key.database_id();
3995 DCHECK_EQ(database_id_, blob_entry_key.database_id());
3996 std::string blob_entry_key_bytes = blob_entry_key.Encode();
3997 db_iter->Seek(blob_entry_key_bytes);
3998 if (db_iter->IsValid() &&
3999 !CompareKeys(db_iter->Key(), blob_entry_key_bytes)) {
4000 std::vector<IndexedDBBlobInfo> blob_info;
4001 if (!DecodeBlobData(db_iter->Value().as_string(), &blob_info)) {
4002 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4003 transaction_ = NULL;
4006 std::vector<IndexedDBBlobInfo>::iterator blob_info_iter;
4007 for (blob_info_iter = blob_info.begin();
4008 blob_info_iter != blob_info.end();
4010 blobs_to_remove_.push_back(
4011 std::make_pair(database_id_, blob_info_iter->key()));
4012 transaction_->Remove(blob_entry_key_bytes);
4019 leveldb::Status IndexedDBBackingStore::Transaction::SortBlobsToRemove() {
4020 IndexedDBActiveBlobRegistry* registry =
4021 backing_store_->active_blob_registry();
4022 BlobJournalType::iterator iter;
4023 BlobJournalType primary_journal, live_blob_journal;
4024 for (iter = blobs_to_remove_.begin(); iter != blobs_to_remove_.end();
4026 if (registry->MarkDeletedCheckIfUsed(iter->first, iter->second))
4027 live_blob_journal.push_back(*iter);
4029 primary_journal.push_back(*iter);
4031 UpdatePrimaryJournalWithBlobList(transaction_.get(), primary_journal);
4033 MergeBlobsIntoLiveBlobJournal(transaction_.get(), live_blob_journal);
4036 // To signal how many blobs need attention right now.
4037 blobs_to_remove_.swap(primary_journal);
4038 return leveldb::Status::OK();
4041 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne(
4042 scoped_refptr<BlobWriteCallback> callback) {
4043 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
4044 DCHECK(transaction_);
4045 DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
4049 s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
4051 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
4052 transaction_ = NULL;
4056 BlobEntryKeyValuePairVec new_blob_entries;
4057 WriteDescriptorVec new_files_to_write;
4058 s = HandleBlobPreTransaction(&new_blob_entries, &new_files_to_write);
4060 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4061 transaction_ = NULL;
4065 DCHECK(!new_files_to_write.size() ||
4066 KeyPrefix::IsValidDatabaseId(database_id_));
4067 if (!CollectBlobFilesToRemove()) {
4068 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4069 transaction_ = NULL;
4070 return InternalInconsistencyStatus();
4073 if (new_files_to_write.size()) {
4074 // This kicks off the writes of the new blobs, if any.
4075 // This call will zero out new_blob_entries and new_files_to_write.
4076 WriteNewBlobs(&new_blob_entries, &new_files_to_write, callback);
4077 // Remove the add journal, if any; once the blobs are written, and we
4078 // commit, this will do the cleanup.
4079 ClearBlobJournal(transaction_.get(), BlobJournalKey::Encode());
4081 callback->Run(true);
4084 return leveldb::Status::OK();
4087 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
4088 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
4090 if (blobs_to_remove_.size()) {
4091 s = SortBlobsToRemove();
4093 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4094 transaction_ = NULL;
4099 s = transaction_->Commit();
4100 transaction_ = NULL;
4102 if (s.ok() && backing_store_->is_incognito() && !blob_change_map_.empty()) {
4103 BlobChangeMap& target_map = backing_store_->incognito_blob_map_;
4104 BlobChangeMap::iterator iter;
4105 for (iter = blob_change_map_.begin(); iter != blob_change_map_.end();
4107 BlobChangeMap::iterator target_record = target_map.find(iter->first);
4108 if (target_record != target_map.end()) {
4109 delete target_record->second;
4110 target_map.erase(target_record);
4113 target_map[iter->first] = iter->second;
4114 iter->second = NULL;
4119 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
4120 else if (blobs_to_remove_.size())
4121 s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
4127 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
4128 : public IndexedDBBackingStore::BlobWriteCallback {
4130 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction,
4131 scoped_refptr<BlobWriteCallback> callback)
4132 : transaction_(transaction), callback_(callback) {}
4133 virtual void Run(bool succeeded) OVERRIDE {
4134 callback_->Run(succeeded);
4135 if (succeeded) // Else it's already been deleted during rollback.
4136 transaction_->chained_blob_writer_ = NULL;
4140 virtual ~BlobWriteCallbackWrapper() {}
4141 friend class base::RefCounted<IndexedDBBackingStore::BlobWriteCallback>;
4143 IndexedDBBackingStore::Transaction* transaction_;
4144 scoped_refptr<BlobWriteCallback> callback_;
4146 DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper);
4149 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
4150 BlobEntryKeyValuePairVec* new_blob_entries,
4151 WriteDescriptorVec* new_files_to_write,
4152 scoped_refptr<BlobWriteCallback> callback) {
4153 DCHECK_GT(new_files_to_write->size(), 0UL);
4154 DCHECK_GT(database_id_, 0);
4155 BlobEntryKeyValuePairVec::iterator blob_entry_iter;
4156 for (blob_entry_iter = new_blob_entries->begin();
4157 blob_entry_iter != new_blob_entries->end();
4158 ++blob_entry_iter) {
4159 // Add the new blob-table entry for each blob to the main transaction, or
4160 // remove any entry that may exist if there's no new one.
4161 if (!blob_entry_iter->second.size())
4162 transaction_->Remove(blob_entry_iter->first.Encode());
4164 transaction_->Put(blob_entry_iter->first.Encode(),
4165 &blob_entry_iter->second);
4167 // Creating the writer will start it going asynchronously.
4168 chained_blob_writer_ =
4169 new ChainedBlobWriterImpl(database_id_,
4172 new BlobWriteCallbackWrapper(this, callback));
4175 void IndexedDBBackingStore::Transaction::Rollback() {
4176 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
4177 if (chained_blob_writer_) {
4178 chained_blob_writer_->Abort();
4179 chained_blob_writer_ = NULL;
4181 if (transaction_.get() == NULL)
4183 transaction_->Rollback();
4184 transaction_ = NULL;
4187 IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
4188 const std::string& key,
4189 int64 object_store_id)
4190 : key_(key), object_store_id_(object_store_id) {
4193 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
4196 void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
4197 std::vector<IndexedDBBlobInfo>* blob_info) {
4200 blob_info_.swap(*blob_info);
4203 void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
4204 ScopedVector<webkit_blob::BlobDataHandle>* handles) {
4207 handles_.swap(*handles);
4210 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord>
4211 IndexedDBBackingStore::BlobChangeRecord::Clone() const {
4212 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord> record(
4213 new BlobChangeRecord(key_, object_store_id_));
4214 record->blob_info_ = blob_info_;
4216 ScopedVector<webkit_blob::BlobDataHandle>::const_iterator iter;
4217 for (iter = handles_.begin(); iter != handles_.end(); ++iter)
4218 record->handles_.push_back(new webkit_blob::BlobDataHandle(**iter));
4219 return record.Pass();
4222 leveldb::Status IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
4224 int64 object_store_id,
4225 const std::string& object_store_data_key,
4226 std::vector<IndexedDBBlobInfo>* blob_info,
4227 ScopedVector<webkit_blob::BlobDataHandle>* handles) {
4228 if (!blob_info || blob_info->empty()) {
4229 blob_change_map_.erase(object_store_data_key);
4230 incognito_blob_map_.erase(object_store_data_key);
4232 BlobEntryKey blob_entry_key;
4233 StringPiece leveldb_key_piece(object_store_data_key);
4234 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
4237 return InternalInconsistencyStatus();
4242 transaction()->Get(blob_entry_key.Encode(), &value, &found);
4246 return leveldb::Status::OK();
4249 database_id, object_store_id, object_store_data_key, blob_info, handles);
4250 return leveldb::Status::OK();
4253 // This is storing an info, even if empty, even if the previous key had no blob
4254 // info that we know of. It duplicates a bunch of information stored in the
4255 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
4256 // changes to exists or index keys here.
4257 void IndexedDBBackingStore::Transaction::PutBlobInfo(
4259 int64 object_store_id,
4260 const std::string& object_store_data_key,
4261 std::vector<IndexedDBBlobInfo>* blob_info,
4262 ScopedVector<webkit_blob::BlobDataHandle>* handles) {
4263 DCHECK_GT(object_store_data_key.size(), 0UL);
4264 if (database_id_ < 0)
4265 database_id_ = database_id;
4266 DCHECK_EQ(database_id_, database_id);
4268 BlobChangeMap::iterator it = blob_change_map_.find(object_store_data_key);
4269 BlobChangeRecord* record = NULL;
4270 if (it == blob_change_map_.end()) {
4271 record = new BlobChangeRecord(object_store_data_key, object_store_id);
4272 blob_change_map_[object_store_data_key] = record;
4274 record = it->second;
4276 DCHECK_EQ(record->object_store_id(), object_store_id);
4277 record->SetBlobInfo(blob_info);
4278 record->SetHandles(handles);
4279 DCHECK(!handles || !handles->size());
4282 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4286 : is_file_(false), url_(url), key_(key), size_(size) {
4289 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4290 const FilePath& file_path,
4293 base::Time last_modified)
4295 file_path_(file_path),
4298 last_modified_(last_modified) {
4301 } // namespace content