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_database_error.h"
20 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
21 #include "content/browser/indexed_db/indexed_db_metadata.h"
22 #include "content/browser/indexed_db/indexed_db_tracing.h"
23 #include "content/browser/indexed_db/indexed_db_value.h"
24 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
25 #include "content/browser/indexed_db/leveldb/leveldb_database.h"
26 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
27 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
28 #include "content/common/indexed_db/indexed_db_key.h"
29 #include "content/common/indexed_db/indexed_db_key_path.h"
30 #include "content/common/indexed_db/indexed_db_key_range.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "net/url_request/url_request_context.h"
33 #include "third_party/WebKit/public/platform/WebIDBTypes.h"
34 #include "third_party/WebKit/public/web/WebSerializedScriptValueVersion.h"
35 #include "third_party/leveldatabase/env_chromium.h"
36 #include "webkit/browser/blob/blob_data_handle.h"
37 #include "webkit/browser/fileapi/file_stream_writer.h"
38 #include "webkit/browser/fileapi/file_writer_delegate.h"
39 #include "webkit/browser/fileapi/local_file_stream_writer.h"
40 #include "webkit/common/database/database_identifier.h"
43 using base::StringPiece
;
44 using fileapi::FileWriterDelegate
;
50 FilePath
GetBlobDirectoryName(const FilePath
& pathBase
, int64 database_id
) {
51 return pathBase
.AppendASCII(base::StringPrintf("%" PRIx64
, database_id
));
54 FilePath
GetBlobDirectoryNameForKey(const FilePath
& pathBase
,
57 FilePath path
= GetBlobDirectoryName(pathBase
, database_id
);
58 path
= path
.AppendASCII(base::StringPrintf(
59 "%02x", static_cast<int>(key
& 0x000000000000ff00) >> 8));
63 FilePath
GetBlobFileNameForKey(const FilePath
& pathBase
,
66 FilePath path
= GetBlobDirectoryNameForKey(pathBase
, database_id
, key
);
67 path
= path
.AppendASCII(base::StringPrintf("%" PRIx64
, key
));
71 bool MakeIDBBlobDirectory(const FilePath
& pathBase
,
74 FilePath path
= GetBlobDirectoryNameForKey(pathBase
, database_id
, key
);
75 return base::CreateDirectory(path
);
78 static std::string
ComputeOriginIdentifier(const GURL
& origin_url
) {
79 return webkit_database::GetIdentifierFromOrigin(origin_url
) + "@1";
82 static base::FilePath
ComputeFileName(const GURL
& origin_url
) {
83 return base::FilePath()
84 .AppendASCII(webkit_database::GetIdentifierFromOrigin(origin_url
))
85 .AddExtension(FILE_PATH_LITERAL(".indexeddb.leveldb"));
88 static base::FilePath
ComputeBlobPath(const GURL
& origin_url
) {
89 return base::FilePath()
90 .AppendASCII(webkit_database::GetIdentifierFromOrigin(origin_url
))
91 .AddExtension(FILE_PATH_LITERAL(".indexeddb.blob"));
94 static base::FilePath
ComputeCorruptionFileName(const GURL
& origin_url
) {
95 return ComputeFileName(origin_url
)
96 .Append(FILE_PATH_LITERAL("corruption_info.json"));
101 static const int64 kKeyGeneratorInitialNumber
=
102 1; // From the IndexedDB specification.
104 enum IndexedDBBackingStoreErrorSource
{
105 // 0 - 2 are no longer used.
106 FIND_KEY_IN_INDEX
= 3,
107 GET_IDBDATABASE_METADATA
,
109 GET_KEY_GENERATOR_CURRENT_NUMBER
,
112 KEY_EXISTS_IN_OBJECT_STORE
,
115 GET_PRIMARY_KEY_VIA_INDEX
,
119 SET_MAX_OBJECT_STORE_ID
,
122 GET_NEW_VERSION_NUMBER
,
123 CREATE_IDBDATABASE_METADATA
,
125 TRANSACTION_COMMIT_METHOD
, // TRANSACTION_COMMIT is a WinNT.h macro
134 static void RecordInternalError(const char* type
,
135 IndexedDBBackingStoreErrorSource location
) {
137 name
.append("WebCore.IndexedDB.BackingStore.").append(type
).append("Error");
138 base::Histogram::FactoryGet(name
,
141 INTERNAL_ERROR_MAX
+ 1,
142 base::HistogramBase::kUmaTargetedHistogramFlag
)
146 // Use to signal conditions caused by data corruption.
147 // A macro is used instead of an inline function so that the assert and log
148 // report the line number.
149 #define REPORT_ERROR(type, location) \
151 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
152 RecordInternalError(type, location); \
155 #define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location)
156 #define INTERNAL_CONSISTENCY_ERROR(location) \
157 REPORT_ERROR("Consistency", location)
158 #define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
160 // Use to signal conditions that usually indicate developer error, but
161 // could be caused by data corruption. A macro is used instead of an
162 // inline function so that the assert and log report the line number.
163 // TODO: Improve test coverage so that all error conditions are "tested" and
164 // then delete this macro.
165 #define REPORT_ERROR_UNTESTED(type, location) \
167 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
169 RecordInternalError(type, location); \
172 #define INTERNAL_READ_ERROR_UNTESTED(location) \
173 REPORT_ERROR_UNTESTED("Read", location)
174 #define INTERNAL_CONSISTENCY_ERROR_UNTESTED(location) \
175 REPORT_ERROR_UNTESTED("Consistency", location)
176 #define INTERNAL_WRITE_ERROR_UNTESTED(location) \
177 REPORT_ERROR_UNTESTED("Write", location)
179 static void PutBool(LevelDBTransaction
* transaction
,
180 const StringPiece
& key
,
183 EncodeBool(value
, &buffer
);
184 transaction
->Put(key
, &buffer
);
187 // Was able to use LevelDB to read the data w/o error, but the data read was not
188 // in the expected format.
189 static leveldb::Status
InternalInconsistencyStatus() {
190 return leveldb::Status::Corruption("Internal inconsistency");
193 static leveldb::Status
InvalidDBKeyStatus() {
194 return leveldb::Status::InvalidArgument("Invalid database key ID");
197 static leveldb::Status
IOErrorStatus() {
198 return leveldb::Status::IOError("IO Error");
201 template <typename DBOrTransaction
>
202 static leveldb::Status
GetInt(DBOrTransaction
* db
,
203 const StringPiece
& key
,
207 leveldb::Status s
= db
->Get(key
, &result
, found
);
211 return leveldb::Status::OK();
212 StringPiece
slice(result
);
213 if (DecodeInt(&slice
, found_int
) && slice
.empty())
215 return InternalInconsistencyStatus();
218 static void PutInt(LevelDBTransaction
* transaction
,
219 const StringPiece
& key
,
223 EncodeInt(value
, &buffer
);
224 transaction
->Put(key
, &buffer
);
227 template <typename DBOrTransaction
>
228 WARN_UNUSED_RESULT
static leveldb::Status
GetVarInt(DBOrTransaction
* db
,
229 const StringPiece
& key
,
233 leveldb::Status s
= db
->Get(key
, &result
, found
);
237 return leveldb::Status::OK();
238 StringPiece
slice(result
);
239 if (DecodeVarInt(&slice
, found_int
) && slice
.empty())
241 return InternalInconsistencyStatus();
244 static void PutVarInt(LevelDBTransaction
* transaction
,
245 const StringPiece
& key
,
248 EncodeVarInt(value
, &buffer
);
249 transaction
->Put(key
, &buffer
);
252 template <typename DBOrTransaction
>
253 WARN_UNUSED_RESULT
static leveldb::Status
GetString(
255 const StringPiece
& key
,
256 base::string16
* found_string
,
260 leveldb::Status s
= db
->Get(key
, &result
, found
);
264 return leveldb::Status::OK();
265 StringPiece
slice(result
);
266 if (DecodeString(&slice
, found_string
) && slice
.empty())
268 return InternalInconsistencyStatus();
271 static void PutString(LevelDBTransaction
* transaction
,
272 const StringPiece
& key
,
273 const base::string16
& value
) {
275 EncodeString(value
, &buffer
);
276 transaction
->Put(key
, &buffer
);
279 static void PutIDBKeyPath(LevelDBTransaction
* transaction
,
280 const StringPiece
& key
,
281 const IndexedDBKeyPath
& value
) {
283 EncodeIDBKeyPath(value
, &buffer
);
284 transaction
->Put(key
, &buffer
);
287 static int CompareKeys(const StringPiece
& a
, const StringPiece
& b
) {
288 return Compare(a
, b
, false /*index_keys*/);
291 static int CompareIndexKeys(const StringPiece
& a
, const StringPiece
& b
) {
292 return Compare(a
, b
, true /*index_keys*/);
295 int IndexedDBBackingStore::Comparator::Compare(const StringPiece
& a
,
296 const StringPiece
& b
) const {
297 return content::Compare(a
, b
, false /*index_keys*/);
300 const char* IndexedDBBackingStore::Comparator::Name() const {
304 // 0 - Initial version.
305 // 1 - Adds UserIntVersion to DatabaseMetaData.
306 // 2 - Adds DataVersion to to global metadata.
307 static const int64 kLatestKnownSchemaVersion
= 2;
308 WARN_UNUSED_RESULT
static bool IsSchemaKnown(LevelDBDatabase
* db
, bool* known
) {
309 int64 db_schema_version
= 0;
312 GetInt(db
, SchemaVersionKey::Encode(), &db_schema_version
, &found
);
319 if (db_schema_version
> kLatestKnownSchemaVersion
) {
324 const uint32 latest_known_data_version
=
325 blink::kSerializedScriptValueVersion
;
326 int64 db_data_version
= 0;
327 s
= GetInt(db
, DataVersionKey::Encode(), &db_data_version
, &found
);
335 if (db_data_version
> latest_known_data_version
) {
344 WARN_UNUSED_RESULT
static bool SetUpMetadata(
346 const std::string
& origin_identifier
) {
347 const uint32 latest_known_data_version
=
348 blink::kSerializedScriptValueVersion
;
349 const std::string schema_version_key
= SchemaVersionKey::Encode();
350 const std::string data_version_key
= DataVersionKey::Encode();
352 scoped_refptr
<LevelDBTransaction
> transaction
= new LevelDBTransaction(db
);
354 int64 db_schema_version
= 0;
355 int64 db_data_version
= 0;
358 GetInt(transaction
.get(), schema_version_key
, &db_schema_version
, &found
);
360 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
364 // Initialize new backing store.
365 db_schema_version
= kLatestKnownSchemaVersion
;
366 PutInt(transaction
.get(), schema_version_key
, db_schema_version
);
367 db_data_version
= latest_known_data_version
;
368 PutInt(transaction
.get(), data_version_key
, db_data_version
);
370 // Upgrade old backing store.
371 DCHECK_LE(db_schema_version
, kLatestKnownSchemaVersion
);
372 if (db_schema_version
< 1) {
373 db_schema_version
= 1;
374 PutInt(transaction
.get(), schema_version_key
, db_schema_version
);
375 const std::string start_key
=
376 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier
);
377 const std::string stop_key
=
378 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier
);
379 scoped_ptr
<LevelDBIterator
> it
= db
->CreateIterator();
380 for (s
= it
->Seek(start_key
);
381 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
383 int64 database_id
= 0;
385 s
= GetInt(transaction
.get(), it
->Key(), &database_id
, &found
);
387 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
391 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA
);
394 std::string int_version_key
= DatabaseMetaDataKey::Encode(
395 database_id
, DatabaseMetaDataKey::USER_INT_VERSION
);
396 PutVarInt(transaction
.get(),
398 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
);
401 if (s
.ok() && db_schema_version
< 2) {
402 db_schema_version
= 2;
403 PutInt(transaction
.get(), schema_version_key
, db_schema_version
);
404 db_data_version
= blink::kSerializedScriptValueVersion
;
405 PutInt(transaction
.get(), data_version_key
, db_data_version
);
410 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
414 // All new values will be written using this serialization version.
416 s
= GetInt(transaction
.get(), data_version_key
, &db_data_version
, &found
);
418 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA
);
422 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA
);
425 if (db_data_version
< latest_known_data_version
) {
426 db_data_version
= latest_known_data_version
;
427 PutInt(transaction
.get(), data_version_key
, db_data_version
);
430 DCHECK_EQ(db_schema_version
, kLatestKnownSchemaVersion
);
431 DCHECK_EQ(db_data_version
, latest_known_data_version
);
433 s
= transaction
->Commit();
435 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA
);
441 template <typename DBOrTransaction
>
442 WARN_UNUSED_RESULT
static leveldb::Status
GetMaxObjectStoreId(
445 int64
* max_object_store_id
) {
446 const std::string max_object_store_id_key
= DatabaseMetaDataKey::Encode(
447 database_id
, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID
);
448 return GetMaxObjectStoreId(db
, max_object_store_id_key
, max_object_store_id
);
451 template <typename DBOrTransaction
>
452 WARN_UNUSED_RESULT
static leveldb::Status
GetMaxObjectStoreId(
454 const std::string
& max_object_store_id_key
,
455 int64
* max_object_store_id
) {
456 *max_object_store_id
= -1;
459 GetInt(db
, max_object_store_id_key
, max_object_store_id
, &found
);
463 *max_object_store_id
= 0;
465 DCHECK_GE(*max_object_store_id
, 0);
469 class DefaultLevelDBFactory
: public LevelDBFactory
{
471 virtual leveldb::Status
OpenLevelDB(const base::FilePath
& file_name
,
472 const LevelDBComparator
* comparator
,
473 scoped_ptr
<LevelDBDatabase
>* db
,
474 bool* is_disk_full
) OVERRIDE
{
475 return LevelDBDatabase::Open(file_name
, comparator
, db
, is_disk_full
);
477 virtual leveldb::Status
DestroyLevelDB(const base::FilePath
& file_name
)
479 return LevelDBDatabase::Destroy(file_name
);
483 // TODO(ericu): Error recovery. If we persistently can't read the
484 // blob journal, the safe thing to do is to clear it and leak the blobs,
485 // though that may be costly. Still, database/directory deletion should always
486 // clean things up, and we can write an fsck that will do a full correction if
488 template <typename T
>
489 static leveldb::Status
GetBlobJournal(const StringPiece
& leveldb_key
,
490 T
* leveldb_transaction
,
491 BlobJournalType
* journal
) {
494 leveldb::Status s
= leveldb_transaction
->Get(leveldb_key
, &data
, &found
);
496 INTERNAL_READ_ERROR_UNTESTED(READ_BLOB_JOURNAL
);
500 if (!found
|| !data
.size())
501 return leveldb::Status::OK();
502 StringPiece
slice(data
);
503 if (!DecodeBlobJournal(&slice
, journal
)) {
504 INTERNAL_READ_ERROR_UNTESTED(DECODE_BLOB_JOURNAL
);
505 s
= InternalInconsistencyStatus();
510 static void ClearBlobJournal(LevelDBTransaction
* leveldb_transaction
,
511 const std::string
& level_db_key
) {
512 leveldb_transaction
->Remove(level_db_key
);
515 static void UpdatePrimaryJournalWithBlobList(
516 LevelDBTransaction
* leveldb_transaction
,
517 const BlobJournalType
& journal
) {
518 const std::string leveldb_key
= BlobJournalKey::Encode();
520 EncodeBlobJournal(journal
, &data
);
521 leveldb_transaction
->Put(leveldb_key
, &data
);
524 static void UpdateLiveBlobJournalWithBlobList(
525 LevelDBTransaction
* leveldb_transaction
,
526 const BlobJournalType
& journal
) {
527 const std::string leveldb_key
= LiveBlobJournalKey::Encode();
529 EncodeBlobJournal(journal
, &data
);
530 leveldb_transaction
->Put(leveldb_key
, &data
);
533 static leveldb::Status
MergeBlobsIntoLiveBlobJournal(
534 LevelDBTransaction
* leveldb_transaction
,
535 const BlobJournalType
& journal
) {
536 BlobJournalType old_journal
;
537 const std::string key
= LiveBlobJournalKey::Encode();
538 leveldb::Status s
= GetBlobJournal(key
, leveldb_transaction
, &old_journal
);
542 old_journal
.insert(old_journal
.end(), journal
.begin(), journal
.end());
544 UpdateLiveBlobJournalWithBlobList(leveldb_transaction
, old_journal
);
545 return leveldb::Status::OK();
548 static void UpdateBlobJournalWithDatabase(
549 LevelDBDirectTransaction
* leveldb_transaction
,
551 BlobJournalType journal
;
553 std::make_pair(database_id
, DatabaseMetaDataKey::kAllBlobsKey
));
554 const std::string key
= BlobJournalKey::Encode();
556 EncodeBlobJournal(journal
, &data
);
557 leveldb_transaction
->Put(key
, &data
);
560 static leveldb::Status
MergeDatabaseIntoLiveBlobJournal(
561 LevelDBDirectTransaction
* leveldb_transaction
,
563 BlobJournalType journal
;
564 const std::string key
= LiveBlobJournalKey::Encode();
565 leveldb::Status s
= GetBlobJournal(key
, leveldb_transaction
, &journal
);
569 std::make_pair(database_id
, DatabaseMetaDataKey::kAllBlobsKey
));
571 EncodeBlobJournal(journal
, &data
);
572 leveldb_transaction
->Put(key
, &data
);
573 return leveldb::Status::OK();
576 IndexedDBBackingStore::IndexedDBBackingStore(
577 IndexedDBFactory
* indexed_db_factory
,
578 const GURL
& origin_url
,
579 const base::FilePath
& blob_path
,
580 net::URLRequestContext
* request_context
,
581 scoped_ptr
<LevelDBDatabase
> db
,
582 scoped_ptr
<LevelDBComparator
> comparator
,
583 base::TaskRunner
* task_runner
)
584 : indexed_db_factory_(indexed_db_factory
),
585 origin_url_(origin_url
),
586 blob_path_(blob_path
),
587 origin_identifier_(ComputeOriginIdentifier(origin_url
)),
588 request_context_(request_context
),
589 task_runner_(task_runner
),
591 comparator_(comparator
.Pass()),
592 active_blob_registry_(this) {}
594 IndexedDBBackingStore::~IndexedDBBackingStore() {
595 if (!blob_path_
.empty() && !child_process_ids_granted_
.empty()) {
596 ChildProcessSecurityPolicyImpl
* policy
=
597 ChildProcessSecurityPolicyImpl::GetInstance();
598 std::set
<int>::const_iterator iter
;
599 for (iter
= child_process_ids_granted_
.begin();
600 iter
!= child_process_ids_granted_
.end();
602 policy
->RevokeAllPermissionsForFile(*iter
, blob_path_
);
605 // db_'s destructor uses comparator_. The order of destruction is important.
610 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
611 const std::string
& primary_key
,
613 : primary_key_(primary_key
), version_(version
) {
614 DCHECK(!primary_key
.empty());
616 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
617 : primary_key_(), version_(-1) {}
618 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
620 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
621 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
623 enum IndexedDBBackingStoreOpenResult
{
624 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS
,
625 INDEXED_DB_BACKING_STORE_OPEN_SUCCESS
,
626 INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY
,
627 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA
,
628 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED
,
629 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED
,
630 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS
,
631 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA
,
632 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR
,
633 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED
,
634 INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII
,
635 INDEXED_DB_BACKING_STORE_OPEN_DISK_FULL_DEPRECATED
,
636 INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG
,
637 INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY
,
638 INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION
,
639 INDEXED_DB_BACKING_STORE_OPEN_MAX
,
643 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::Open(
644 IndexedDBFactory
* indexed_db_factory
,
645 const GURL
& origin_url
,
646 const base::FilePath
& path_base
,
647 net::URLRequestContext
* request_context
,
648 blink::WebIDBDataLoss
* data_loss
,
649 std::string
* data_loss_message
,
651 base::TaskRunner
* task_runner
,
652 bool clean_journal
) {
653 *data_loss
= blink::WebIDBDataLossNone
;
654 DefaultLevelDBFactory leveldb_factory
;
655 return IndexedDBBackingStore::Open(indexed_db_factory
,
667 static std::string
OriginToCustomHistogramSuffix(const GURL
& origin_url
) {
668 if (origin_url
.host() == "docs.google.com")
670 return std::string();
673 static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result
,
674 const GURL
& origin_url
) {
675 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.BackingStore.OpenStatus",
677 INDEXED_DB_BACKING_STORE_OPEN_MAX
);
678 const std::string suffix
= OriginToCustomHistogramSuffix(origin_url
);
679 // Data from the WebCore.IndexedDB.BackingStore.OpenStatus histogram is used
680 // to generate a graph. So as not to alter the meaning of that graph,
681 // continue to collect all stats there (above) but also now collect docs stats
682 // separately (below).
683 if (!suffix
.empty()) {
684 base::LinearHistogram::FactoryGet(
685 "WebCore.IndexedDB.BackingStore.OpenStatus" + suffix
,
687 INDEXED_DB_BACKING_STORE_OPEN_MAX
,
688 INDEXED_DB_BACKING_STORE_OPEN_MAX
+ 1,
689 base::HistogramBase::kUmaTargetedHistogramFlag
)->Add(result
);
693 static bool IsPathTooLong(const base::FilePath
& leveldb_dir
) {
694 int limit
= base::GetMaximumPathComponentLength(leveldb_dir
.DirName());
696 DLOG(WARNING
) << "GetMaximumPathComponentLength returned -1";
697 // In limited testing, ChromeOS returns 143, other OSes 255.
698 #if defined(OS_CHROMEOS)
704 size_t component_length
= leveldb_dir
.BaseName().value().length();
705 if (component_length
> static_cast<uint32_t>(limit
)) {
706 DLOG(WARNING
) << "Path component length (" << component_length
707 << ") exceeds maximum (" << limit
708 << ") allowed by this filesystem.";
711 const int num_buckets
= 12;
712 UMA_HISTOGRAM_CUSTOM_COUNTS(
713 "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
723 leveldb::Status
IndexedDBBackingStore::DestroyBackingStore(
724 const base::FilePath
& path_base
,
725 const GURL
& origin_url
) {
726 const base::FilePath file_path
=
727 path_base
.Append(ComputeFileName(origin_url
));
728 DefaultLevelDBFactory leveldb_factory
;
729 return leveldb_factory
.DestroyLevelDB(file_path
);
732 bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath
& path_base
,
733 const GURL
& origin_url
,
734 std::string
& message
) {
736 const base::FilePath info_path
=
737 path_base
.Append(ComputeCorruptionFileName(origin_url
));
739 if (IsPathTooLong(info_path
))
742 const int64 max_json_len
= 4096;
744 if (!GetFileSize(info_path
, &file_size
) || file_size
> max_json_len
)
751 base::File
file(info_path
, base::File::FLAG_OPEN
| base::File::FLAG_READ
);
752 bool success
= false;
753 if (file
.IsValid()) {
754 std::vector
<char> bytes(file_size
);
755 if (file_size
== file
.Read(0, &bytes
[0], file_size
)) {
756 std::string
input_js(&bytes
[0], file_size
);
757 base::JSONReader reader
;
758 scoped_ptr
<base::Value
> val(reader
.ReadToValue(input_js
));
759 if (val
&& val
->GetType() == base::Value::TYPE_DICTIONARY
) {
760 base::DictionaryValue
* dict_val
=
761 static_cast<base::DictionaryValue
*>(val
.get());
762 success
= dict_val
->GetString("message", &message
);
768 base::DeleteFile(info_path
, false);
773 bool IndexedDBBackingStore::RecordCorruptionInfo(
774 const base::FilePath
& path_base
,
775 const GURL
& origin_url
,
776 const std::string
& message
) {
777 const base::FilePath info_path
=
778 path_base
.Append(ComputeCorruptionFileName(origin_url
));
779 if (IsPathTooLong(info_path
))
782 base::DictionaryValue root_dict
;
783 root_dict
.SetString("message", message
);
784 std::string output_js
;
785 base::JSONWriter::Write(&root_dict
, &output_js
);
787 base::File
file(info_path
,
788 base::File::FLAG_CREATE_ALWAYS
| base::File::FLAG_WRITE
);
791 int written
= file
.Write(0, output_js
.c_str(), output_js
.length());
792 return size_t(written
) == output_js
.length();
796 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::Open(
797 IndexedDBFactory
* indexed_db_factory
,
798 const GURL
& origin_url
,
799 const base::FilePath
& path_base
,
800 net::URLRequestContext
* request_context
,
801 blink::WebIDBDataLoss
* data_loss
,
802 std::string
* data_loss_message
,
804 LevelDBFactory
* leveldb_factory
,
805 base::TaskRunner
* task_runner
,
806 bool clean_journal
) {
807 IDB_TRACE("IndexedDBBackingStore::Open");
808 DCHECK(!path_base
.empty());
809 *data_loss
= blink::WebIDBDataLossNone
;
810 *data_loss_message
= "";
811 *is_disk_full
= false;
813 scoped_ptr
<LevelDBComparator
> comparator(new Comparator());
815 if (!IsStringASCII(path_base
.AsUTF8Unsafe())) {
816 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII
,
819 if (!base::CreateDirectory(path_base
)) {
820 LOG(ERROR
) << "Unable to create IndexedDB database path "
821 << path_base
.AsUTF8Unsafe();
822 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY
,
824 return scoped_refptr
<IndexedDBBackingStore
>();
827 const base::FilePath file_path
=
828 path_base
.Append(ComputeFileName(origin_url
));
829 const base::FilePath blob_path
=
830 path_base
.Append(ComputeBlobPath(origin_url
));
832 if (IsPathTooLong(file_path
)) {
833 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG
,
835 return scoped_refptr
<IndexedDBBackingStore
>();
838 scoped_ptr
<LevelDBDatabase
> db
;
839 leveldb::Status status
= leveldb_factory
->OpenLevelDB(
840 file_path
, comparator
.get(), &db
, is_disk_full
);
842 DCHECK(!db
== !status
.ok());
844 if (leveldb_env::IndicatesDiskFull(status
)) {
845 *is_disk_full
= true;
846 } else if (leveldb_env::IsCorruption(status
)) {
847 *data_loss
= blink::WebIDBDataLossTotal
;
848 *data_loss_message
= leveldb_env::GetCorruptionMessage(status
);
852 bool is_schema_known
= false;
854 std::string corruption_message
;
855 if (ReadCorruptionInfo(path_base
, origin_url
, corruption_message
)) {
856 LOG(ERROR
) << "IndexedDB recovering from a corrupted (and deleted) "
858 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION
,
861 *data_loss
= blink::WebIDBDataLossTotal
;
863 "IndexedDB (database was corrupt): " + corruption_message
;
864 } else if (!IsSchemaKnown(db
.get(), &is_schema_known
)) {
865 LOG(ERROR
) << "IndexedDB had IO error checking schema, treating it as "
868 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA
,
871 *data_loss
= blink::WebIDBDataLossTotal
;
872 *data_loss_message
= "I/O error checking schema";
873 } else if (!is_schema_known
) {
874 LOG(ERROR
) << "IndexedDB backing store had unknown schema, treating it "
875 "as failure to open";
876 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA
,
879 *data_loss
= blink::WebIDBDataLossTotal
;
880 *data_loss_message
= "Unknown schema";
884 DCHECK(status
.ok() || !is_schema_known
|| leveldb_env::IsIOError(status
) ||
885 leveldb_env::IsCorruption(status
));
888 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_SUCCESS
, origin_url
);
889 } else if (leveldb_env::IsIOError(status
)) {
890 LOG(ERROR
) << "Unable to open backing store, not trying to recover - "
891 << status
.ToString();
892 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY
, origin_url
);
893 return scoped_refptr
<IndexedDBBackingStore
>();
895 DCHECK(!is_schema_known
|| leveldb_env::IsCorruption(status
));
896 LOG(ERROR
) << "IndexedDB backing store open failed, attempting cleanup";
897 status
= leveldb_factory
->DestroyLevelDB(file_path
);
899 LOG(ERROR
) << "IndexedDB backing store cleanup failed";
900 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED
,
902 return scoped_refptr
<IndexedDBBackingStore
>();
905 LOG(ERROR
) << "IndexedDB backing store cleanup succeeded, reopening";
906 leveldb_factory
->OpenLevelDB(file_path
, comparator
.get(), &db
, NULL
);
908 LOG(ERROR
) << "IndexedDB backing store reopen after recovery failed";
909 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED
,
911 return scoped_refptr
<IndexedDBBackingStore
>();
913 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS
,
919 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR
,
921 return scoped_refptr
<IndexedDBBackingStore
>();
924 return Create(indexed_db_factory
,
934 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::OpenInMemory(
935 const GURL
& origin_url
,
936 base::TaskRunner
* task_runner
) {
937 DefaultLevelDBFactory leveldb_factory
;
938 return IndexedDBBackingStore::OpenInMemory(
939 origin_url
, &leveldb_factory
, task_runner
);
943 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::OpenInMemory(
944 const GURL
& origin_url
,
945 LevelDBFactory
* leveldb_factory
,
946 base::TaskRunner
* task_runner
) {
947 IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
949 scoped_ptr
<LevelDBComparator
> comparator(new Comparator());
950 scoped_ptr
<LevelDBDatabase
> db
=
951 LevelDBDatabase::OpenInMemory(comparator
.get());
953 LOG(ERROR
) << "LevelDBDatabase::OpenInMemory failed.";
954 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED
,
956 return scoped_refptr
<IndexedDBBackingStore
>();
958 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS
, origin_url
);
960 return Create(NULL
/* indexed_db_factory */,
963 NULL
/* request_context */,
970 scoped_refptr
<IndexedDBBackingStore
> IndexedDBBackingStore::Create(
971 IndexedDBFactory
* indexed_db_factory
,
972 const GURL
& origin_url
,
973 const base::FilePath
& blob_path
,
974 net::URLRequestContext
* request_context
,
975 scoped_ptr
<LevelDBDatabase
> db
,
976 scoped_ptr
<LevelDBComparator
> comparator
,
977 base::TaskRunner
* task_runner
) {
978 // TODO(jsbell): Handle comparator name changes.
979 scoped_refptr
<IndexedDBBackingStore
> backing_store(
980 new IndexedDBBackingStore(indexed_db_factory
,
987 if (!SetUpMetadata(backing_store
->db_
.get(),
988 backing_store
->origin_identifier_
))
989 return scoped_refptr
<IndexedDBBackingStore
>();
991 return backing_store
;
994 void IndexedDBBackingStore::GrantChildProcessPermissions(int child_process_id
) {
995 if (!child_process_ids_granted_
.count(child_process_id
)) {
996 child_process_ids_granted_
.insert(child_process_id
);
997 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
998 child_process_id
, blob_path_
);
1002 std::vector
<base::string16
> IndexedDBBackingStore::GetDatabaseNames(
1003 leveldb::Status
* s
) {
1004 *s
= leveldb::Status::OK();
1005 std::vector
<base::string16
> found_names
;
1006 const std::string start_key
=
1007 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_
);
1008 const std::string stop_key
=
1009 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_
);
1011 DCHECK(found_names
.empty());
1013 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
1014 for (*s
= it
->Seek(start_key
);
1015 s
->ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
1017 StringPiece
slice(it
->Key());
1018 DatabaseNameKey database_name_key
;
1019 if (!DatabaseNameKey::Decode(&slice
, &database_name_key
)) {
1020 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES
);
1023 found_names
.push_back(database_name_key
.database_name());
1027 INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES
);
1032 leveldb::Status
IndexedDBBackingStore::GetIDBDatabaseMetaData(
1033 const base::string16
& name
,
1034 IndexedDBDatabaseMetadata
* metadata
,
1036 const std::string key
= DatabaseNameKey::Encode(origin_identifier_
, name
);
1039 leveldb::Status s
= GetInt(db_
.get(), key
, &metadata
->id
, found
);
1041 INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA
);
1045 return leveldb::Status::OK();
1047 s
= GetString(db_
.get(),
1048 DatabaseMetaDataKey::Encode(metadata
->id
,
1049 DatabaseMetaDataKey::USER_VERSION
),
1053 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1057 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1058 return InternalInconsistencyStatus();
1061 s
= GetVarInt(db_
.get(),
1062 DatabaseMetaDataKey::Encode(
1063 metadata
->id
, DatabaseMetaDataKey::USER_INT_VERSION
),
1064 &metadata
->int_version
,
1067 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1071 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1072 return InternalInconsistencyStatus();
1075 if (metadata
->int_version
== IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
)
1076 metadata
->int_version
= IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1078 s
= GetMaxObjectStoreId(
1079 db_
.get(), metadata
->id
, &metadata
->max_object_store_id
);
1081 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA
);
1087 WARN_UNUSED_RESULT
static leveldb::Status
GetNewDatabaseId(
1088 LevelDBTransaction
* transaction
,
1091 int64 max_database_id
= -1;
1094 GetInt(transaction
, MaxDatabaseIdKey::Encode(), &max_database_id
, &found
);
1096 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID
);
1100 max_database_id
= 0;
1102 DCHECK_GE(max_database_id
, 0);
1104 int64 database_id
= max_database_id
+ 1;
1105 PutInt(transaction
, MaxDatabaseIdKey::Encode(), database_id
);
1106 *new_id
= database_id
;
1107 return leveldb::Status::OK();
1110 leveldb::Status
IndexedDBBackingStore::CreateIDBDatabaseMetaData(
1111 const base::string16
& name
,
1112 const base::string16
& version
,
1115 scoped_refptr
<LevelDBTransaction
> transaction
=
1116 new LevelDBTransaction(db_
.get());
1118 leveldb::Status s
= GetNewDatabaseId(transaction
.get(), row_id
);
1121 DCHECK_GE(*row_id
, 0);
1123 if (int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
)
1124 int_version
= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
;
1126 PutInt(transaction
.get(),
1127 DatabaseNameKey::Encode(origin_identifier_
, name
),
1131 DatabaseMetaDataKey::Encode(*row_id
, DatabaseMetaDataKey::USER_VERSION
),
1133 PutVarInt(transaction
.get(),
1134 DatabaseMetaDataKey::Encode(*row_id
,
1135 DatabaseMetaDataKey::USER_INT_VERSION
),
1137 s
= transaction
->Commit();
1139 INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA
);
1143 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
1144 IndexedDBBackingStore::Transaction
* transaction
,
1146 int64 int_version
) {
1147 if (int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
)
1148 int_version
= IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
;
1149 DCHECK_GE(int_version
, 0) << "int_version was " << int_version
;
1150 PutVarInt(transaction
->transaction(),
1151 DatabaseMetaDataKey::Encode(row_id
,
1152 DatabaseMetaDataKey::USER_INT_VERSION
),
1157 static leveldb::Status
DeleteRange(LevelDBTransaction
* transaction
,
1158 const std::string
& begin
,
1159 const std::string
& end
) {
1160 scoped_ptr
<LevelDBIterator
> it
= transaction
->CreateIterator();
1162 for (s
= it
->Seek(begin
);
1163 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), end
) < 0;
1165 transaction
->Remove(it
->Key());
1169 leveldb::Status
IndexedDBBackingStore::DeleteDatabase(
1170 const base::string16
& name
) {
1171 IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
1172 scoped_ptr
<LevelDBDirectTransaction
> transaction
=
1173 LevelDBDirectTransaction::Create(db_
.get());
1175 IndexedDBDatabaseMetadata metadata
;
1176 bool success
= false;
1177 leveldb::Status s
= GetIDBDatabaseMetaData(name
, &metadata
, &success
);
1181 return leveldb::Status::OK();
1183 const std::string start_key
= DatabaseMetaDataKey::Encode(
1184 metadata
.id
, DatabaseMetaDataKey::ORIGIN_NAME
);
1185 const std::string stop_key
= DatabaseMetaDataKey::Encode(
1186 metadata
.id
+ 1, DatabaseMetaDataKey::ORIGIN_NAME
);
1187 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
1188 for (s
= it
->Seek(start_key
);
1189 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
1191 transaction
->Remove(it
->Key());
1193 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE
);
1197 const std::string key
= DatabaseNameKey::Encode(origin_identifier_
, name
);
1198 transaction
->Remove(key
);
1200 // TODO(ericu): Put the real calls to the blob journal code here. For now,
1201 // I've inserted fake calls so that we don't get "you didn't use this static
1202 // function" compiler errors.
1204 scoped_refptr
<LevelDBTransaction
> fake_transaction
=
1205 new LevelDBTransaction(NULL
);
1206 BlobJournalType fake_journal
;
1207 MergeDatabaseIntoLiveBlobJournal(transaction
.get(), metadata
.id
);
1208 UpdateBlobJournalWithDatabase(transaction
.get(), metadata
.id
);
1209 MergeBlobsIntoLiveBlobJournal(fake_transaction
.get(), fake_journal
);
1212 s
= transaction
->Commit();
1214 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE
);
1217 db_
->Compact(start_key
, stop_key
);
1221 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator
* it
,
1222 const std::string
& stop_key
,
1223 int64 object_store_id
,
1224 int64 meta_data_type
) {
1225 if (!it
->IsValid() || CompareKeys(it
->Key(), stop_key
) >= 0)
1228 StringPiece
slice(it
->Key());
1229 ObjectStoreMetaDataKey meta_data_key
;
1230 bool ok
= ObjectStoreMetaDataKey::Decode(&slice
, &meta_data_key
);
1232 if (meta_data_key
.ObjectStoreId() != object_store_id
)
1234 if (meta_data_key
.MetaDataType() != meta_data_type
)
1239 // TODO(jsbell): This should do some error handling rather than
1240 // plowing ahead when bad data is encountered.
1241 leveldb::Status
IndexedDBBackingStore::GetObjectStores(
1243 IndexedDBDatabaseMetadata::ObjectStoreMap
* object_stores
) {
1244 IDB_TRACE("IndexedDBBackingStore::GetObjectStores");
1245 if (!KeyPrefix::IsValidDatabaseId(database_id
))
1246 return InvalidDBKeyStatus();
1247 const std::string start_key
=
1248 ObjectStoreMetaDataKey::Encode(database_id
, 1, 0);
1249 const std::string stop_key
=
1250 ObjectStoreMetaDataKey::EncodeMaxKey(database_id
);
1252 DCHECK(object_stores
->empty());
1254 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
1255 leveldb::Status s
= it
->Seek(start_key
);
1256 while (s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0) {
1257 StringPiece
slice(it
->Key());
1258 ObjectStoreMetaDataKey meta_data_key
;
1259 bool ok
= ObjectStoreMetaDataKey::Decode(&slice
, &meta_data_key
);
1261 if (meta_data_key
.MetaDataType() != ObjectStoreMetaDataKey::NAME
) {
1262 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1263 // Possible stale metadata, but don't fail the load.
1270 int64 object_store_id
= meta_data_key
.ObjectStoreId();
1272 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
1274 base::string16 object_store_name
;
1276 StringPiece
slice(it
->Value());
1277 if (!DecodeString(&slice
, &object_store_name
) || !slice
.empty())
1278 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1284 if (!CheckObjectStoreAndMetaDataType(it
.get(),
1287 ObjectStoreMetaDataKey::KEY_PATH
)) {
1288 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1291 IndexedDBKeyPath key_path
;
1293 StringPiece
slice(it
->Value());
1294 if (!DecodeIDBKeyPath(&slice
, &key_path
) || !slice
.empty())
1295 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1301 if (!CheckObjectStoreAndMetaDataType(
1305 ObjectStoreMetaDataKey::AUTO_INCREMENT
)) {
1306 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1309 bool auto_increment
;
1311 StringPiece
slice(it
->Value());
1312 if (!DecodeBool(&slice
, &auto_increment
) || !slice
.empty())
1313 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1316 s
= it
->Next(); // Is evictable.
1319 if (!CheckObjectStoreAndMetaDataType(it
.get(),
1322 ObjectStoreMetaDataKey::EVICTABLE
)) {
1323 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1327 s
= it
->Next(); // Last version.
1330 if (!CheckObjectStoreAndMetaDataType(
1334 ObjectStoreMetaDataKey::LAST_VERSION
)) {
1335 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1339 s
= it
->Next(); // Maximum index id allocated.
1342 if (!CheckObjectStoreAndMetaDataType(
1346 ObjectStoreMetaDataKey::MAX_INDEX_ID
)) {
1347 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1352 StringPiece
slice(it
->Value());
1353 if (!DecodeInt(&slice
, &max_index_id
) || !slice
.empty())
1354 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1357 s
= it
->Next(); // [optional] has key path (is not null)
1360 if (CheckObjectStoreAndMetaDataType(it
.get(),
1363 ObjectStoreMetaDataKey::HAS_KEY_PATH
)) {
1366 StringPiece
slice(it
->Value());
1367 if (!DecodeBool(&slice
, &has_key_path
))
1368 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1370 // This check accounts for two layers of legacy coding:
1371 // (1) Initially, has_key_path was added to distinguish null vs. string.
1372 // (2) Later, null vs. string vs. array was stored in the key_path itself.
1373 // So this check is only relevant for string-type key_paths.
1374 if (!has_key_path
&&
1375 (key_path
.type() == blink::WebIDBKeyPathTypeString
&&
1376 !key_path
.string().empty())) {
1377 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1381 key_path
= IndexedDBKeyPath();
1387 int64 key_generator_current_number
= -1;
1388 if (CheckObjectStoreAndMetaDataType(
1392 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
)) {
1393 StringPiece
slice(it
->Value());
1394 if (!DecodeInt(&slice
, &key_generator_current_number
) || !slice
.empty())
1395 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES
);
1397 // TODO(jsbell): Return key_generator_current_number, cache in
1398 // object store, and write lazily to backing store. For now,
1399 // just assert that if it was written it was valid.
1400 DCHECK_GE(key_generator_current_number
, kKeyGeneratorInitialNumber
);
1406 IndexedDBObjectStoreMetadata
metadata(object_store_name
,
1411 s
= GetIndexes(database_id
, object_store_id
, &metadata
.indexes
);
1414 (*object_stores
)[object_store_id
] = metadata
;
1418 INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES
);
1423 WARN_UNUSED_RESULT
static leveldb::Status
SetMaxObjectStoreId(
1424 LevelDBTransaction
* transaction
,
1426 int64 object_store_id
) {
1427 const std::string max_object_store_id_key
= DatabaseMetaDataKey::Encode(
1428 database_id
, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID
);
1429 int64 max_object_store_id
= -1;
1430 leveldb::Status s
= GetMaxObjectStoreId(
1431 transaction
, max_object_store_id_key
, &max_object_store_id
);
1433 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID
);
1437 if (object_store_id
<= max_object_store_id
) {
1438 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID
);
1439 return InternalInconsistencyStatus();
1441 PutInt(transaction
, max_object_store_id_key
, object_store_id
);
1445 void IndexedDBBackingStore::Compact() { db_
->CompactAll(); }
1447 leveldb::Status
IndexedDBBackingStore::CreateObjectStore(
1448 IndexedDBBackingStore::Transaction
* transaction
,
1450 int64 object_store_id
,
1451 const base::string16
& name
,
1452 const IndexedDBKeyPath
& key_path
,
1453 bool auto_increment
) {
1454 IDB_TRACE("IndexedDBBackingStore::CreateObjectStore");
1455 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1456 return InvalidDBKeyStatus();
1457 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1459 SetMaxObjectStoreId(leveldb_transaction
, database_id
, object_store_id
);
1463 const std::string name_key
= ObjectStoreMetaDataKey::Encode(
1464 database_id
, object_store_id
, ObjectStoreMetaDataKey::NAME
);
1465 const std::string key_path_key
= ObjectStoreMetaDataKey::Encode(
1466 database_id
, object_store_id
, ObjectStoreMetaDataKey::KEY_PATH
);
1467 const std::string auto_increment_key
= ObjectStoreMetaDataKey::Encode(
1468 database_id
, object_store_id
, ObjectStoreMetaDataKey::AUTO_INCREMENT
);
1469 const std::string evictable_key
= ObjectStoreMetaDataKey::Encode(
1470 database_id
, object_store_id
, ObjectStoreMetaDataKey::EVICTABLE
);
1471 const std::string last_version_key
= ObjectStoreMetaDataKey::Encode(
1472 database_id
, object_store_id
, ObjectStoreMetaDataKey::LAST_VERSION
);
1473 const std::string max_index_id_key
= ObjectStoreMetaDataKey::Encode(
1474 database_id
, object_store_id
, ObjectStoreMetaDataKey::MAX_INDEX_ID
);
1475 const std::string has_key_path_key
= ObjectStoreMetaDataKey::Encode(
1476 database_id
, object_store_id
, ObjectStoreMetaDataKey::HAS_KEY_PATH
);
1477 const std::string key_generator_current_number_key
=
1478 ObjectStoreMetaDataKey::Encode(
1481 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
1482 const std::string names_key
= ObjectStoreNamesKey::Encode(database_id
, name
);
1484 PutString(leveldb_transaction
, name_key
, name
);
1485 PutIDBKeyPath(leveldb_transaction
, key_path_key
, key_path
);
1486 PutInt(leveldb_transaction
, auto_increment_key
, auto_increment
);
1487 PutInt(leveldb_transaction
, evictable_key
, false);
1488 PutInt(leveldb_transaction
, last_version_key
, 1);
1489 PutInt(leveldb_transaction
, max_index_id_key
, kMinimumIndexId
);
1490 PutBool(leveldb_transaction
, has_key_path_key
, !key_path
.IsNull());
1491 PutInt(leveldb_transaction
,
1492 key_generator_current_number_key
,
1493 kKeyGeneratorInitialNumber
);
1494 PutInt(leveldb_transaction
, names_key
, object_store_id
);
1498 leveldb::Status
IndexedDBBackingStore::DeleteObjectStore(
1499 IndexedDBBackingStore::Transaction
* transaction
,
1501 int64 object_store_id
) {
1502 IDB_TRACE("IndexedDBBackingStore::DeleteObjectStore");
1503 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1504 return InvalidDBKeyStatus();
1505 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1507 base::string16 object_store_name
;
1510 GetString(leveldb_transaction
,
1511 ObjectStoreMetaDataKey::Encode(
1512 database_id
, object_store_id
, ObjectStoreMetaDataKey::NAME
),
1516 INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1520 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1521 return InternalInconsistencyStatus();
1525 leveldb_transaction
,
1526 ObjectStoreMetaDataKey::Encode(database_id
, object_store_id
, 0),
1527 ObjectStoreMetaDataKey::EncodeMaxKey(database_id
, object_store_id
));
1530 leveldb_transaction
->Remove(
1531 ObjectStoreNamesKey::Encode(database_id
, object_store_name
));
1534 leveldb_transaction
,
1535 IndexFreeListKey::Encode(database_id
, object_store_id
, 0),
1536 IndexFreeListKey::EncodeMaxKey(database_id
, object_store_id
));
1541 leveldb_transaction
,
1542 IndexMetaDataKey::Encode(database_id
, object_store_id
, 0, 0),
1543 IndexMetaDataKey::EncodeMaxKey(database_id
, object_store_id
));
1547 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE
);
1551 return ClearObjectStore(transaction
, database_id
, object_store_id
);
1554 leveldb::Status
IndexedDBBackingStore::GetRecord(
1555 IndexedDBBackingStore::Transaction
* transaction
,
1557 int64 object_store_id
,
1558 const IndexedDBKey
& key
,
1559 IndexedDBValue
* record
) {
1560 IDB_TRACE("IndexedDBBackingStore::GetRecord");
1561 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1562 return InvalidDBKeyStatus();
1563 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1565 const std::string leveldb_key
=
1566 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
1572 leveldb::Status s
= leveldb_transaction
->Get(leveldb_key
, &data
, &found
);
1574 INTERNAL_READ_ERROR(GET_RECORD
);
1580 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD
);
1581 return leveldb::Status::NotFound("Record contained no data");
1585 StringPiece
slice(data
);
1586 if (!DecodeVarInt(&slice
, &version
)) {
1587 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD
);
1588 return InternalInconsistencyStatus();
1591 record
->bits
= slice
.as_string();
1595 WARN_UNUSED_RESULT
static leveldb::Status
GetNewVersionNumber(
1596 LevelDBTransaction
* transaction
,
1598 int64 object_store_id
,
1599 int64
* new_version_number
) {
1600 const std::string last_version_key
= ObjectStoreMetaDataKey::Encode(
1601 database_id
, object_store_id
, ObjectStoreMetaDataKey::LAST_VERSION
);
1603 *new_version_number
= -1;
1604 int64 last_version
= -1;
1607 GetInt(transaction
, last_version_key
, &last_version
, &found
);
1609 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER
);
1615 DCHECK_GE(last_version
, 0);
1617 int64 version
= last_version
+ 1;
1618 PutInt(transaction
, last_version_key
, version
);
1620 // TODO(jsbell): Think about how we want to handle the overflow scenario.
1621 DCHECK(version
> last_version
);
1623 *new_version_number
= version
;
1627 leveldb::Status
IndexedDBBackingStore::PutRecord(
1628 IndexedDBBackingStore::Transaction
* transaction
,
1630 int64 object_store_id
,
1631 const IndexedDBKey
& key
,
1632 IndexedDBValue
& value
,
1633 ScopedVector
<webkit_blob::BlobDataHandle
>* handles
,
1634 RecordIdentifier
* record_identifier
) {
1635 IDB_TRACE("IndexedDBBackingStore::PutRecord");
1636 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1637 return InvalidDBKeyStatus();
1638 DCHECK(key
.IsValid());
1640 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1642 leveldb::Status s
= GetNewVersionNumber(
1643 leveldb_transaction
, database_id
, object_store_id
, &version
);
1646 DCHECK_GE(version
, 0);
1647 const std::string object_store_data_key
=
1648 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
1651 EncodeVarInt(version
, &v
);
1652 v
.append(value
.bits
);
1654 leveldb_transaction
->Put(object_store_data_key
, &v
);
1655 transaction
->PutBlobInfo(database_id
,
1657 object_store_data_key
,
1660 DCHECK(!handles
->size());
1662 const std::string exists_entry_key
=
1663 ExistsEntryKey::Encode(database_id
, object_store_id
, key
);
1664 std::string version_encoded
;
1665 EncodeInt(version
, &version_encoded
);
1666 leveldb_transaction
->Put(exists_entry_key
, &version_encoded
);
1668 std::string key_encoded
;
1669 EncodeIDBKey(key
, &key_encoded
);
1670 record_identifier
->Reset(key_encoded
, version
);
1674 leveldb::Status
IndexedDBBackingStore::ClearObjectStore(
1675 IndexedDBBackingStore::Transaction
* transaction
,
1677 int64 object_store_id
) {
1678 IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
1679 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1680 return InvalidDBKeyStatus();
1681 const std::string start_key
=
1682 KeyPrefix(database_id
, object_store_id
).Encode();
1683 const std::string stop_key
=
1684 KeyPrefix(database_id
, object_store_id
+ 1).Encode();
1687 DeleteRange(transaction
->transaction(), start_key
, stop_key
);
1689 INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE
);
1693 leveldb::Status
IndexedDBBackingStore::DeleteRecord(
1694 IndexedDBBackingStore::Transaction
* transaction
,
1696 int64 object_store_id
,
1697 const RecordIdentifier
& record_identifier
) {
1698 IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
1699 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1700 return InvalidDBKeyStatus();
1701 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1703 const std::string object_store_data_key
= ObjectStoreDataKey::Encode(
1704 database_id
, object_store_id
, record_identifier
.primary_key());
1705 leveldb_transaction
->Remove(object_store_data_key
);
1706 transaction
->PutBlobInfo(
1707 database_id
, object_store_id
, object_store_data_key
, NULL
, NULL
);
1709 const std::string exists_entry_key
= ExistsEntryKey::Encode(
1710 database_id
, object_store_id
, record_identifier
.primary_key());
1711 leveldb_transaction
->Remove(exists_entry_key
);
1712 return leveldb::Status::OK();
1715 leveldb::Status
IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
1716 IndexedDBBackingStore::Transaction
* transaction
,
1718 int64 object_store_id
,
1719 int64
* key_generator_current_number
) {
1720 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1721 return InvalidDBKeyStatus();
1722 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
1724 const std::string key_generator_current_number_key
=
1725 ObjectStoreMetaDataKey::Encode(
1728 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
1730 *key_generator_current_number
= -1;
1735 leveldb_transaction
->Get(key_generator_current_number_key
, &data
, &found
);
1737 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
1740 if (found
&& !data
.empty()) {
1741 StringPiece
slice(data
);
1742 if (!DecodeInt(&slice
, key_generator_current_number
) || !slice
.empty()) {
1743 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
1744 return InternalInconsistencyStatus();
1749 // Previously, the key generator state was not stored explicitly
1750 // but derived from the maximum numeric key present in existing
1751 // data. This violates the spec as the data may be cleared but the
1752 // key generator state must be preserved.
1753 // TODO(jsbell): Fix this for all stores on database open?
1754 const std::string start_key
=
1755 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MinIDBKey());
1756 const std::string stop_key
=
1757 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MaxIDBKey());
1759 scoped_ptr
<LevelDBIterator
> it
= leveldb_transaction
->CreateIterator();
1760 int64 max_numeric_key
= 0;
1762 for (s
= it
->Seek(start_key
);
1763 s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0;
1765 StringPiece
slice(it
->Key());
1766 ObjectStoreDataKey data_key
;
1767 if (!ObjectStoreDataKey::Decode(&slice
, &data_key
)) {
1768 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
1769 return InternalInconsistencyStatus();
1771 scoped_ptr
<IndexedDBKey
> user_key
= data_key
.user_key();
1772 if (user_key
->type() == blink::WebIDBKeyTypeNumber
) {
1773 int64 n
= static_cast<int64
>(user_key
->number());
1774 if (n
> max_numeric_key
)
1775 max_numeric_key
= n
;
1780 *key_generator_current_number
= max_numeric_key
+ 1;
1782 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER
);
1787 leveldb::Status
IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
1788 IndexedDBBackingStore::Transaction
* transaction
,
1790 int64 object_store_id
,
1792 bool check_current
) {
1793 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1794 return InvalidDBKeyStatus();
1796 if (check_current
) {
1797 int64 current_number
;
1798 leveldb::Status s
= GetKeyGeneratorCurrentNumber(
1799 transaction
, database_id
, object_store_id
, ¤t_number
);
1802 if (new_number
<= current_number
)
1806 const std::string key_generator_current_number_key
=
1807 ObjectStoreMetaDataKey::Encode(
1810 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER
);
1812 transaction
->transaction(), key_generator_current_number_key
, new_number
);
1813 return leveldb::Status::OK();
1816 leveldb::Status
IndexedDBBackingStore::KeyExistsInObjectStore(
1817 IndexedDBBackingStore::Transaction
* transaction
,
1819 int64 object_store_id
,
1820 const IndexedDBKey
& key
,
1821 RecordIdentifier
* found_record_identifier
,
1823 IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
1824 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
1825 return InvalidDBKeyStatus();
1827 const std::string leveldb_key
=
1828 ObjectStoreDataKey::Encode(database_id
, object_store_id
, key
);
1832 transaction
->transaction()->Get(leveldb_key
, &data
, found
);
1834 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE
);
1838 return leveldb::Status::OK();
1840 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE
);
1841 return InternalInconsistencyStatus();
1845 StringPiece
slice(data
);
1846 if (!DecodeVarInt(&slice
, &version
))
1847 return InternalInconsistencyStatus();
1849 std::string encoded_key
;
1850 EncodeIDBKey(key
, &encoded_key
);
1851 found_record_identifier
->Reset(encoded_key
, version
);
1855 class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
1856 : public IndexedDBBackingStore::Transaction::ChainedBlobWriter
{
1858 typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
1860 ChainedBlobWriterImpl(
1862 IndexedDBBackingStore
* backingStore
,
1863 WriteDescriptorVec
& blobs
,
1864 scoped_refptr
<IndexedDBBackingStore::BlobWriteCallback
> callback
)
1865 : waiting_for_callback_(false),
1866 database_id_(database_id
),
1867 backing_store_(backingStore
),
1868 callback_(callback
),
1871 iter_
= blobs_
.begin();
1875 virtual void set_delegate(scoped_ptr
<FileWriterDelegate
> delegate
) OVERRIDE
{
1876 delegate_
.reset(delegate
.release());
1879 virtual void ReportWriteCompletion(bool succeeded
,
1880 int64 bytes_written
) OVERRIDE
{
1881 // TODO(ericu): Check bytes_written against the blob's snapshot value.
1882 DCHECK(waiting_for_callback_
);
1883 DCHECK(!succeeded
|| bytes_written
>= 0);
1884 waiting_for_callback_
= false;
1885 if (delegate_
.get()) // Only present for Blob, not File.
1886 content::BrowserThread::DeleteSoon(
1887 content::BrowserThread::IO
, FROM_HERE
, delegate_
.release());
1895 callback_
->Run(false);
1898 virtual void Abort() OVERRIDE
{
1899 if (!waiting_for_callback_
)
1906 virtual ~ChainedBlobWriterImpl() {}
1908 void WriteNextFile() {
1909 DCHECK(!waiting_for_callback_
);
1911 if (iter_
== blobs_
.end()) {
1913 callback_
->Run(true);
1916 if (!backing_store_
->WriteBlobFile(database_id_
, *iter_
, this)) {
1917 callback_
->Run(false);
1920 waiting_for_callback_
= true;
1925 bool waiting_for_callback_
;
1926 scoped_refptr
<ChainedBlobWriterImpl
> self_ref_
;
1927 WriteDescriptorVec blobs_
;
1928 WriteDescriptorVec::const_iterator iter_
;
1930 IndexedDBBackingStore
* backing_store_
;
1931 scoped_refptr
<IndexedDBBackingStore::BlobWriteCallback
> callback_
;
1932 scoped_ptr
<FileWriterDelegate
> delegate_
;
1936 class LocalWriteClosure
: public FileWriterDelegate::DelegateWriteCallback
,
1937 public base::RefCounted
<LocalWriteClosure
> {
1939 LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter
*
1940 chained_blob_writer_
,
1941 base::TaskRunner
* task_runner
)
1942 : chained_blob_writer_(chained_blob_writer_
),
1943 task_runner_(task_runner
),
1944 bytes_written_(-1) {}
1946 void Run(base::File::Error rv
,
1948 FileWriterDelegate::WriteProgressStatus write_status
) {
1949 if (write_status
== FileWriterDelegate::SUCCESS_IO_PENDING
)
1950 return; // We don't care about progress events.
1951 if (rv
== base::File::FILE_OK
) {
1953 DCHECK(write_status
== FileWriterDelegate::SUCCESS_COMPLETED
);
1954 bytes_written_
= bytes
;
1956 DCHECK(write_status
== FileWriterDelegate::ERROR_WRITE_STARTED
||
1957 write_status
== FileWriterDelegate::ERROR_WRITE_NOT_STARTED
);
1959 task_runner_
->PostTask(
1961 base::Bind(&LocalWriteClosure::callBlobCallbackOnIDBTaskRunner
,
1963 write_status
== FileWriterDelegate::SUCCESS_COMPLETED
));
1966 void writeBlobToFileOnIOThread(const FilePath
& file_path
,
1967 const GURL
& blob_url
,
1968 net::URLRequestContext
* request_context
) {
1969 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO
));
1970 scoped_ptr
<fileapi::FileStreamWriter
> writer(
1971 fileapi::FileStreamWriter::CreateForLocalFile(
1972 task_runner_
, file_path
, 0,
1973 fileapi::FileStreamWriter::CREATE_NEW_FILE
));
1974 scoped_ptr
<FileWriterDelegate
> delegate(
1975 new FileWriterDelegate(writer
.Pass()));
1977 DCHECK(blob_url
.is_valid());
1978 scoped_ptr
<net::URLRequest
> blob_request(request_context
->CreateRequest(
1979 blob_url
, net::DEFAULT_PRIORITY
, delegate
.get(), NULL
));
1981 delegate
->Start(blob_request
.Pass(),
1982 base::Bind(&LocalWriteClosure::Run
, this));
1983 chained_blob_writer_
->set_delegate(delegate
.Pass());
1987 virtual ~LocalWriteClosure() {}
1988 friend class base::RefCounted
<LocalWriteClosure
>;
1990 void callBlobCallbackOnIDBTaskRunner(bool succeeded
) {
1991 DCHECK(task_runner_
->RunsTasksOnCurrentThread());
1992 chained_blob_writer_
->ReportWriteCompletion(succeeded
, bytes_written_
);
1995 IndexedDBBackingStore::Transaction::ChainedBlobWriter
* chained_blob_writer_
;
1996 base::TaskRunner
* task_runner_
;
1997 int64 bytes_written_
;
2000 bool IndexedDBBackingStore::WriteBlobFile(
2002 const Transaction::WriteDescriptor
& descriptor
,
2003 Transaction::ChainedBlobWriter
* chained_blob_writer
) {
2005 if (!MakeIDBBlobDirectory(blob_path_
, database_id
, descriptor
.key()))
2008 FilePath path
= GetBlobFileName(database_id
, descriptor
.key());
2010 if (descriptor
.is_file()) {
2011 DCHECK(!descriptor
.file_path().empty());
2012 if (!base::CopyFile(descriptor
.file_path(), path
))
2015 base::File::Info info
;
2016 if (base::GetFileInfo(descriptor
.file_path(), &info
)) {
2017 // TODO(ericu): Validate the snapshot date here. Expand WriteDescriptor
2018 // to include snapshot date and file size, and check both.
2019 if (!base::TouchFile(path
, info
.last_accessed
, info
.last_modified
)) {
2020 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2023 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2026 task_runner_
->PostTask(
2028 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion
,
2029 chained_blob_writer
,
2033 DCHECK(descriptor
.url().is_valid());
2034 scoped_refptr
<LocalWriteClosure
> write_closure(
2035 new LocalWriteClosure(chained_blob_writer
, task_runner_
));
2036 content::BrowserThread::PostTask(
2037 content::BrowserThread::IO
,
2039 base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread
,
2040 write_closure
.get(),
2048 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id
,
2050 DCHECK(KeyPrefix::IsValidDatabaseId(database_id
));
2051 bool all_blobs
= blob_key
== DatabaseMetaDataKey::kAllBlobsKey
;
2052 DCHECK(all_blobs
|| DatabaseMetaDataKey::IsValidBlobKey(blob_key
));
2053 scoped_refptr
<LevelDBTransaction
> transaction
=
2054 new LevelDBTransaction(db_
.get());
2056 std::string live_blob_key
= LiveBlobJournalKey::Encode();
2057 BlobJournalType live_blob_journal
;
2058 if (!GetBlobJournal(live_blob_key
, transaction
.get(), &live_blob_journal
)
2061 DCHECK(live_blob_journal
.size());
2063 std::string primary_key
= BlobJournalKey::Encode();
2064 BlobJournalType primary_journal
;
2065 if (!GetBlobJournal(primary_key
, transaction
.get(), &primary_journal
).ok())
2068 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2069 // remove all entries with database_id from the live_blob journal and add only
2070 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2071 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2072 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2073 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2074 // matching (database_id, blob_key) tuple, we should move it to the primary
2076 BlobJournalType new_live_blob_journal
;
2077 for (BlobJournalType::iterator journal_iter
= live_blob_journal
.begin();
2078 journal_iter
!= live_blob_journal
.end();
2080 int64 current_database_id
= journal_iter
->first
;
2081 int64 current_blob_key
= journal_iter
->second
;
2082 bool current_all_blobs
=
2083 current_blob_key
== DatabaseMetaDataKey::kAllBlobsKey
;
2084 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id
) ||
2086 if (current_database_id
== database_id
&&
2087 (all_blobs
|| current_all_blobs
|| blob_key
== current_blob_key
)) {
2089 primary_journal
.push_back(
2090 std::make_pair(database_id
, current_blob_key
));
2091 if (current_all_blobs
)
2092 new_live_blob_journal
.push_back(*journal_iter
);
2093 new_live_blob_journal
.insert(new_live_blob_journal
.end(),
2095 live_blob_journal
.end()); // All the rest.
2099 new_live_blob_journal
.push_back(*journal_iter
);
2103 primary_journal
.push_back(
2104 std::make_pair(database_id
, DatabaseMetaDataKey::kAllBlobsKey
));
2106 UpdatePrimaryJournalWithBlobList(transaction
.get(), primary_journal
);
2107 UpdateLiveBlobJournalWithBlobList(transaction
.get(), new_live_blob_journal
);
2108 transaction
->Commit();
2109 // We could just do the deletions/cleaning here, but if there are a lot of
2110 // blobs about to be garbage collected, it'd be better to wait and do them all
2112 StartJournalCleaningTimer();
2115 // The this reference is a raw pointer that's declared Unretained inside the
2116 // timer code, so this won't confuse IndexedDBFactory's check for
2117 // HasLastBackingStoreReference. It's safe because if the backing store is
2118 // deleted, the timer will automatically be canceled on destruction.
2119 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2120 journal_cleaning_timer_
.Start(
2122 base::TimeDelta::FromSeconds(5),
2124 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn
);
2127 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
2128 FilePath
IndexedDBBackingStore::GetBlobFileName(int64 database_id
, int64 key
) {
2129 return GetBlobFileNameForKey(blob_path_
, database_id
, key
);
2132 static bool CheckIndexAndMetaDataKey(const LevelDBIterator
* it
,
2133 const std::string
& stop_key
,
2135 unsigned char meta_data_type
) {
2136 if (!it
->IsValid() || CompareKeys(it
->Key(), stop_key
) >= 0)
2139 StringPiece
slice(it
->Key());
2140 IndexMetaDataKey meta_data_key
;
2141 bool ok
= IndexMetaDataKey::Decode(&slice
, &meta_data_key
);
2143 if (meta_data_key
.IndexId() != index_id
)
2145 if (meta_data_key
.meta_data_type() != meta_data_type
)
2150 // TODO(jsbell): This should do some error handling rather than plowing ahead
2151 // when bad data is encountered.
2152 leveldb::Status
IndexedDBBackingStore::GetIndexes(
2154 int64 object_store_id
,
2155 IndexedDBObjectStoreMetadata::IndexMap
* indexes
) {
2156 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2157 if (!KeyPrefix::ValidIds(database_id
, object_store_id
))
2158 return InvalidDBKeyStatus();
2159 const std::string start_key
=
2160 IndexMetaDataKey::Encode(database_id
, object_store_id
, 0, 0);
2161 const std::string stop_key
=
2162 IndexMetaDataKey::Encode(database_id
, object_store_id
+ 1, 0, 0);
2164 DCHECK(indexes
->empty());
2166 scoped_ptr
<LevelDBIterator
> it
= db_
->CreateIterator();
2167 leveldb::Status s
= it
->Seek(start_key
);
2168 while (s
.ok() && it
->IsValid() && CompareKeys(it
->Key(), stop_key
) < 0) {
2169 StringPiece
slice(it
->Key());
2170 IndexMetaDataKey meta_data_key
;
2171 bool ok
= IndexMetaDataKey::Decode(&slice
, &meta_data_key
);
2173 if (meta_data_key
.meta_data_type() != IndexMetaDataKey::NAME
) {
2174 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2175 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2183 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2185 int64 index_id
= meta_data_key
.IndexId();
2186 base::string16 index_name
;
2188 StringPiece
slice(it
->Value());
2189 if (!DecodeString(&slice
, &index_name
) || !slice
.empty())
2190 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2193 s
= it
->Next(); // unique flag
2196 if (!CheckIndexAndMetaDataKey(
2197 it
.get(), stop_key
, index_id
, IndexMetaDataKey::UNIQUE
)) {
2198 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2203 StringPiece
slice(it
->Value());
2204 if (!DecodeBool(&slice
, &index_unique
) || !slice
.empty())
2205 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2208 s
= it
->Next(); // key_path
2211 if (!CheckIndexAndMetaDataKey(
2212 it
.get(), stop_key
, index_id
, IndexMetaDataKey::KEY_PATH
)) {
2213 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2216 IndexedDBKeyPath key_path
;
2218 StringPiece
slice(it
->Value());
2219 if (!DecodeIDBKeyPath(&slice
, &key_path
) || !slice
.empty())
2220 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2223 s
= it
->Next(); // [optional] multi_entry flag
2226 bool index_multi_entry
= false;
2227 if (CheckIndexAndMetaDataKey(
2228 it
.get(), stop_key
, index_id
, IndexMetaDataKey::MULTI_ENTRY
)) {
2229 StringPiece
slice(it
->Value());
2230 if (!DecodeBool(&slice
, &index_multi_entry
) || !slice
.empty())
2231 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES
);
2238 (*indexes
)[index_id
] = IndexedDBIndexMetadata(
2239 index_name
, index_id
, key_path
, index_unique
, index_multi_entry
);
2243 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES
);
2248 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id
, int64 key
) {
2249 FilePath fileName
= GetBlobFileName(database_id
, key
);
2250 return base::DeleteFile(fileName
, false);
2253 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id
) {
2254 FilePath dirName
= GetBlobDirectoryName(blob_path_
, database_id
);
2255 return base::DeleteFile(dirName
, true);
2258 leveldb::Status
IndexedDBBackingStore::CleanUpBlobJournal(
2259 const std::string
& level_db_key
) {
2260 scoped_refptr
<LevelDBTransaction
> journal_transaction
=
2261 new LevelDBTransaction(db_
.get());
2262 BlobJournalType journal
;
2264 GetBlobJournal(level_db_key
, journal_transaction
.get(), &journal
);
2267 if (!journal
.size())
2268 return leveldb::Status::OK();
2269 BlobJournalType::iterator journal_iter
;
2270 for (journal_iter
= journal
.begin(); journal_iter
!= journal
.end();
2272 int64 database_id
= journal_iter
->first
;
2273 int64 blob_key
= journal_iter
->second
;
2274 DCHECK(KeyPrefix::IsValidDatabaseId(database_id
));
2275 if (blob_key
== DatabaseMetaDataKey::kAllBlobsKey
) {
2276 if (!RemoveBlobDirectory(database_id
))
2277 return IOErrorStatus();
2279 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key
));
2280 if (!RemoveBlobFile(database_id
, blob_key
))
2281 return IOErrorStatus();
2284 ClearBlobJournal(journal_transaction
.get(), level_db_key
);
2285 return journal_transaction
->Commit();
2288 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2289 CleanUpBlobJournal(BlobJournalKey::Encode());
2292 WARN_UNUSED_RESULT
static leveldb::Status
SetMaxIndexId(
2293 LevelDBTransaction
* transaction
,
2295 int64 object_store_id
,
2297 int64 max_index_id
= -1;
2298 const std::string max_index_id_key
= ObjectStoreMetaDataKey::Encode(
2299 database_id
, object_store_id
, ObjectStoreMetaDataKey::MAX_INDEX_ID
);
2302 GetInt(transaction
, max_index_id_key
, &max_index_id
, &found
);
2304 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID
);
2308 max_index_id
= kMinimumIndexId
;
2310 if (index_id
<= max_index_id
) {
2311 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID
);
2312 return InternalInconsistencyStatus();
2315 PutInt(transaction
, max_index_id_key
, index_id
);
2319 leveldb::Status
IndexedDBBackingStore::CreateIndex(
2320 IndexedDBBackingStore::Transaction
* transaction
,
2322 int64 object_store_id
,
2324 const base::string16
& name
,
2325 const IndexedDBKeyPath
& key_path
,
2327 bool is_multi_entry
) {
2328 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2329 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2330 return InvalidDBKeyStatus();
2331 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2332 leveldb::Status s
= SetMaxIndexId(
2333 leveldb_transaction
, database_id
, object_store_id
, index_id
);
2338 const std::string name_key
= IndexMetaDataKey::Encode(
2339 database_id
, object_store_id
, index_id
, IndexMetaDataKey::NAME
);
2340 const std::string unique_key
= IndexMetaDataKey::Encode(
2341 database_id
, object_store_id
, index_id
, IndexMetaDataKey::UNIQUE
);
2342 const std::string key_path_key
= IndexMetaDataKey::Encode(
2343 database_id
, object_store_id
, index_id
, IndexMetaDataKey::KEY_PATH
);
2344 const std::string multi_entry_key
= IndexMetaDataKey::Encode(
2345 database_id
, object_store_id
, index_id
, IndexMetaDataKey::MULTI_ENTRY
);
2347 PutString(leveldb_transaction
, name_key
, name
);
2348 PutBool(leveldb_transaction
, unique_key
, is_unique
);
2349 PutIDBKeyPath(leveldb_transaction
, key_path_key
, key_path
);
2350 PutBool(leveldb_transaction
, multi_entry_key
, is_multi_entry
);
2354 leveldb::Status
IndexedDBBackingStore::DeleteIndex(
2355 IndexedDBBackingStore::Transaction
* transaction
,
2357 int64 object_store_id
,
2359 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2360 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2361 return InvalidDBKeyStatus();
2362 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2364 const std::string index_meta_data_start
=
2365 IndexMetaDataKey::Encode(database_id
, object_store_id
, index_id
, 0);
2366 const std::string index_meta_data_end
=
2367 IndexMetaDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
2368 leveldb::Status s
= DeleteRange(
2369 leveldb_transaction
, index_meta_data_start
, index_meta_data_end
);
2372 const std::string index_data_start
=
2373 IndexDataKey::EncodeMinKey(database_id
, object_store_id
, index_id
);
2374 const std::string index_data_end
=
2375 IndexDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
2376 s
= DeleteRange(leveldb_transaction
, index_data_start
, index_data_end
);
2380 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX
);
2385 leveldb::Status
IndexedDBBackingStore::PutIndexDataForRecord(
2386 IndexedDBBackingStore::Transaction
* transaction
,
2388 int64 object_store_id
,
2390 const IndexedDBKey
& key
,
2391 const RecordIdentifier
& record_identifier
) {
2392 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2393 DCHECK(key
.IsValid());
2394 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2395 return InvalidDBKeyStatus();
2397 std::string encoded_key
;
2398 EncodeIDBKey(key
, &encoded_key
);
2400 const std::string index_data_key
=
2401 IndexDataKey::Encode(database_id
,
2405 record_identifier
.primary_key(),
2409 EncodeVarInt(record_identifier
.version(), &data
);
2410 data
.append(record_identifier
.primary_key());
2412 transaction
->transaction()->Put(index_data_key
, &data
);
2413 return leveldb::Status::OK();
2416 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction
* transaction
,
2417 const std::string
& target
,
2418 std::string
* found_key
,
2419 leveldb::Status
& s
) {
2420 scoped_ptr
<LevelDBIterator
> it
= transaction
->CreateIterator();
2421 s
= it
->Seek(target
);
2425 if (!it
->IsValid()) {
2426 s
= it
->SeekToLast();
2427 if (!s
.ok() || !it
->IsValid())
2431 while (CompareIndexKeys(it
->Key(), target
) > 0) {
2433 if (!s
.ok() || !it
->IsValid())
2438 *found_key
= it
->Key().as_string();
2440 // There can be several index keys that compare equal. We want the last one.
2442 } while (s
.ok() && it
->IsValid() && !CompareIndexKeys(it
->Key(), target
));
2447 static leveldb::Status
VersionExists(LevelDBTransaction
* transaction
,
2449 int64 object_store_id
,
2451 const std::string
& encoded_primary_key
,
2453 const std::string key
=
2454 ExistsEntryKey::Encode(database_id
, object_store_id
, encoded_primary_key
);
2457 leveldb::Status s
= transaction
->Get(key
, &data
, exists
);
2459 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS
);
2465 StringPiece
slice(data
);
2467 if (!DecodeInt(&slice
, &decoded
) || !slice
.empty())
2468 return InternalInconsistencyStatus();
2469 *exists
= (decoded
== version
);
2473 leveldb::Status
IndexedDBBackingStore::FindKeyInIndex(
2474 IndexedDBBackingStore::Transaction
* transaction
,
2476 int64 object_store_id
,
2478 const IndexedDBKey
& key
,
2479 std::string
* found_encoded_primary_key
,
2481 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
2482 DCHECK(KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
));
2484 DCHECK(found_encoded_primary_key
->empty());
2487 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
2488 const std::string leveldb_key
=
2489 IndexDataKey::Encode(database_id
, object_store_id
, index_id
, key
);
2490 scoped_ptr
<LevelDBIterator
> it
= leveldb_transaction
->CreateIterator();
2491 leveldb::Status s
= it
->Seek(leveldb_key
);
2493 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX
);
2499 return leveldb::Status::OK();
2500 if (CompareIndexKeys(it
->Key(), leveldb_key
) > 0)
2501 return leveldb::Status::OK();
2503 StringPiece
slice(it
->Value());
2506 if (!DecodeVarInt(&slice
, &version
)) {
2507 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX
);
2508 return InternalInconsistencyStatus();
2510 *found_encoded_primary_key
= slice
.as_string();
2512 bool exists
= false;
2513 s
= VersionExists(leveldb_transaction
,
2517 *found_encoded_primary_key
,
2522 // Delete stale index data entry and continue.
2523 leveldb_transaction
->Remove(it
->Key());
2532 leveldb::Status
IndexedDBBackingStore::GetPrimaryKeyViaIndex(
2533 IndexedDBBackingStore::Transaction
* transaction
,
2535 int64 object_store_id
,
2537 const IndexedDBKey
& key
,
2538 scoped_ptr
<IndexedDBKey
>* primary_key
) {
2539 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
2540 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2541 return InvalidDBKeyStatus();
2544 std::string found_encoded_primary_key
;
2545 leveldb::Status s
= FindKeyInIndex(transaction
,
2550 &found_encoded_primary_key
,
2553 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX
);
2558 if (!found_encoded_primary_key
.size()) {
2559 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX
);
2560 return InvalidDBKeyStatus();
2563 StringPiece
slice(found_encoded_primary_key
);
2564 if (DecodeIDBKey(&slice
, primary_key
) && slice
.empty())
2567 return InvalidDBKeyStatus();
2570 leveldb::Status
IndexedDBBackingStore::KeyExistsInIndex(
2571 IndexedDBBackingStore::Transaction
* transaction
,
2573 int64 object_store_id
,
2575 const IndexedDBKey
& index_key
,
2576 scoped_ptr
<IndexedDBKey
>* found_primary_key
,
2578 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
2579 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
2580 return InvalidDBKeyStatus();
2583 std::string found_encoded_primary_key
;
2584 leveldb::Status s
= FindKeyInIndex(transaction
,
2589 &found_encoded_primary_key
,
2592 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX
);
2596 return leveldb::Status::OK();
2597 if (found_encoded_primary_key
.empty()) {
2598 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX
);
2599 return InvalidDBKeyStatus();
2602 StringPiece
slice(found_encoded_primary_key
);
2603 if (DecodeIDBKey(&slice
, found_primary_key
) && slice
.empty())
2606 return InvalidDBKeyStatus();
2609 IndexedDBBackingStore::Cursor::Cursor(
2610 const IndexedDBBackingStore::Cursor
* other
)
2611 : transaction_(other
->transaction_
),
2612 cursor_options_(other
->cursor_options_
),
2613 current_key_(new IndexedDBKey(*other
->current_key_
)) {
2614 if (other
->iterator_
) {
2615 iterator_
= transaction_
->CreateIterator();
2617 if (other
->iterator_
->IsValid()) {
2618 leveldb::Status s
= iterator_
->Seek(other
->iterator_
->Key());
2619 // TODO(cmumford): Handle this error (crbug.com/363397)
2620 DCHECK(iterator_
->IsValid());
2625 IndexedDBBackingStore::Cursor::Cursor(LevelDBTransaction
* transaction
,
2626 const CursorOptions
& cursor_options
)
2627 : transaction_(transaction
), cursor_options_(cursor_options
) {}
2628 IndexedDBBackingStore::Cursor::~Cursor() {}
2630 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status
* s
) {
2631 iterator_
= transaction_
->CreateIterator();
2632 if (cursor_options_
.forward
)
2633 *s
= iterator_
->Seek(cursor_options_
.low_key
);
2635 *s
= iterator_
->Seek(cursor_options_
.high_key
);
2639 return Continue(0, READY
, s
);
2642 bool IndexedDBBackingStore::Cursor::Advance(uint32 count
, leveldb::Status
* s
) {
2643 *s
= leveldb::Status::OK();
2651 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey
* key
,
2652 const IndexedDBKey
* primary_key
,
2653 IteratorState next_state
,
2654 leveldb::Status
* s
) {
2655 DCHECK(!key
|| key
->IsValid());
2656 DCHECK(!primary_key
|| primary_key
->IsValid());
2657 *s
= leveldb::Status::OK();
2659 // TODO(alecflett): avoid a copy here?
2660 IndexedDBKey previous_key
= current_key_
? *current_key_
: IndexedDBKey();
2662 bool first_iteration
= true;
2664 // When iterating with PrevNoDuplicate, spec requires that the
2665 // value we yield for each key is the first duplicate in forwards
2667 IndexedDBKey last_duplicate_key
;
2669 bool forward
= cursor_options_
.forward
;
2672 if (next_state
== SEEK
) {
2673 // TODO(jsbell): Optimize seeking for reverse cursors as well.
2674 if (first_iteration
&& key
&& forward
) {
2675 std::string leveldb_key
;
2677 leveldb_key
= EncodeKey(*key
, *primary_key
);
2679 leveldb_key
= EncodeKey(*key
);
2681 *s
= iterator_
->Seek(leveldb_key
);
2682 first_iteration
= false;
2683 } else if (forward
) {
2684 *s
= iterator_
->Next();
2686 *s
= iterator_
->Prev();
2691 next_state
= SEEK
; // for subsequent iterations
2694 if (!iterator_
->IsValid()) {
2695 if (!forward
&& last_duplicate_key
.IsValid()) {
2696 // We need to walk forward because we hit the end of
2705 if (IsPastBounds()) {
2706 if (!forward
&& last_duplicate_key
.IsValid()) {
2707 // We need to walk forward because now we're beyond the
2708 // bounds defined by the cursor.
2716 if (!HaveEnteredRange())
2719 // The row may not load because there's a stale entry in the
2720 // index. This is not fatal.
2721 if (!LoadCurrentRow())
2726 if (primary_key
&& current_key_
->Equals(*key
) &&
2727 this->primary_key().IsLessThan(*primary_key
))
2729 if (current_key_
->IsLessThan(*key
))
2732 if (primary_key
&& key
->Equals(*current_key_
) &&
2733 primary_key
->IsLessThan(this->primary_key()))
2735 if (key
->IsLessThan(*current_key_
))
2740 if (cursor_options_
.unique
) {
2741 if (previous_key
.IsValid() && current_key_
->Equals(previous_key
)) {
2742 // We should never be able to walk forward all the way
2743 // to the previous key.
2744 DCHECK(!last_duplicate_key
.IsValid());
2749 if (!last_duplicate_key
.IsValid()) {
2750 last_duplicate_key
= *current_key_
;
2754 // We need to walk forward because we hit the boundary
2755 // between key ranges.
2756 if (!last_duplicate_key
.Equals(*current_key_
)) {
2767 DCHECK(!last_duplicate_key
.IsValid() ||
2768 (forward
&& last_duplicate_key
.Equals(*current_key_
)));
2772 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
2773 if (cursor_options_
.forward
) {
2774 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.low_key
);
2775 if (cursor_options_
.low_open
) {
2778 return compare
>= 0;
2780 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.high_key
);
2781 if (cursor_options_
.high_open
) {
2784 return compare
<= 0;
2787 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
2788 if (cursor_options_
.forward
) {
2789 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.high_key
);
2790 if (cursor_options_
.high_open
) {
2791 return compare
>= 0;
2795 int compare
= CompareIndexKeys(iterator_
->Key(), cursor_options_
.low_key
);
2796 if (cursor_options_
.low_open
) {
2797 return compare
<= 0;
2802 const IndexedDBKey
& IndexedDBBackingStore::Cursor::primary_key() const {
2803 return *current_key_
;
2806 const IndexedDBBackingStore::RecordIdentifier
&
2807 IndexedDBBackingStore::Cursor::record_identifier() const {
2808 return record_identifier_
;
2811 class ObjectStoreKeyCursorImpl
: public IndexedDBBackingStore::Cursor
{
2813 ObjectStoreKeyCursorImpl(
2814 LevelDBTransaction
* transaction
,
2815 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
2816 : IndexedDBBackingStore::Cursor(transaction
, cursor_options
) {}
2818 virtual Cursor
* Clone() OVERRIDE
{
2819 return new ObjectStoreKeyCursorImpl(this);
2822 // IndexedDBBackingStore::Cursor
2823 virtual IndexedDBValue
* value() OVERRIDE
{
2827 virtual bool LoadCurrentRow() OVERRIDE
;
2830 virtual std::string
EncodeKey(const IndexedDBKey
& key
) OVERRIDE
{
2831 return ObjectStoreDataKey::Encode(
2832 cursor_options_
.database_id
, cursor_options_
.object_store_id
, key
);
2834 virtual std::string
EncodeKey(const IndexedDBKey
& key
,
2835 const IndexedDBKey
& primary_key
) OVERRIDE
{
2837 return std::string();
2841 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl
* other
)
2842 : IndexedDBBackingStore::Cursor(other
) {}
2845 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
2846 StringPiece
slice(iterator_
->Key());
2847 ObjectStoreDataKey object_store_data_key
;
2848 if (!ObjectStoreDataKey::Decode(&slice
, &object_store_data_key
)) {
2849 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
2853 current_key_
= object_store_data_key
.user_key();
2856 slice
= StringPiece(iterator_
->Value());
2857 if (!DecodeVarInt(&slice
, &version
)) {
2858 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
2862 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
2863 std::string encoded_key
;
2864 EncodeIDBKey(*current_key_
, &encoded_key
);
2865 record_identifier_
.Reset(encoded_key
, version
);
2870 class ObjectStoreCursorImpl
: public IndexedDBBackingStore::Cursor
{
2872 ObjectStoreCursorImpl(
2873 LevelDBTransaction
* transaction
,
2874 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
2875 : IndexedDBBackingStore::Cursor(transaction
, cursor_options
) {}
2877 virtual Cursor
* Clone() OVERRIDE
{ return new ObjectStoreCursorImpl(this); }
2879 // IndexedDBBackingStore::Cursor
2880 virtual IndexedDBValue
* value() OVERRIDE
{ return ¤t_value_
; }
2881 virtual bool LoadCurrentRow() OVERRIDE
;
2884 virtual std::string
EncodeKey(const IndexedDBKey
& key
) OVERRIDE
{
2885 return ObjectStoreDataKey::Encode(
2886 cursor_options_
.database_id
, cursor_options_
.object_store_id
, key
);
2888 virtual std::string
EncodeKey(const IndexedDBKey
& key
,
2889 const IndexedDBKey
& primary_key
) OVERRIDE
{
2891 return std::string();
2895 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl
* other
)
2896 : IndexedDBBackingStore::Cursor(other
),
2897 current_value_(other
->current_value_
) {}
2899 IndexedDBValue current_value_
;
2902 bool ObjectStoreCursorImpl::LoadCurrentRow() {
2903 StringPiece
key_slice(iterator_
->Key());
2904 ObjectStoreDataKey object_store_data_key
;
2905 if (!ObjectStoreDataKey::Decode(&key_slice
, &object_store_data_key
)) {
2906 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
2910 current_key_
= object_store_data_key
.user_key();
2913 StringPiece value_slice
= StringPiece(iterator_
->Value());
2914 if (!DecodeVarInt(&value_slice
, &version
)) {
2915 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
2919 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
2920 std::string encoded_key
;
2921 EncodeIDBKey(*current_key_
, &encoded_key
);
2922 record_identifier_
.Reset(encoded_key
, version
);
2924 current_value_
.bits
= value_slice
.as_string();
2928 class IndexKeyCursorImpl
: public IndexedDBBackingStore::Cursor
{
2931 LevelDBTransaction
* transaction
,
2932 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
2933 : IndexedDBBackingStore::Cursor(transaction
, cursor_options
) {}
2935 virtual Cursor
* Clone() OVERRIDE
{ return new IndexKeyCursorImpl(this); }
2937 // IndexedDBBackingStore::Cursor
2938 virtual IndexedDBValue
* value() OVERRIDE
{
2942 virtual const IndexedDBKey
& primary_key() const OVERRIDE
{
2943 return *primary_key_
;
2945 virtual const IndexedDBBackingStore::RecordIdentifier
& record_identifier()
2948 return record_identifier_
;
2950 virtual bool LoadCurrentRow() OVERRIDE
;
2953 virtual std::string
EncodeKey(const IndexedDBKey
& key
) OVERRIDE
{
2954 return IndexDataKey::Encode(cursor_options_
.database_id
,
2955 cursor_options_
.object_store_id
,
2956 cursor_options_
.index_id
,
2959 virtual std::string
EncodeKey(const IndexedDBKey
& key
,
2960 const IndexedDBKey
& primary_key
) OVERRIDE
{
2961 return IndexDataKey::Encode(cursor_options_
.database_id
,
2962 cursor_options_
.object_store_id
,
2963 cursor_options_
.index_id
,
2969 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl
* other
)
2970 : IndexedDBBackingStore::Cursor(other
),
2971 primary_key_(new IndexedDBKey(*other
->primary_key_
)) {}
2973 scoped_ptr
<IndexedDBKey
> primary_key_
;
2976 bool IndexKeyCursorImpl::LoadCurrentRow() {
2977 StringPiece
slice(iterator_
->Key());
2978 IndexDataKey index_data_key
;
2979 if (!IndexDataKey::Decode(&slice
, &index_data_key
)) {
2980 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
2984 current_key_
= index_data_key
.user_key();
2985 DCHECK(current_key_
);
2987 slice
= StringPiece(iterator_
->Value());
2988 int64 index_data_version
;
2989 if (!DecodeVarInt(&slice
, &index_data_version
)) {
2990 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
2994 if (!DecodeIDBKey(&slice
, &primary_key_
) || !slice
.empty()) {
2995 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
2999 std::string primary_leveldb_key
=
3000 ObjectStoreDataKey::Encode(index_data_key
.DatabaseId(),
3001 index_data_key
.ObjectStoreId(),
3006 leveldb::Status s
= transaction_
->Get(primary_leveldb_key
, &result
, &found
);
3008 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3012 transaction_
->Remove(iterator_
->Key());
3015 if (!result
.size()) {
3016 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3020 int64 object_store_data_version
;
3021 slice
= StringPiece(result
);
3022 if (!DecodeVarInt(&slice
, &object_store_data_version
)) {
3023 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3027 if (object_store_data_version
!= index_data_version
) {
3028 transaction_
->Remove(iterator_
->Key());
3035 class IndexCursorImpl
: public IndexedDBBackingStore::Cursor
{
3038 LevelDBTransaction
* transaction
,
3039 const IndexedDBBackingStore::Cursor::CursorOptions
& cursor_options
)
3040 : IndexedDBBackingStore::Cursor(transaction
, cursor_options
) {}
3042 virtual Cursor
* Clone() OVERRIDE
{ return new IndexCursorImpl(this); }
3044 // IndexedDBBackingStore::Cursor
3045 virtual IndexedDBValue
* value() OVERRIDE
{ return ¤t_value_
; }
3046 virtual const IndexedDBKey
& primary_key() const OVERRIDE
{
3047 return *primary_key_
;
3049 virtual const IndexedDBBackingStore::RecordIdentifier
& record_identifier()
3052 return record_identifier_
;
3054 virtual bool LoadCurrentRow() OVERRIDE
;
3057 virtual std::string
EncodeKey(const IndexedDBKey
& key
) OVERRIDE
{
3058 return IndexDataKey::Encode(cursor_options_
.database_id
,
3059 cursor_options_
.object_store_id
,
3060 cursor_options_
.index_id
,
3063 virtual std::string
EncodeKey(const IndexedDBKey
& key
,
3064 const IndexedDBKey
& primary_key
) OVERRIDE
{
3065 return IndexDataKey::Encode(cursor_options_
.database_id
,
3066 cursor_options_
.object_store_id
,
3067 cursor_options_
.index_id
,
3073 explicit IndexCursorImpl(const IndexCursorImpl
* other
)
3074 : IndexedDBBackingStore::Cursor(other
),
3075 primary_key_(new IndexedDBKey(*other
->primary_key_
)),
3076 current_value_(other
->current_value_
),
3077 primary_leveldb_key_(other
->primary_leveldb_key_
) {}
3079 scoped_ptr
<IndexedDBKey
> primary_key_
;
3080 IndexedDBValue current_value_
;
3081 std::string primary_leveldb_key_
;
3084 bool IndexCursorImpl::LoadCurrentRow() {
3085 StringPiece
slice(iterator_
->Key());
3086 IndexDataKey index_data_key
;
3087 if (!IndexDataKey::Decode(&slice
, &index_data_key
)) {
3088 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3092 current_key_
= index_data_key
.user_key();
3093 DCHECK(current_key_
);
3095 slice
= StringPiece(iterator_
->Value());
3096 int64 index_data_version
;
3097 if (!DecodeVarInt(&slice
, &index_data_version
)) {
3098 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3101 if (!DecodeIDBKey(&slice
, &primary_key_
)) {
3102 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3106 primary_leveldb_key_
=
3107 ObjectStoreDataKey::Encode(index_data_key
.DatabaseId(),
3108 index_data_key
.ObjectStoreId(),
3113 leveldb::Status s
= transaction_
->Get(primary_leveldb_key_
, &result
, &found
);
3115 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3119 transaction_
->Remove(iterator_
->Key());
3122 if (!result
.size()) {
3123 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3127 int64 object_store_data_version
;
3128 slice
= StringPiece(result
);
3129 if (!DecodeVarInt(&slice
, &object_store_data_version
)) {
3130 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW
);
3134 if (object_store_data_version
!= index_data_version
) {
3135 transaction_
->Remove(iterator_
->Key());
3139 current_value_
.bits
= slice
.as_string();
3143 bool ObjectStoreCursorOptions(
3144 LevelDBTransaction
* transaction
,
3146 int64 object_store_id
,
3147 const IndexedDBKeyRange
& range
,
3148 indexed_db::CursorDirection direction
,
3149 IndexedDBBackingStore::Cursor::CursorOptions
* cursor_options
) {
3150 cursor_options
->database_id
= database_id
;
3151 cursor_options
->object_store_id
= object_store_id
;
3153 bool lower_bound
= range
.lower().IsValid();
3154 bool upper_bound
= range
.upper().IsValid();
3155 cursor_options
->forward
=
3156 (direction
== indexed_db::CURSOR_NEXT_NO_DUPLICATE
||
3157 direction
== indexed_db::CURSOR_NEXT
);
3158 cursor_options
->unique
= (direction
== indexed_db::CURSOR_NEXT_NO_DUPLICATE
||
3159 direction
== indexed_db::CURSOR_PREV_NO_DUPLICATE
);
3162 cursor_options
->low_key
=
3163 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MinIDBKey());
3164 cursor_options
->low_open
= true; // Not included.
3166 cursor_options
->low_key
=
3167 ObjectStoreDataKey::Encode(database_id
, object_store_id
, range
.lower());
3168 cursor_options
->low_open
= range
.lowerOpen();
3174 cursor_options
->high_key
=
3175 ObjectStoreDataKey::Encode(database_id
, object_store_id
, MaxIDBKey());
3177 if (cursor_options
->forward
) {
3178 cursor_options
->high_open
= true; // Not included.
3180 // We need a key that exists.
3181 // TODO(cmumford): Handle this error (crbug.com/363397)
3182 if (!FindGreatestKeyLessThanOrEqual(transaction
,
3183 cursor_options
->high_key
,
3184 &cursor_options
->high_key
,
3187 cursor_options
->high_open
= false;
3190 cursor_options
->high_key
=
3191 ObjectStoreDataKey::Encode(database_id
, object_store_id
, range
.upper());
3192 cursor_options
->high_open
= range
.upperOpen();
3194 if (!cursor_options
->forward
) {
3195 // For reverse cursors, we need a key that exists.
3196 std::string found_high_key
;
3197 // TODO(cmumford): Handle this error (crbug.com/363397)
3198 if (!FindGreatestKeyLessThanOrEqual(
3199 transaction
, cursor_options
->high_key
, &found_high_key
, s
))
3202 // If the target key should not be included, but we end up with a smaller
3203 // key, we should include that.
3204 if (cursor_options
->high_open
&&
3205 CompareIndexKeys(found_high_key
, cursor_options
->high_key
) < 0)
3206 cursor_options
->high_open
= false;
3208 cursor_options
->high_key
= found_high_key
;
3215 bool IndexCursorOptions(
3216 LevelDBTransaction
* transaction
,
3218 int64 object_store_id
,
3220 const IndexedDBKeyRange
& range
,
3221 indexed_db::CursorDirection direction
,
3222 IndexedDBBackingStore::Cursor::CursorOptions
* cursor_options
) {
3223 DCHECK(transaction
);
3224 if (!KeyPrefix::ValidIds(database_id
, object_store_id
, index_id
))
3227 cursor_options
->database_id
= database_id
;
3228 cursor_options
->object_store_id
= object_store_id
;
3229 cursor_options
->index_id
= index_id
;
3231 bool lower_bound
= range
.lower().IsValid();
3232 bool upper_bound
= range
.upper().IsValid();
3233 cursor_options
->forward
=
3234 (direction
== indexed_db::CURSOR_NEXT_NO_DUPLICATE
||
3235 direction
== indexed_db::CURSOR_NEXT
);
3236 cursor_options
->unique
= (direction
== indexed_db::CURSOR_NEXT_NO_DUPLICATE
||
3237 direction
== indexed_db::CURSOR_PREV_NO_DUPLICATE
);
3240 cursor_options
->low_key
=
3241 IndexDataKey::EncodeMinKey(database_id
, object_store_id
, index_id
);
3242 cursor_options
->low_open
= false; // Included.
3244 cursor_options
->low_key
= IndexDataKey::Encode(
3245 database_id
, object_store_id
, index_id
, range
.lower());
3246 cursor_options
->low_open
= range
.lowerOpen();
3252 cursor_options
->high_key
=
3253 IndexDataKey::EncodeMaxKey(database_id
, object_store_id
, index_id
);
3254 cursor_options
->high_open
= false; // Included.
3256 if (!cursor_options
->forward
) { // We need a key that exists.
3257 if (!FindGreatestKeyLessThanOrEqual(transaction
,
3258 cursor_options
->high_key
,
3259 &cursor_options
->high_key
,
3262 cursor_options
->high_open
= false;
3265 cursor_options
->high_key
= IndexDataKey::Encode(
3266 database_id
, object_store_id
, index_id
, range
.upper());
3267 cursor_options
->high_open
= range
.upperOpen();
3269 std::string found_high_key
;
3270 // Seek to the *last* key in the set of non-unique keys
3271 // TODO(cmumford): Handle this error (crbug.com/363397)
3272 if (!FindGreatestKeyLessThanOrEqual(
3273 transaction
, cursor_options
->high_key
, &found_high_key
, s
))
3276 // If the target key should not be included, but we end up with a smaller
3277 // key, we should include that.
3278 if (cursor_options
->high_open
&&
3279 CompareIndexKeys(found_high_key
, cursor_options
->high_key
) < 0)
3280 cursor_options
->high_open
= false;
3282 cursor_options
->high_key
= found_high_key
;
3288 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3289 IndexedDBBackingStore::OpenObjectStoreCursor(
3290 IndexedDBBackingStore::Transaction
* transaction
,
3292 int64 object_store_id
,
3293 const IndexedDBKeyRange
& range
,
3294 indexed_db::CursorDirection direction
,
3295 leveldb::Status
* s
) {
3296 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3297 *s
= leveldb::Status::OK();
3298 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3299 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3300 if (!ObjectStoreCursorOptions(leveldb_transaction
,
3306 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3307 scoped_ptr
<ObjectStoreCursorImpl
> cursor(
3308 new ObjectStoreCursorImpl(leveldb_transaction
, cursor_options
));
3309 if (!cursor
->FirstSeek(s
))
3310 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3312 return cursor
.PassAs
<IndexedDBBackingStore::Cursor
>();
3315 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3316 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3317 IndexedDBBackingStore::Transaction
* transaction
,
3319 int64 object_store_id
,
3320 const IndexedDBKeyRange
& range
,
3321 indexed_db::CursorDirection direction
,
3322 leveldb::Status
* s
) {
3323 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3324 *s
= leveldb::Status::OK();
3325 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3326 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3327 if (!ObjectStoreCursorOptions(leveldb_transaction
,
3333 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3334 scoped_ptr
<ObjectStoreKeyCursorImpl
> cursor(
3335 new ObjectStoreKeyCursorImpl(leveldb_transaction
, cursor_options
));
3336 if (!cursor
->FirstSeek(s
))
3337 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3339 return cursor
.PassAs
<IndexedDBBackingStore::Cursor
>();
3342 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3343 IndexedDBBackingStore::OpenIndexKeyCursor(
3344 IndexedDBBackingStore::Transaction
* transaction
,
3346 int64 object_store_id
,
3348 const IndexedDBKeyRange
& range
,
3349 indexed_db::CursorDirection direction
,
3350 leveldb::Status
* s
) {
3351 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3352 *s
= leveldb::Status::OK();
3353 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3354 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3355 if (!IndexCursorOptions(leveldb_transaction
,
3362 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3363 scoped_ptr
<IndexKeyCursorImpl
> cursor(
3364 new IndexKeyCursorImpl(leveldb_transaction
, cursor_options
));
3365 if (!cursor
->FirstSeek(s
))
3366 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3368 return cursor
.PassAs
<IndexedDBBackingStore::Cursor
>();
3371 scoped_ptr
<IndexedDBBackingStore::Cursor
>
3372 IndexedDBBackingStore::OpenIndexCursor(
3373 IndexedDBBackingStore::Transaction
* transaction
,
3375 int64 object_store_id
,
3377 const IndexedDBKeyRange
& range
,
3378 indexed_db::CursorDirection direction
,
3379 leveldb::Status
* s
) {
3380 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
3381 LevelDBTransaction
* leveldb_transaction
= transaction
->transaction();
3382 IndexedDBBackingStore::Cursor::CursorOptions cursor_options
;
3383 if (!IndexCursorOptions(leveldb_transaction
,
3390 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3391 scoped_ptr
<IndexCursorImpl
> cursor(
3392 new IndexCursorImpl(leveldb_transaction
, cursor_options
));
3393 if (!cursor
->FirstSeek(s
))
3394 return scoped_ptr
<IndexedDBBackingStore::Cursor
>();
3396 return cursor
.PassAs
<IndexedDBBackingStore::Cursor
>();
3399 IndexedDBBackingStore::Transaction::Transaction(
3400 IndexedDBBackingStore
* backing_store
)
3401 : backing_store_(backing_store
), database_id_(-1) {}
3403 IndexedDBBackingStore::Transaction::~Transaction() {
3404 STLDeleteContainerPairSecondPointers(
3405 blob_change_map_
.begin(), blob_change_map_
.end());
3408 void IndexedDBBackingStore::Transaction::Begin() {
3409 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
3410 DCHECK(!transaction_
.get());
3411 transaction_
= new LevelDBTransaction(backing_store_
->db_
.get());
3414 leveldb::Status
IndexedDBBackingStore::Transaction::Commit() {
3415 IDB_TRACE("IndexedDBBackingStore::Transaction::Commit");
3416 DCHECK(transaction_
.get());
3417 leveldb::Status s
= transaction_
->Commit();
3418 transaction_
= NULL
;
3420 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD
);
3425 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
3426 : public IndexedDBBackingStore::BlobWriteCallback
{
3428 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction
* transaction
,
3429 scoped_refptr
<BlobWriteCallback
> callback
)
3430 : transaction_(transaction
), callback_(callback
) {}
3431 virtual void Run(bool succeeded
) OVERRIDE
{
3432 callback_
->Run(succeeded
);
3433 transaction_
->chained_blob_writer_
= NULL
;
3437 virtual ~BlobWriteCallbackWrapper() {}
3438 friend class base::RefCounted
<IndexedDBBackingStore::BlobWriteCallback
>;
3440 IndexedDBBackingStore::Transaction
* transaction_
;
3441 scoped_refptr
<BlobWriteCallback
> callback_
;
3444 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
3445 BlobEntryKeyValuePairVec
& new_blob_entries
,
3446 WriteDescriptorVec
& new_files_to_write
,
3447 scoped_refptr
<BlobWriteCallback
> callback
) {
3448 DCHECK_GT(new_files_to_write
.size(), 0UL);
3449 DCHECK_GT(database_id_
, 0);
3450 BlobEntryKeyValuePairVec::iterator blob_entry_iter
;
3451 for (blob_entry_iter
= new_blob_entries
.begin();
3452 blob_entry_iter
!= new_blob_entries
.end();
3453 ++blob_entry_iter
) {
3454 // Add the new blob-table entry for each blob to the main transaction, or
3455 // remove any entry that may exist if there's no new one.
3456 if (!blob_entry_iter
->second
.size())
3457 transaction_
->Remove(blob_entry_iter
->first
.Encode());
3459 transaction_
->Put(blob_entry_iter
->first
.Encode(),
3460 &blob_entry_iter
->second
);
3462 // Creating the writer will start it going asynchronously.
3463 chained_blob_writer_
=
3464 new ChainedBlobWriterImpl(database_id_
,
3467 new BlobWriteCallbackWrapper(this, callback
));
3470 void IndexedDBBackingStore::Transaction::Rollback() {
3471 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
3472 DCHECK(transaction_
.get());
3473 if (chained_blob_writer_
) {
3474 chained_blob_writer_
->Abort();
3475 chained_blob_writer_
= NULL
;
3477 transaction_
->Rollback();
3478 transaction_
= NULL
;
3481 IndexedDBBackingStore::Transaction::BlobChangeRecord::BlobChangeRecord(
3482 const std::string
& key
, int64 object_store_id
)
3483 : key_(key
), object_store_id_(object_store_id
) {
3486 IndexedDBBackingStore::Transaction::BlobChangeRecord::~BlobChangeRecord() {
3489 void IndexedDBBackingStore::Transaction::BlobChangeRecord::SetBlobInfo(
3490 std::vector
<IndexedDBBlobInfo
>* blob_info
) {
3493 blob_info_
.swap(*blob_info
);
3496 void IndexedDBBackingStore::Transaction::BlobChangeRecord::SetHandles(
3497 ScopedVector
<webkit_blob::BlobDataHandle
>* handles
) {
3500 handles_
.swap(*handles
);
3503 // This is storing an info, even if empty, even if the previous key had no blob
3504 // info that we know of. It duplicates a bunch of information stored in the
3505 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
3506 // changes to exists or index keys here.
3507 void IndexedDBBackingStore::Transaction::PutBlobInfo(
3509 int64 object_store_id
,
3510 const std::string
& key
,
3511 std::vector
<IndexedDBBlobInfo
>* blob_info
,
3512 ScopedVector
<webkit_blob::BlobDataHandle
>* handles
) {
3513 DCHECK_GT(key
.size(), 0UL);
3514 if (database_id_
< 0)
3515 database_id_
= database_id
;
3516 DCHECK_EQ(database_id_
, database_id
);
3518 BlobChangeMap::iterator it
= blob_change_map_
.find(key
);
3519 BlobChangeRecord
* record
= NULL
;
3520 if (it
== blob_change_map_
.end()) {
3521 record
= new BlobChangeRecord(key
, object_store_id
);
3522 blob_change_map_
[key
] = record
;
3524 record
= it
->second
;
3526 DCHECK_EQ(record
->object_store_id(), object_store_id
);
3527 record
->SetBlobInfo(blob_info
);
3528 record
->SetHandles(handles
);
3529 DCHECK(!handles
|| !handles
->size());
3532 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
3535 : is_file_(false), url_(url
), key_(key
) {}
3537 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
3538 const FilePath
& file_path
,
3540 : is_file_(true), file_path_(file_path
), key_(key
) {}
3542 } // namespace content