Battery Status API: add UMA logging for Linux.
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_backing_store.cc
blobc27216b20bd7527d79d90b5035b2762f056a90cc
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/indexed_db/indexed_db_backing_store.h"
7 #include "base/file_util.h"
8 #include "base/files/file_path.h"
9 #include "base/format_macros.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "content/browser/child_process_security_policy_impl.h"
18 #include "content/browser/indexed_db/indexed_db_blob_info.h"
19 #include "content/browser/indexed_db/indexed_db_class_factory.h"
20 #include "content/browser/indexed_db/indexed_db_database_error.h"
21 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
22 #include "content/browser/indexed_db/indexed_db_metadata.h"
23 #include "content/browser/indexed_db/indexed_db_tracing.h"
24 #include "content/browser/indexed_db/indexed_db_value.h"
25 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
26 #include "content/browser/indexed_db/leveldb/leveldb_database.h"
27 #include "content/browser/indexed_db/leveldb/leveldb_factory.h"
28 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
29 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
30 #include "content/common/indexed_db/indexed_db_key.h"
31 #include "content/common/indexed_db/indexed_db_key_path.h"
32 #include "content/common/indexed_db/indexed_db_key_range.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "net/url_request/url_request_context.h"
35 #include "third_party/WebKit/public/platform/WebIDBTypes.h"
36 #include "third_party/WebKit/public/web/WebSerializedScriptValueVersion.h"
37 #include "third_party/leveldatabase/env_chromium.h"
38 #include "webkit/browser/blob/blob_data_handle.h"
39 #include "webkit/browser/fileapi/file_stream_writer.h"
40 #include "webkit/browser/fileapi/file_writer_delegate.h"
41 #include "webkit/browser/fileapi/local_file_stream_writer.h"
42 #include "webkit/common/database/database_identifier.h"
44 using base::FilePath;
45 using base::StringPiece;
46 using storage::FileWriterDelegate;
48 namespace content {
50 namespace {
52 FilePath GetBlobDirectoryName(const FilePath& pathBase, int64 database_id) {
53 return pathBase.AppendASCII(base::StringPrintf("%" PRIx64, database_id));
56 FilePath GetBlobDirectoryNameForKey(const FilePath& pathBase,
57 int64 database_id,
58 int64 key) {
59 FilePath path = GetBlobDirectoryName(pathBase, database_id);
60 path = path.AppendASCII(base::StringPrintf(
61 "%02x", static_cast<int>(key & 0x000000000000ff00) >> 8));
62 return path;
65 FilePath GetBlobFileNameForKey(const FilePath& pathBase,
66 int64 database_id,
67 int64 key) {
68 FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
69 path = path.AppendASCII(base::StringPrintf("%" PRIx64, key));
70 return path;
73 bool MakeIDBBlobDirectory(const FilePath& pathBase,
74 int64 database_id,
75 int64 key) {
76 FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
77 return base::CreateDirectory(path);
80 static std::string ComputeOriginIdentifier(const GURL& origin_url) {
81 return storage::GetIdentifierFromOrigin(origin_url) + "@1";
84 static base::FilePath ComputeFileName(const GURL& origin_url) {
85 return base::FilePath()
86 .AppendASCII(storage::GetIdentifierFromOrigin(origin_url))
87 .AddExtension(FILE_PATH_LITERAL(".indexeddb.leveldb"));
90 static base::FilePath ComputeBlobPath(const GURL& origin_url) {
91 return base::FilePath()
92 .AppendASCII(storage::GetIdentifierFromOrigin(origin_url))
93 .AddExtension(FILE_PATH_LITERAL(".indexeddb.blob"));
96 static base::FilePath ComputeCorruptionFileName(const GURL& origin_url) {
97 return ComputeFileName(origin_url)
98 .Append(FILE_PATH_LITERAL("corruption_info.json"));
101 } // namespace
103 static const int64 kKeyGeneratorInitialNumber =
104 1; // From the IndexedDB specification.
106 enum IndexedDBBackingStoreErrorSource {
107 // 0 - 2 are no longer used.
108 FIND_KEY_IN_INDEX = 3,
109 GET_IDBDATABASE_METADATA,
110 GET_INDEXES,
111 GET_KEY_GENERATOR_CURRENT_NUMBER,
112 GET_OBJECT_STORES,
113 GET_RECORD,
114 KEY_EXISTS_IN_OBJECT_STORE,
115 LOAD_CURRENT_ROW,
116 SET_UP_METADATA,
117 GET_PRIMARY_KEY_VIA_INDEX,
118 KEY_EXISTS_IN_INDEX,
119 VERSION_EXISTS,
120 DELETE_OBJECT_STORE,
121 SET_MAX_OBJECT_STORE_ID,
122 SET_MAX_INDEX_ID,
123 GET_NEW_DATABASE_ID,
124 GET_NEW_VERSION_NUMBER,
125 CREATE_IDBDATABASE_METADATA,
126 DELETE_DATABASE,
127 TRANSACTION_COMMIT_METHOD, // TRANSACTION_COMMIT is a WinNT.h macro
128 GET_DATABASE_NAMES,
129 DELETE_INDEX,
130 CLEAR_OBJECT_STORE,
131 READ_BLOB_JOURNAL,
132 DECODE_BLOB_JOURNAL,
133 GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER,
134 GET_BLOB_INFO_FOR_RECORD,
135 INTERNAL_ERROR_MAX,
138 static void RecordInternalError(const char* type,
139 IndexedDBBackingStoreErrorSource location) {
140 std::string name;
141 name.append("WebCore.IndexedDB.BackingStore.").append(type).append("Error");
142 base::Histogram::FactoryGet(name,
144 INTERNAL_ERROR_MAX,
145 INTERNAL_ERROR_MAX + 1,
146 base::HistogramBase::kUmaTargetedHistogramFlag)
147 ->Add(location);
150 // Use to signal conditions caused by data corruption.
151 // A macro is used instead of an inline function so that the assert and log
152 // report the line number.
153 #define REPORT_ERROR(type, location) \
154 do { \
155 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
156 RecordInternalError(type, location); \
157 } while (0)
159 #define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location)
160 #define INTERNAL_CONSISTENCY_ERROR(location) \
161 REPORT_ERROR("Consistency", location)
162 #define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
164 // Use to signal conditions that usually indicate developer error, but
165 // could be caused by data corruption. A macro is used instead of an
166 // inline function so that the assert and log report the line number.
167 // TODO(cmumford): Improve test coverage so that all error conditions are
168 // "tested" and then delete this macro.
169 #define REPORT_ERROR_UNTESTED(type, location) \
170 do { \
171 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
172 NOTREACHED(); \
173 RecordInternalError(type, location); \
174 } while (0)
176 #define INTERNAL_READ_ERROR_UNTESTED(location) \
177 REPORT_ERROR_UNTESTED("Read", location)
178 #define INTERNAL_CONSISTENCY_ERROR_UNTESTED(location) \
179 REPORT_ERROR_UNTESTED("Consistency", location)
180 #define INTERNAL_WRITE_ERROR_UNTESTED(location) \
181 REPORT_ERROR_UNTESTED("Write", location)
183 static void PutBool(LevelDBTransaction* transaction,
184 const StringPiece& key,
185 bool value) {
186 std::string buffer;
187 EncodeBool(value, &buffer);
188 transaction->Put(key, &buffer);
191 // Was able to use LevelDB to read the data w/o error, but the data read was not
192 // in the expected format.
193 static leveldb::Status InternalInconsistencyStatus() {
194 return leveldb::Status::Corruption("Internal inconsistency");
197 static leveldb::Status InvalidDBKeyStatus() {
198 return leveldb::Status::InvalidArgument("Invalid database key ID");
201 static leveldb::Status IOErrorStatus() {
202 return leveldb::Status::IOError("IO Error");
205 template <typename DBOrTransaction>
206 static leveldb::Status GetInt(DBOrTransaction* db,
207 const StringPiece& key,
208 int64* found_int,
209 bool* found) {
210 std::string result;
211 leveldb::Status s = db->Get(key, &result, found);
212 if (!s.ok())
213 return s;
214 if (!*found)
215 return leveldb::Status::OK();
216 StringPiece slice(result);
217 if (DecodeInt(&slice, found_int) && slice.empty())
218 return s;
219 return InternalInconsistencyStatus();
222 static void PutInt(LevelDBTransaction* transaction,
223 const StringPiece& key,
224 int64 value) {
225 DCHECK_GE(value, 0);
226 std::string buffer;
227 EncodeInt(value, &buffer);
228 transaction->Put(key, &buffer);
231 template <typename DBOrTransaction>
232 WARN_UNUSED_RESULT static leveldb::Status GetVarInt(DBOrTransaction* db,
233 const StringPiece& key,
234 int64* found_int,
235 bool* found) {
236 std::string result;
237 leveldb::Status s = db->Get(key, &result, found);
238 if (!s.ok())
239 return s;
240 if (!*found)
241 return leveldb::Status::OK();
242 StringPiece slice(result);
243 if (DecodeVarInt(&slice, found_int) && slice.empty())
244 return s;
245 return InternalInconsistencyStatus();
248 static void PutVarInt(LevelDBTransaction* transaction,
249 const StringPiece& key,
250 int64 value) {
251 std::string buffer;
252 EncodeVarInt(value, &buffer);
253 transaction->Put(key, &buffer);
256 template <typename DBOrTransaction>
257 WARN_UNUSED_RESULT static leveldb::Status GetString(
258 DBOrTransaction* db,
259 const StringPiece& key,
260 base::string16* found_string,
261 bool* found) {
262 std::string result;
263 *found = false;
264 leveldb::Status s = db->Get(key, &result, found);
265 if (!s.ok())
266 return s;
267 if (!*found)
268 return leveldb::Status::OK();
269 StringPiece slice(result);
270 if (DecodeString(&slice, found_string) && slice.empty())
271 return s;
272 return InternalInconsistencyStatus();
275 static void PutString(LevelDBTransaction* transaction,
276 const StringPiece& key,
277 const base::string16& value) {
278 std::string buffer;
279 EncodeString(value, &buffer);
280 transaction->Put(key, &buffer);
283 static void PutIDBKeyPath(LevelDBTransaction* transaction,
284 const StringPiece& key,
285 const IndexedDBKeyPath& value) {
286 std::string buffer;
287 EncodeIDBKeyPath(value, &buffer);
288 transaction->Put(key, &buffer);
291 static int CompareKeys(const StringPiece& a, const StringPiece& b) {
292 return Compare(a, b, false /*index_keys*/);
295 static int CompareIndexKeys(const StringPiece& a, const StringPiece& b) {
296 return Compare(a, b, true /*index_keys*/);
299 int IndexedDBBackingStore::Comparator::Compare(const StringPiece& a,
300 const StringPiece& b) const {
301 return content::Compare(a, b, false /*index_keys*/);
304 const char* IndexedDBBackingStore::Comparator::Name() const {
305 return "idb_cmp1";
308 // 0 - Initial version.
309 // 1 - Adds UserIntVersion to DatabaseMetaData.
310 // 2 - Adds DataVersion to to global metadata.
311 // 3 - Adds metadata needed for blob support.
312 static const int64 kLatestKnownSchemaVersion = 3;
313 WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
314 int64 db_schema_version = 0;
315 bool found = false;
316 leveldb::Status s =
317 GetInt(db, SchemaVersionKey::Encode(), &db_schema_version, &found);
318 if (!s.ok())
319 return false;
320 if (!found) {
321 *known = true;
322 return true;
324 if (db_schema_version > kLatestKnownSchemaVersion) {
325 *known = false;
326 return true;
329 const uint32 latest_known_data_version =
330 blink::kSerializedScriptValueVersion;
331 int64 db_data_version = 0;
332 s = GetInt(db, DataVersionKey::Encode(), &db_data_version, &found);
333 if (!s.ok())
334 return false;
335 if (!found) {
336 *known = true;
337 return true;
340 if (db_data_version > latest_known_data_version) {
341 *known = false;
342 return true;
345 *known = true;
346 return true;
349 // TODO(ericu): Move this down into the member section of this file. I'm
350 // leaving it here for this CL as it's easier to see the diffs in place.
351 WARN_UNUSED_RESULT leveldb::Status IndexedDBBackingStore::SetUpMetadata() {
352 const uint32 latest_known_data_version =
353 blink::kSerializedScriptValueVersion;
354 const std::string schema_version_key = SchemaVersionKey::Encode();
355 const std::string data_version_key = DataVersionKey::Encode();
357 scoped_refptr<LevelDBTransaction> transaction =
358 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
360 int64 db_schema_version = 0;
361 int64 db_data_version = 0;
362 bool found = false;
363 leveldb::Status s =
364 GetInt(transaction.get(), schema_version_key, &db_schema_version, &found);
365 if (!s.ok()) {
366 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
367 return s;
369 if (!found) {
370 // Initialize new backing store.
371 db_schema_version = kLatestKnownSchemaVersion;
372 PutInt(transaction.get(), schema_version_key, db_schema_version);
373 db_data_version = latest_known_data_version;
374 PutInt(transaction.get(), data_version_key, db_data_version);
375 // If a blob directory already exists for this database, blow it away. It's
376 // leftover from a partially-purged previous generation of data.
377 if (!base::DeleteFile(blob_path_, true)) {
378 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
379 return IOErrorStatus();
381 } else {
382 // Upgrade old backing store.
383 DCHECK_LE(db_schema_version, kLatestKnownSchemaVersion);
384 if (db_schema_version < 1) {
385 db_schema_version = 1;
386 PutInt(transaction.get(), schema_version_key, db_schema_version);
387 const std::string start_key =
388 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
389 const std::string stop_key =
390 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
391 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
392 for (s = it->Seek(start_key);
393 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
394 s = it->Next()) {
395 int64 database_id = 0;
396 found = false;
397 s = GetInt(transaction.get(), it->Key(), &database_id, &found);
398 if (!s.ok()) {
399 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
400 return s;
402 if (!found) {
403 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
404 return InternalInconsistencyStatus();
406 std::string int_version_key = DatabaseMetaDataKey::Encode(
407 database_id, DatabaseMetaDataKey::USER_INT_VERSION);
408 PutVarInt(transaction.get(),
409 int_version_key,
410 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
413 if (s.ok() && db_schema_version < 2) {
414 db_schema_version = 2;
415 PutInt(transaction.get(), schema_version_key, db_schema_version);
416 db_data_version = blink::kSerializedScriptValueVersion;
417 PutInt(transaction.get(), data_version_key, db_data_version);
419 if (db_schema_version < 3) {
420 db_schema_version = 3;
421 if (!base::DeleteFile(blob_path_, true)) {
422 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
423 return IOErrorStatus();
428 if (!s.ok()) {
429 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
430 return s;
433 // All new values will be written using this serialization version.
434 found = false;
435 s = GetInt(transaction.get(), data_version_key, &db_data_version, &found);
436 if (!s.ok()) {
437 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
438 return s;
440 if (!found) {
441 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
442 return InternalInconsistencyStatus();
444 if (db_data_version < latest_known_data_version) {
445 db_data_version = latest_known_data_version;
446 PutInt(transaction.get(), data_version_key, db_data_version);
449 DCHECK_EQ(db_schema_version, kLatestKnownSchemaVersion);
450 DCHECK_EQ(db_data_version, latest_known_data_version);
452 s = transaction->Commit();
453 if (!s.ok())
454 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
455 return s;
458 template <typename DBOrTransaction>
459 WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
460 DBOrTransaction* db,
461 int64 database_id,
462 int64* max_object_store_id) {
463 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
464 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
465 return GetMaxObjectStoreId(db, max_object_store_id_key, max_object_store_id);
468 template <typename DBOrTransaction>
469 WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
470 DBOrTransaction* db,
471 const std::string& max_object_store_id_key,
472 int64* max_object_store_id) {
473 *max_object_store_id = -1;
474 bool found = false;
475 leveldb::Status s =
476 GetInt(db, max_object_store_id_key, max_object_store_id, &found);
477 if (!s.ok())
478 return s;
479 if (!found)
480 *max_object_store_id = 0;
482 DCHECK_GE(*max_object_store_id, 0);
483 return s;
486 class DefaultLevelDBFactory : public LevelDBFactory {
487 public:
488 DefaultLevelDBFactory() {}
489 virtual leveldb::Status OpenLevelDB(const base::FilePath& file_name,
490 const LevelDBComparator* comparator,
491 scoped_ptr<LevelDBDatabase>* db,
492 bool* is_disk_full) OVERRIDE {
493 return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full);
495 virtual leveldb::Status DestroyLevelDB(const base::FilePath& file_name)
496 OVERRIDE {
497 return LevelDBDatabase::Destroy(file_name);
500 private:
501 DISALLOW_COPY_AND_ASSIGN(DefaultLevelDBFactory);
504 static bool GetBlobKeyGeneratorCurrentNumber(
505 LevelDBTransaction* leveldb_transaction,
506 int64 database_id,
507 int64* blob_key_generator_current_number) {
508 const std::string key_gen_key = DatabaseMetaDataKey::Encode(
509 database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
511 // Default to initial number if not found.
512 int64 cur_number = DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber;
513 std::string data;
515 bool found = false;
516 bool ok = leveldb_transaction->Get(key_gen_key, &data, &found).ok();
517 if (!ok) {
518 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
519 return false;
521 if (found) {
522 StringPiece slice(data);
523 if (!DecodeVarInt(&slice, &cur_number) || !slice.empty() ||
524 !DatabaseMetaDataKey::IsValidBlobKey(cur_number)) {
525 INTERNAL_READ_ERROR_UNTESTED(GET_BLOB_KEY_GENERATOR_CURRENT_NUMBER);
526 return false;
529 *blob_key_generator_current_number = cur_number;
530 return true;
533 static bool UpdateBlobKeyGeneratorCurrentNumber(
534 LevelDBTransaction* leveldb_transaction,
535 int64 database_id,
536 int64 blob_key_generator_current_number) {
537 #ifndef NDEBUG
538 int64 old_number;
539 if (!GetBlobKeyGeneratorCurrentNumber(
540 leveldb_transaction, database_id, &old_number))
541 return false;
542 DCHECK_LT(old_number, blob_key_generator_current_number);
543 #endif
544 DCHECK(
545 DatabaseMetaDataKey::IsValidBlobKey(blob_key_generator_current_number));
546 const std::string key = DatabaseMetaDataKey::Encode(
547 database_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER);
549 PutVarInt(leveldb_transaction, key, blob_key_generator_current_number);
550 return true;
553 // TODO(ericu): Error recovery. If we persistently can't read the
554 // blob journal, the safe thing to do is to clear it and leak the blobs,
555 // though that may be costly. Still, database/directory deletion should always
556 // clean things up, and we can write an fsck that will do a full correction if
557 // need be.
558 template <typename T>
559 static leveldb::Status GetBlobJournal(const StringPiece& leveldb_key,
560 T* leveldb_transaction,
561 BlobJournalType* journal) {
562 std::string data;
563 bool found = false;
564 leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
565 if (!s.ok()) {
566 INTERNAL_READ_ERROR(READ_BLOB_JOURNAL);
567 return s;
569 journal->clear();
570 if (!found || !data.size())
571 return leveldb::Status::OK();
572 StringPiece slice(data);
573 if (!DecodeBlobJournal(&slice, journal)) {
574 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DECODE_BLOB_JOURNAL);
575 s = InternalInconsistencyStatus();
577 return s;
580 static void ClearBlobJournal(LevelDBTransaction* leveldb_transaction,
581 const std::string& level_db_key) {
582 leveldb_transaction->Remove(level_db_key);
585 static void UpdatePrimaryJournalWithBlobList(
586 LevelDBTransaction* leveldb_transaction,
587 const BlobJournalType& journal) {
588 const std::string leveldb_key = BlobJournalKey::Encode();
589 std::string data;
590 EncodeBlobJournal(journal, &data);
591 leveldb_transaction->Put(leveldb_key, &data);
594 static void UpdateLiveBlobJournalWithBlobList(
595 LevelDBTransaction* leveldb_transaction,
596 const BlobJournalType& journal) {
597 const std::string leveldb_key = LiveBlobJournalKey::Encode();
598 std::string data;
599 EncodeBlobJournal(journal, &data);
600 leveldb_transaction->Put(leveldb_key, &data);
603 static leveldb::Status MergeBlobsIntoLiveBlobJournal(
604 LevelDBTransaction* leveldb_transaction,
605 const BlobJournalType& journal) {
606 BlobJournalType old_journal;
607 const std::string key = LiveBlobJournalKey::Encode();
608 leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &old_journal);
609 if (!s.ok())
610 return s;
612 old_journal.insert(old_journal.end(), journal.begin(), journal.end());
614 UpdateLiveBlobJournalWithBlobList(leveldb_transaction, old_journal);
615 return leveldb::Status::OK();
618 static void UpdateBlobJournalWithDatabase(
619 LevelDBDirectTransaction* leveldb_transaction,
620 int64 database_id) {
621 BlobJournalType journal;
622 journal.push_back(
623 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
624 const std::string key = BlobJournalKey::Encode();
625 std::string data;
626 EncodeBlobJournal(journal, &data);
627 leveldb_transaction->Put(key, &data);
630 static leveldb::Status MergeDatabaseIntoLiveBlobJournal(
631 LevelDBDirectTransaction* leveldb_transaction,
632 int64 database_id) {
633 BlobJournalType journal;
634 const std::string key = LiveBlobJournalKey::Encode();
635 leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &journal);
636 if (!s.ok())
637 return s;
638 journal.push_back(
639 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
640 std::string data;
641 EncodeBlobJournal(journal, &data);
642 leveldb_transaction->Put(key, &data);
643 return leveldb::Status::OK();
646 // Blob Data is encoded as a series of:
647 // { is_file [bool], key [int64 as varInt],
648 // type [string-with-length, may be empty],
649 // (for Blobs only) size [int64 as varInt]
650 // (for Files only) fileName [string-with-length]
651 // }
652 // There is no length field; just read until you run out of data.
653 static std::string EncodeBlobData(
654 const std::vector<IndexedDBBlobInfo*>& blob_info) {
655 std::string ret;
656 std::vector<IndexedDBBlobInfo*>::const_iterator iter;
657 for (iter = blob_info.begin(); iter != blob_info.end(); ++iter) {
658 const IndexedDBBlobInfo& info = **iter;
659 EncodeBool(info.is_file(), &ret);
660 EncodeVarInt(info.key(), &ret);
661 EncodeStringWithLength(info.type(), &ret);
662 if (info.is_file())
663 EncodeStringWithLength(info.file_name(), &ret);
664 else
665 EncodeVarInt(info.size(), &ret);
667 return ret;
670 static bool DecodeBlobData(const std::string& data,
671 std::vector<IndexedDBBlobInfo>* output) {
672 std::vector<IndexedDBBlobInfo> ret;
673 output->clear();
674 StringPiece slice(data);
675 while (!slice.empty()) {
676 bool is_file;
677 int64 key;
678 base::string16 type;
679 int64 size;
680 base::string16 file_name;
682 if (!DecodeBool(&slice, &is_file))
683 return false;
684 if (!DecodeVarInt(&slice, &key) ||
685 !DatabaseMetaDataKey::IsValidBlobKey(key))
686 return false;
687 if (!DecodeStringWithLength(&slice, &type))
688 return false;
689 if (is_file) {
690 if (!DecodeStringWithLength(&slice, &file_name))
691 return false;
692 ret.push_back(IndexedDBBlobInfo(key, type, file_name));
693 } else {
694 if (!DecodeVarInt(&slice, &size) || size < 0)
695 return false;
696 ret.push_back(IndexedDBBlobInfo(type, static_cast<uint64>(size), key));
699 output->swap(ret);
701 return true;
704 IndexedDBBackingStore::IndexedDBBackingStore(
705 IndexedDBFactory* indexed_db_factory,
706 const GURL& origin_url,
707 const base::FilePath& blob_path,
708 net::URLRequestContext* request_context,
709 scoped_ptr<LevelDBDatabase> db,
710 scoped_ptr<LevelDBComparator> comparator,
711 base::SequencedTaskRunner* task_runner)
712 : indexed_db_factory_(indexed_db_factory),
713 origin_url_(origin_url),
714 blob_path_(blob_path),
715 origin_identifier_(ComputeOriginIdentifier(origin_url)),
716 request_context_(request_context),
717 task_runner_(task_runner),
718 db_(db.Pass()),
719 comparator_(comparator.Pass()),
720 active_blob_registry_(this) {
723 IndexedDBBackingStore::~IndexedDBBackingStore() {
724 if (!blob_path_.empty() && !child_process_ids_granted_.empty()) {
725 ChildProcessSecurityPolicyImpl* policy =
726 ChildProcessSecurityPolicyImpl::GetInstance();
727 std::set<int>::const_iterator iter;
728 for (iter = child_process_ids_granted_.begin();
729 iter != child_process_ids_granted_.end();
730 ++iter) {
731 policy->RevokeAllPermissionsForFile(*iter, blob_path_);
734 STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
735 incognito_blob_map_.end());
736 // db_'s destructor uses comparator_. The order of destruction is important.
737 db_.reset();
738 comparator_.reset();
741 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
742 const std::string& primary_key,
743 int64 version)
744 : primary_key_(primary_key), version_(version) {
745 DCHECK(!primary_key.empty());
747 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
748 : primary_key_(), version_(-1) {}
749 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
751 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
752 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
754 enum IndexedDBBackingStoreOpenResult {
755 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS,
756 INDEXED_DB_BACKING_STORE_OPEN_SUCCESS,
757 INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
758 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
759 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
760 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
761 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
762 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
763 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR,
764 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
765 INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
766 INDEXED_DB_BACKING_STORE_OPEN_DISK_FULL_DEPRECATED,
767 INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
768 INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
769 INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
770 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR,
771 INDEXED_DB_BACKING_STORE_OPEN_MAX,
774 // static
775 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
776 IndexedDBFactory* indexed_db_factory,
777 const GURL& origin_url,
778 const base::FilePath& path_base,
779 net::URLRequestContext* request_context,
780 blink::WebIDBDataLoss* data_loss,
781 std::string* data_loss_message,
782 bool* disk_full,
783 base::SequencedTaskRunner* task_runner,
784 bool clean_journal,
785 leveldb::Status* status) {
786 *data_loss = blink::WebIDBDataLossNone;
787 DefaultLevelDBFactory leveldb_factory;
788 return IndexedDBBackingStore::Open(indexed_db_factory,
789 origin_url,
790 path_base,
791 request_context,
792 data_loss,
793 data_loss_message,
794 disk_full,
795 &leveldb_factory,
796 task_runner,
797 clean_journal,
798 status);
801 static std::string OriginToCustomHistogramSuffix(const GURL& origin_url) {
802 if (origin_url.host() == "docs.google.com")
803 return ".Docs";
804 return std::string();
807 static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result,
808 const GURL& origin_url) {
809 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.BackingStore.OpenStatus",
810 result,
811 INDEXED_DB_BACKING_STORE_OPEN_MAX);
812 const std::string suffix = OriginToCustomHistogramSuffix(origin_url);
813 // Data from the WebCore.IndexedDB.BackingStore.OpenStatus histogram is used
814 // to generate a graph. So as not to alter the meaning of that graph,
815 // continue to collect all stats there (above) but also now collect docs stats
816 // separately (below).
817 if (!suffix.empty()) {
818 base::LinearHistogram::FactoryGet(
819 "WebCore.IndexedDB.BackingStore.OpenStatus" + suffix,
821 INDEXED_DB_BACKING_STORE_OPEN_MAX,
822 INDEXED_DB_BACKING_STORE_OPEN_MAX + 1,
823 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(result);
827 static bool IsPathTooLong(const base::FilePath& leveldb_dir) {
828 int limit = base::GetMaximumPathComponentLength(leveldb_dir.DirName());
829 if (limit == -1) {
830 DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
831 // In limited testing, ChromeOS returns 143, other OSes 255.
832 #if defined(OS_CHROMEOS)
833 limit = 143;
834 #else
835 limit = 255;
836 #endif
838 size_t component_length = leveldb_dir.BaseName().value().length();
839 if (component_length > static_cast<uint32_t>(limit)) {
840 DLOG(WARNING) << "Path component length (" << component_length
841 << ") exceeds maximum (" << limit
842 << ") allowed by this filesystem.";
843 const int min = 140;
844 const int max = 300;
845 const int num_buckets = 12;
846 UMA_HISTOGRAM_CUSTOM_COUNTS(
847 "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
848 component_length,
849 min,
850 max,
851 num_buckets);
852 return true;
854 return false;
857 leveldb::Status IndexedDBBackingStore::DestroyBackingStore(
858 const base::FilePath& path_base,
859 const GURL& origin_url) {
860 const base::FilePath file_path =
861 path_base.Append(ComputeFileName(origin_url));
862 DefaultLevelDBFactory leveldb_factory;
863 return leveldb_factory.DestroyLevelDB(file_path);
866 bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath& path_base,
867 const GURL& origin_url,
868 std::string* message) {
869 const base::FilePath info_path =
870 path_base.Append(ComputeCorruptionFileName(origin_url));
872 if (IsPathTooLong(info_path))
873 return false;
875 const int64 max_json_len = 4096;
876 int64 file_size(0);
877 if (!GetFileSize(info_path, &file_size) || file_size > max_json_len)
878 return false;
879 if (!file_size) {
880 NOTREACHED();
881 return false;
884 base::File file(info_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
885 bool success = false;
886 if (file.IsValid()) {
887 std::vector<char> bytes(file_size);
888 if (file_size == file.Read(0, &bytes[0], file_size)) {
889 std::string input_js(&bytes[0], file_size);
890 base::JSONReader reader;
891 scoped_ptr<base::Value> val(reader.ReadToValue(input_js));
892 if (val && val->GetType() == base::Value::TYPE_DICTIONARY) {
893 base::DictionaryValue* dict_val =
894 static_cast<base::DictionaryValue*>(val.get());
895 success = dict_val->GetString("message", message);
898 file.Close();
901 base::DeleteFile(info_path, false);
903 return success;
906 bool IndexedDBBackingStore::RecordCorruptionInfo(
907 const base::FilePath& path_base,
908 const GURL& origin_url,
909 const std::string& message) {
910 const base::FilePath info_path =
911 path_base.Append(ComputeCorruptionFileName(origin_url));
912 if (IsPathTooLong(info_path))
913 return false;
915 base::DictionaryValue root_dict;
916 root_dict.SetString("message", message);
917 std::string output_js;
918 base::JSONWriter::Write(&root_dict, &output_js);
920 base::File file(info_path,
921 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
922 if (!file.IsValid())
923 return false;
924 int written = file.Write(0, output_js.c_str(), output_js.length());
925 return size_t(written) == output_js.length();
928 // static
929 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
930 IndexedDBFactory* indexed_db_factory,
931 const GURL& origin_url,
932 const base::FilePath& path_base,
933 net::URLRequestContext* request_context,
934 blink::WebIDBDataLoss* data_loss,
935 std::string* data_loss_message,
936 bool* is_disk_full,
937 LevelDBFactory* leveldb_factory,
938 base::SequencedTaskRunner* task_runner,
939 bool clean_journal,
940 leveldb::Status* status) {
941 IDB_TRACE("IndexedDBBackingStore::Open");
942 DCHECK(!path_base.empty());
943 *data_loss = blink::WebIDBDataLossNone;
944 *data_loss_message = "";
945 *is_disk_full = false;
947 *status = leveldb::Status::OK();
949 scoped_ptr<LevelDBComparator> comparator(new Comparator());
951 if (!base::IsStringASCII(path_base.AsUTF8Unsafe())) {
952 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
953 origin_url);
955 if (!base::CreateDirectory(path_base)) {
956 *status =
957 leveldb::Status::IOError("Unable to create IndexedDB database path");
958 LOG(ERROR) << status->ToString() << ": \"" << path_base.AsUTF8Unsafe()
959 << "\"";
960 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
961 origin_url);
962 return scoped_refptr<IndexedDBBackingStore>();
965 const base::FilePath file_path =
966 path_base.Append(ComputeFileName(origin_url));
967 const base::FilePath blob_path =
968 path_base.Append(ComputeBlobPath(origin_url));
970 if (IsPathTooLong(file_path)) {
971 *status = leveldb::Status::IOError("File path too long");
972 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
973 origin_url);
974 return scoped_refptr<IndexedDBBackingStore>();
977 scoped_ptr<LevelDBDatabase> db;
978 *status = leveldb_factory->OpenLevelDB(
979 file_path, comparator.get(), &db, is_disk_full);
981 DCHECK(!db == !status->ok());
982 if (!status->ok()) {
983 if (leveldb_env::IndicatesDiskFull(*status)) {
984 *is_disk_full = true;
985 } else if (leveldb_env::IsCorruption(*status)) {
986 *data_loss = blink::WebIDBDataLossTotal;
987 *data_loss_message = leveldb_env::GetCorruptionMessage(*status);
991 bool is_schema_known = false;
992 if (db) {
993 std::string corruption_message;
994 if (ReadCorruptionInfo(path_base, origin_url, &corruption_message)) {
995 LOG(ERROR) << "IndexedDB recovering from a corrupted (and deleted) "
996 "database.";
997 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
998 origin_url);
999 db.reset();
1000 *data_loss = blink::WebIDBDataLossTotal;
1001 *data_loss_message =
1002 "IndexedDB (database was corrupt): " + corruption_message;
1003 } else if (!IsSchemaKnown(db.get(), &is_schema_known)) {
1004 LOG(ERROR) << "IndexedDB had IO error checking schema, treating it as "
1005 "failure to open";
1006 HistogramOpenStatus(
1007 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
1008 origin_url);
1009 db.reset();
1010 *data_loss = blink::WebIDBDataLossTotal;
1011 *data_loss_message = "I/O error checking schema";
1012 } else if (!is_schema_known) {
1013 LOG(ERROR) << "IndexedDB backing store had unknown schema, treating it "
1014 "as failure to open";
1015 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
1016 origin_url);
1017 db.reset();
1018 *data_loss = blink::WebIDBDataLossTotal;
1019 *data_loss_message = "Unknown schema";
1023 DCHECK(status->ok() || !is_schema_known || leveldb_env::IsIOError(*status) ||
1024 leveldb_env::IsCorruption(*status));
1026 if (db) {
1027 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_SUCCESS, origin_url);
1028 } else if (leveldb_env::IsIOError(*status)) {
1029 LOG(ERROR) << "Unable to open backing store, not trying to recover - "
1030 << status->ToString();
1031 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY, origin_url);
1032 return scoped_refptr<IndexedDBBackingStore>();
1033 } else {
1034 DCHECK(!is_schema_known || leveldb_env::IsCorruption(*status));
1035 LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup";
1036 *status = leveldb_factory->DestroyLevelDB(file_path);
1037 if (!status->ok()) {
1038 LOG(ERROR) << "IndexedDB backing store cleanup failed";
1039 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
1040 origin_url);
1041 return scoped_refptr<IndexedDBBackingStore>();
1044 LOG(ERROR) << "IndexedDB backing store cleanup succeeded, reopening";
1045 leveldb_factory->OpenLevelDB(file_path, comparator.get(), &db, NULL);
1046 if (!db) {
1047 LOG(ERROR) << "IndexedDB backing store reopen after recovery failed";
1048 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
1049 origin_url);
1050 return scoped_refptr<IndexedDBBackingStore>();
1052 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
1053 origin_url);
1056 if (!db) {
1057 NOTREACHED();
1058 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR,
1059 origin_url);
1060 return scoped_refptr<IndexedDBBackingStore>();
1063 scoped_refptr<IndexedDBBackingStore> backing_store =
1064 Create(indexed_db_factory,
1065 origin_url,
1066 blob_path,
1067 request_context,
1068 db.Pass(),
1069 comparator.Pass(),
1070 task_runner,
1071 status);
1073 if (clean_journal && backing_store.get() &&
1074 !backing_store->CleanUpBlobJournal(LiveBlobJournalKey::Encode()).ok()) {
1075 HistogramOpenStatus(
1076 INDEXED_DB_BACKING_STORE_OPEN_FAILED_CLEANUP_JOURNAL_ERROR, origin_url);
1077 return scoped_refptr<IndexedDBBackingStore>();
1079 return backing_store;
1082 // static
1083 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
1084 const GURL& origin_url,
1085 base::SequencedTaskRunner* task_runner,
1086 leveldb::Status* status) {
1087 DefaultLevelDBFactory leveldb_factory;
1088 return IndexedDBBackingStore::OpenInMemory(
1089 origin_url, &leveldb_factory, task_runner, status);
1092 // static
1093 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
1094 const GURL& origin_url,
1095 LevelDBFactory* leveldb_factory,
1096 base::SequencedTaskRunner* task_runner,
1097 leveldb::Status* status) {
1098 IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
1100 scoped_ptr<LevelDBComparator> comparator(new Comparator());
1101 scoped_ptr<LevelDBDatabase> db =
1102 LevelDBDatabase::OpenInMemory(comparator.get());
1103 if (!db) {
1104 LOG(ERROR) << "LevelDBDatabase::OpenInMemory failed.";
1105 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
1106 origin_url);
1107 return scoped_refptr<IndexedDBBackingStore>();
1109 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS, origin_url);
1111 return Create(NULL /* indexed_db_factory */,
1112 origin_url,
1113 base::FilePath(),
1114 NULL /* request_context */,
1115 db.Pass(),
1116 comparator.Pass(),
1117 task_runner,
1118 status);
1121 // static
1122 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
1123 IndexedDBFactory* indexed_db_factory,
1124 const GURL& origin_url,
1125 const base::FilePath& blob_path,
1126 net::URLRequestContext* request_context,
1127 scoped_ptr<LevelDBDatabase> db,
1128 scoped_ptr<LevelDBComparator> comparator,
1129 base::SequencedTaskRunner* task_runner,
1130 leveldb::Status* status) {
1131 // TODO(jsbell): Handle comparator name changes.
1132 scoped_refptr<IndexedDBBackingStore> backing_store(
1133 new IndexedDBBackingStore(indexed_db_factory,
1134 origin_url,
1135 blob_path,
1136 request_context,
1137 db.Pass(),
1138 comparator.Pass(),
1139 task_runner));
1140 *status = backing_store->SetUpMetadata();
1141 if (!status->ok())
1142 return scoped_refptr<IndexedDBBackingStore>();
1144 return backing_store;
1147 void IndexedDBBackingStore::GrantChildProcessPermissions(int child_process_id) {
1148 if (!child_process_ids_granted_.count(child_process_id)) {
1149 child_process_ids_granted_.insert(child_process_id);
1150 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
1151 child_process_id, blob_path_);
1155 std::vector<base::string16> IndexedDBBackingStore::GetDatabaseNames(
1156 leveldb::Status* s) {
1157 *s = leveldb::Status::OK();
1158 std::vector<base::string16> found_names;
1159 const std::string start_key =
1160 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
1161 const std::string stop_key =
1162 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
1164 DCHECK(found_names.empty());
1166 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1167 for (*s = it->Seek(start_key);
1168 s->ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1169 *s = it->Next()) {
1170 // Decode database name (in iterator key).
1171 StringPiece slice(it->Key());
1172 DatabaseNameKey database_name_key;
1173 if (!DatabaseNameKey::Decode(&slice, &database_name_key) ||
1174 !slice.empty()) {
1175 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
1176 continue;
1179 // Decode database id (in iterator value).
1180 int64 database_id = 0;
1181 StringPiece valueSlice(it->Value());
1182 if (!DecodeInt(&valueSlice, &database_id) || !valueSlice.empty()) {
1183 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
1184 continue;
1187 // Look up version by id.
1188 bool found = false;
1189 int64 database_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1190 *s = GetVarInt(db_.get(),
1191 DatabaseMetaDataKey::Encode(
1192 database_id, DatabaseMetaDataKey::USER_INT_VERSION),
1193 &database_version,
1194 &found);
1195 if (!s->ok() || !found) {
1196 INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES);
1197 continue;
1200 // Ignore stale metadata from failed initial opens.
1201 if (database_version != IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
1202 found_names.push_back(database_name_key.database_name());
1205 if (!s->ok())
1206 INTERNAL_READ_ERROR(GET_DATABASE_NAMES);
1208 return found_names;
1211 leveldb::Status IndexedDBBackingStore::GetIDBDatabaseMetaData(
1212 const base::string16& name,
1213 IndexedDBDatabaseMetadata* metadata,
1214 bool* found) {
1215 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1216 *found = false;
1218 leveldb::Status s = GetInt(db_.get(), key, &metadata->id, found);
1219 if (!s.ok()) {
1220 INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
1221 return s;
1223 if (!*found)
1224 return leveldb::Status::OK();
1226 s = GetString(db_.get(),
1227 DatabaseMetaDataKey::Encode(metadata->id,
1228 DatabaseMetaDataKey::USER_VERSION),
1229 &metadata->version,
1230 found);
1231 if (!s.ok()) {
1232 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1233 return s;
1235 if (!*found) {
1236 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1237 return InternalInconsistencyStatus();
1240 s = GetVarInt(db_.get(),
1241 DatabaseMetaDataKey::Encode(
1242 metadata->id, DatabaseMetaDataKey::USER_INT_VERSION),
1243 &metadata->int_version,
1244 found);
1245 if (!s.ok()) {
1246 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1247 return s;
1249 if (!*found) {
1250 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1251 return InternalInconsistencyStatus();
1254 if (metadata->int_version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
1255 metadata->int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1257 s = GetMaxObjectStoreId(
1258 db_.get(), metadata->id, &metadata->max_object_store_id);
1259 if (!s.ok()) {
1260 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1263 // We don't cache this, we just check it if it's there.
1264 int64 blob_key_generator_current_number =
1265 DatabaseMetaDataKey::kInvalidBlobKey;
1267 s = GetVarInt(
1268 db_.get(),
1269 DatabaseMetaDataKey::Encode(
1270 metadata->id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
1271 &blob_key_generator_current_number,
1272 found);
1273 if (!s.ok()) {
1274 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1275 return s;
1277 if (!*found) {
1278 // This database predates blob support.
1279 *found = true;
1280 } else if (!DatabaseMetaDataKey::IsValidBlobKey(
1281 blob_key_generator_current_number)) {
1282 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1283 return InternalInconsistencyStatus();
1286 return s;
1289 WARN_UNUSED_RESULT static leveldb::Status GetNewDatabaseId(
1290 LevelDBTransaction* transaction,
1291 int64* new_id) {
1292 *new_id = -1;
1293 int64 max_database_id = -1;
1294 bool found = false;
1295 leveldb::Status s =
1296 GetInt(transaction, MaxDatabaseIdKey::Encode(), &max_database_id, &found);
1297 if (!s.ok()) {
1298 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID);
1299 return s;
1301 if (!found)
1302 max_database_id = 0;
1304 DCHECK_GE(max_database_id, 0);
1306 int64 database_id = max_database_id + 1;
1307 PutInt(transaction, MaxDatabaseIdKey::Encode(), database_id);
1308 *new_id = database_id;
1309 return leveldb::Status::OK();
1312 leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData(
1313 const base::string16& name,
1314 const base::string16& version,
1315 int64 int_version,
1316 int64* row_id) {
1317 // TODO(jsbell): Don't persist metadata if open fails. http://crbug.com/395472
1318 scoped_refptr<LevelDBTransaction> transaction =
1319 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
1321 leveldb::Status s = GetNewDatabaseId(transaction.get(), row_id);
1322 if (!s.ok())
1323 return s;
1324 DCHECK_GE(*row_id, 0);
1326 if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1327 int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1329 PutInt(transaction.get(),
1330 DatabaseNameKey::Encode(origin_identifier_, name),
1331 *row_id);
1332 PutString(
1333 transaction.get(),
1334 DatabaseMetaDataKey::Encode(*row_id, DatabaseMetaDataKey::USER_VERSION),
1335 version);
1336 PutVarInt(transaction.get(),
1337 DatabaseMetaDataKey::Encode(*row_id,
1338 DatabaseMetaDataKey::USER_INT_VERSION),
1339 int_version);
1340 PutVarInt(
1341 transaction.get(),
1342 DatabaseMetaDataKey::Encode(
1343 *row_id, DatabaseMetaDataKey::BLOB_KEY_GENERATOR_CURRENT_NUMBER),
1344 DatabaseMetaDataKey::kBlobKeyGeneratorInitialNumber);
1346 s = transaction->Commit();
1347 if (!s.ok())
1348 INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA);
1349 return s;
1352 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
1353 IndexedDBBackingStore::Transaction* transaction,
1354 int64 row_id,
1355 int64 int_version) {
1356 if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1357 int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1358 DCHECK_GE(int_version, 0) << "int_version was " << int_version;
1359 PutVarInt(transaction->transaction(),
1360 DatabaseMetaDataKey::Encode(row_id,
1361 DatabaseMetaDataKey::USER_INT_VERSION),
1362 int_version);
1363 return true;
1366 // If you're deleting a range that contains user keys that have blob info, this
1367 // won't clean up the blobs.
1368 static leveldb::Status DeleteRangeBasic(LevelDBTransaction* transaction,
1369 const std::string& begin,
1370 const std::string& end,
1371 bool upper_open) {
1372 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
1373 leveldb::Status s;
1374 for (s = it->Seek(begin); s.ok() && it->IsValid() &&
1375 (upper_open ? CompareKeys(it->Key(), end) < 0
1376 : CompareKeys(it->Key(), end) <= 0);
1377 s = it->Next())
1378 transaction->Remove(it->Key());
1379 return s;
1382 static leveldb::Status DeleteBlobsInRange(
1383 IndexedDBBackingStore::Transaction* transaction,
1384 int64 database_id,
1385 int64 object_store_id,
1386 const std::string& start_key,
1387 const std::string& end_key,
1388 bool upper_open) {
1389 scoped_ptr<LevelDBIterator> it = transaction->transaction()->CreateIterator();
1390 leveldb::Status s = it->Seek(start_key);
1391 for (; s.ok() && it->IsValid() &&
1392 (upper_open ? CompareKeys(it->Key(), end_key) < 0
1393 : CompareKeys(it->Key(), end_key) <= 0);
1394 s = it->Next()) {
1395 StringPiece key_piece(it->Key());
1396 std::string user_key =
1397 BlobEntryKey::ReencodeToObjectStoreDataKey(&key_piece);
1398 if (!user_key.size()) {
1399 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1400 return InternalInconsistencyStatus();
1402 transaction->PutBlobInfo(
1403 database_id, object_store_id, user_key, NULL, NULL);
1405 return s;
1408 static leveldb::Status DeleteBlobsInObjectStore(
1409 IndexedDBBackingStore::Transaction* transaction,
1410 int64 database_id,
1411 int64 object_store_id) {
1412 std::string start_key, stop_key;
1413 start_key =
1414 BlobEntryKey::EncodeMinKeyForObjectStore(database_id, object_store_id);
1415 stop_key =
1416 BlobEntryKey::EncodeStopKeyForObjectStore(database_id, object_store_id);
1417 return DeleteBlobsInRange(
1418 transaction, database_id, object_store_id, start_key, stop_key, true);
1421 leveldb::Status IndexedDBBackingStore::DeleteDatabase(
1422 const base::string16& name) {
1423 IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
1424 scoped_ptr<LevelDBDirectTransaction> transaction =
1425 LevelDBDirectTransaction::Create(db_.get());
1427 leveldb::Status s;
1428 s = CleanUpBlobJournal(BlobJournalKey::Encode());
1429 if (!s.ok())
1430 return s;
1432 IndexedDBDatabaseMetadata metadata;
1433 bool success = false;
1434 s = GetIDBDatabaseMetaData(name, &metadata, &success);
1435 if (!s.ok())
1436 return s;
1437 if (!success)
1438 return leveldb::Status::OK();
1440 const std::string start_key = DatabaseMetaDataKey::Encode(
1441 metadata.id, DatabaseMetaDataKey::ORIGIN_NAME);
1442 const std::string stop_key = DatabaseMetaDataKey::Encode(
1443 metadata.id + 1, DatabaseMetaDataKey::ORIGIN_NAME);
1444 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1445 for (s = it->Seek(start_key);
1446 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1447 s = it->Next())
1448 transaction->Remove(it->Key());
1449 if (!s.ok()) {
1450 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1451 return s;
1454 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1455 transaction->Remove(key);
1457 bool need_cleanup = false;
1458 if (active_blob_registry()->MarkDeletedCheckIfUsed(
1459 metadata.id, DatabaseMetaDataKey::kAllBlobsKey)) {
1460 s = MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id);
1461 if (!s.ok())
1462 return s;
1463 } else {
1464 UpdateBlobJournalWithDatabase(transaction.get(), metadata.id);
1465 need_cleanup = true;
1468 s = transaction->Commit();
1469 if (!s.ok()) {
1470 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1471 return s;
1474 if (need_cleanup)
1475 CleanUpBlobJournal(BlobJournalKey::Encode());
1477 db_->Compact(start_key, stop_key);
1478 return s;
1481 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it,
1482 const std::string& stop_key,
1483 int64 object_store_id,
1484 int64 meta_data_type) {
1485 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
1486 return false;
1488 StringPiece slice(it->Key());
1489 ObjectStoreMetaDataKey meta_data_key;
1490 bool ok =
1491 ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
1492 DCHECK(ok);
1493 if (meta_data_key.ObjectStoreId() != object_store_id)
1494 return false;
1495 if (meta_data_key.MetaDataType() != meta_data_type)
1496 return false;
1497 return ok;
1500 // TODO(jsbell): This should do some error handling rather than
1501 // plowing ahead when bad data is encountered.
1502 leveldb::Status IndexedDBBackingStore::GetObjectStores(
1503 int64 database_id,
1504 IndexedDBDatabaseMetadata::ObjectStoreMap* object_stores) {
1505 IDB_TRACE("IndexedDBBackingStore::GetObjectStores");
1506 if (!KeyPrefix::IsValidDatabaseId(database_id))
1507 return InvalidDBKeyStatus();
1508 const std::string start_key =
1509 ObjectStoreMetaDataKey::Encode(database_id, 1, 0);
1510 const std::string stop_key =
1511 ObjectStoreMetaDataKey::EncodeMaxKey(database_id);
1513 DCHECK(object_stores->empty());
1515 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1516 leveldb::Status s = it->Seek(start_key);
1517 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
1518 StringPiece slice(it->Key());
1519 ObjectStoreMetaDataKey meta_data_key;
1520 bool ok =
1521 ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key) && slice.empty();
1522 DCHECK(ok);
1523 if (!ok || meta_data_key.MetaDataType() != ObjectStoreMetaDataKey::NAME) {
1524 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1525 // Possible stale metadata, but don't fail the load.
1526 s = it->Next();
1527 if (!s.ok())
1528 break;
1529 continue;
1532 int64 object_store_id = meta_data_key.ObjectStoreId();
1534 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
1535 // simplify.
1536 base::string16 object_store_name;
1538 StringPiece slice(it->Value());
1539 if (!DecodeString(&slice, &object_store_name) || !slice.empty())
1540 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1543 s = it->Next();
1544 if (!s.ok())
1545 break;
1546 if (!CheckObjectStoreAndMetaDataType(it.get(),
1547 stop_key,
1548 object_store_id,
1549 ObjectStoreMetaDataKey::KEY_PATH)) {
1550 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1551 break;
1553 IndexedDBKeyPath key_path;
1555 StringPiece slice(it->Value());
1556 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
1557 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1560 s = it->Next();
1561 if (!s.ok())
1562 break;
1563 if (!CheckObjectStoreAndMetaDataType(
1564 it.get(),
1565 stop_key,
1566 object_store_id,
1567 ObjectStoreMetaDataKey::AUTO_INCREMENT)) {
1568 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1569 break;
1571 bool auto_increment;
1573 StringPiece slice(it->Value());
1574 if (!DecodeBool(&slice, &auto_increment) || !slice.empty())
1575 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1578 s = it->Next(); // Is evictable.
1579 if (!s.ok())
1580 break;
1581 if (!CheckObjectStoreAndMetaDataType(it.get(),
1582 stop_key,
1583 object_store_id,
1584 ObjectStoreMetaDataKey::EVICTABLE)) {
1585 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1586 break;
1589 s = it->Next(); // Last version.
1590 if (!s.ok())
1591 break;
1592 if (!CheckObjectStoreAndMetaDataType(
1593 it.get(),
1594 stop_key,
1595 object_store_id,
1596 ObjectStoreMetaDataKey::LAST_VERSION)) {
1597 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1598 break;
1601 s = it->Next(); // Maximum index id allocated.
1602 if (!s.ok())
1603 break;
1604 if (!CheckObjectStoreAndMetaDataType(
1605 it.get(),
1606 stop_key,
1607 object_store_id,
1608 ObjectStoreMetaDataKey::MAX_INDEX_ID)) {
1609 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1610 break;
1612 int64 max_index_id;
1614 StringPiece slice(it->Value());
1615 if (!DecodeInt(&slice, &max_index_id) || !slice.empty())
1616 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1619 s = it->Next(); // [optional] has key path (is not null)
1620 if (!s.ok())
1621 break;
1622 if (CheckObjectStoreAndMetaDataType(it.get(),
1623 stop_key,
1624 object_store_id,
1625 ObjectStoreMetaDataKey::HAS_KEY_PATH)) {
1626 bool has_key_path;
1628 StringPiece slice(it->Value());
1629 if (!DecodeBool(&slice, &has_key_path))
1630 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1632 // This check accounts for two layers of legacy coding:
1633 // (1) Initially, has_key_path was added to distinguish null vs. string.
1634 // (2) Later, null vs. string vs. array was stored in the key_path itself.
1635 // So this check is only relevant for string-type key_paths.
1636 if (!has_key_path &&
1637 (key_path.type() == blink::WebIDBKeyPathTypeString &&
1638 !key_path.string().empty())) {
1639 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1640 break;
1642 if (!has_key_path)
1643 key_path = IndexedDBKeyPath();
1644 s = it->Next();
1645 if (!s.ok())
1646 break;
1649 int64 key_generator_current_number = -1;
1650 if (CheckObjectStoreAndMetaDataType(
1651 it.get(),
1652 stop_key,
1653 object_store_id,
1654 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER)) {
1655 StringPiece slice(it->Value());
1656 if (!DecodeInt(&slice, &key_generator_current_number) || !slice.empty())
1657 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1659 // TODO(jsbell): Return key_generator_current_number, cache in
1660 // object store, and write lazily to backing store. For now,
1661 // just assert that if it was written it was valid.
1662 DCHECK_GE(key_generator_current_number, kKeyGeneratorInitialNumber);
1663 s = it->Next();
1664 if (!s.ok())
1665 break;
1668 IndexedDBObjectStoreMetadata metadata(object_store_name,
1669 object_store_id,
1670 key_path,
1671 auto_increment,
1672 max_index_id);
1673 s = GetIndexes(database_id, object_store_id, &metadata.indexes);
1674 if (!s.ok())
1675 break;
1676 (*object_stores)[object_store_id] = metadata;
1679 if (!s.ok())
1680 INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES);
1682 return s;
1685 WARN_UNUSED_RESULT static leveldb::Status SetMaxObjectStoreId(
1686 LevelDBTransaction* transaction,
1687 int64 database_id,
1688 int64 object_store_id) {
1689 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
1690 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
1691 int64 max_object_store_id = -1;
1692 leveldb::Status s = GetMaxObjectStoreId(
1693 transaction, max_object_store_id_key, &max_object_store_id);
1694 if (!s.ok()) {
1695 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1696 return s;
1699 if (object_store_id <= max_object_store_id) {
1700 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1701 return InternalInconsistencyStatus();
1703 PutInt(transaction, max_object_store_id_key, object_store_id);
1704 return s;
1707 void IndexedDBBackingStore::Compact() { db_->CompactAll(); }
1709 leveldb::Status IndexedDBBackingStore::CreateObjectStore(
1710 IndexedDBBackingStore::Transaction* transaction,
1711 int64 database_id,
1712 int64 object_store_id,
1713 const base::string16& name,
1714 const IndexedDBKeyPath& key_path,
1715 bool auto_increment) {
1716 IDB_TRACE("IndexedDBBackingStore::CreateObjectStore");
1717 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1718 return InvalidDBKeyStatus();
1719 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1720 leveldb::Status s =
1721 SetMaxObjectStoreId(leveldb_transaction, database_id, object_store_id);
1722 if (!s.ok())
1723 return s;
1725 const std::string name_key = ObjectStoreMetaDataKey::Encode(
1726 database_id, object_store_id, ObjectStoreMetaDataKey::NAME);
1727 const std::string key_path_key = ObjectStoreMetaDataKey::Encode(
1728 database_id, object_store_id, ObjectStoreMetaDataKey::KEY_PATH);
1729 const std::string auto_increment_key = ObjectStoreMetaDataKey::Encode(
1730 database_id, object_store_id, ObjectStoreMetaDataKey::AUTO_INCREMENT);
1731 const std::string evictable_key = ObjectStoreMetaDataKey::Encode(
1732 database_id, object_store_id, ObjectStoreMetaDataKey::EVICTABLE);
1733 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1734 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1735 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
1736 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
1737 const std::string has_key_path_key = ObjectStoreMetaDataKey::Encode(
1738 database_id, object_store_id, ObjectStoreMetaDataKey::HAS_KEY_PATH);
1739 const std::string key_generator_current_number_key =
1740 ObjectStoreMetaDataKey::Encode(
1741 database_id,
1742 object_store_id,
1743 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
1744 const std::string names_key = ObjectStoreNamesKey::Encode(database_id, name);
1746 PutString(leveldb_transaction, name_key, name);
1747 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
1748 PutInt(leveldb_transaction, auto_increment_key, auto_increment);
1749 PutInt(leveldb_transaction, evictable_key, false);
1750 PutInt(leveldb_transaction, last_version_key, 1);
1751 PutInt(leveldb_transaction, max_index_id_key, kMinimumIndexId);
1752 PutBool(leveldb_transaction, has_key_path_key, !key_path.IsNull());
1753 PutInt(leveldb_transaction,
1754 key_generator_current_number_key,
1755 kKeyGeneratorInitialNumber);
1756 PutInt(leveldb_transaction, names_key, object_store_id);
1757 return s;
1760 leveldb::Status IndexedDBBackingStore::DeleteObjectStore(
1761 IndexedDBBackingStore::Transaction* transaction,
1762 int64 database_id,
1763 int64 object_store_id) {
1764 IDB_TRACE("IndexedDBBackingStore::DeleteObjectStore");
1765 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1766 return InvalidDBKeyStatus();
1767 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1769 base::string16 object_store_name;
1770 bool found = false;
1771 leveldb::Status s =
1772 GetString(leveldb_transaction,
1773 ObjectStoreMetaDataKey::Encode(
1774 database_id, object_store_id, ObjectStoreMetaDataKey::NAME),
1775 &object_store_name,
1776 &found);
1777 if (!s.ok()) {
1778 INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1779 return s;
1781 if (!found) {
1782 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1783 return InternalInconsistencyStatus();
1786 s = DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
1787 if (!s.ok()) {
1788 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1789 return s;
1792 s = DeleteRangeBasic(
1793 leveldb_transaction,
1794 ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0),
1795 ObjectStoreMetaDataKey::EncodeMaxKey(database_id, object_store_id),
1796 true);
1798 if (s.ok()) {
1799 leveldb_transaction->Remove(
1800 ObjectStoreNamesKey::Encode(database_id, object_store_name));
1802 s = DeleteRangeBasic(
1803 leveldb_transaction,
1804 IndexFreeListKey::Encode(database_id, object_store_id, 0),
1805 IndexFreeListKey::EncodeMaxKey(database_id, object_store_id),
1806 true);
1809 if (s.ok()) {
1810 s = DeleteRangeBasic(
1811 leveldb_transaction,
1812 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0),
1813 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id),
1814 true);
1817 if (!s.ok()) {
1818 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1819 return s;
1822 return ClearObjectStore(transaction, database_id, object_store_id);
1825 leveldb::Status IndexedDBBackingStore::GetRecord(
1826 IndexedDBBackingStore::Transaction* transaction,
1827 int64 database_id,
1828 int64 object_store_id,
1829 const IndexedDBKey& key,
1830 IndexedDBValue* record) {
1831 IDB_TRACE("IndexedDBBackingStore::GetRecord");
1832 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1833 return InvalidDBKeyStatus();
1834 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1836 const std::string leveldb_key =
1837 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1838 std::string data;
1840 record->clear();
1842 bool found = false;
1843 leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
1844 if (!s.ok()) {
1845 INTERNAL_READ_ERROR(GET_RECORD);
1846 return s;
1848 if (!found)
1849 return s;
1850 if (data.empty()) {
1851 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1852 return leveldb::Status::NotFound("Record contained no data");
1855 int64 version;
1856 StringPiece slice(data);
1857 if (!DecodeVarInt(&slice, &version)) {
1858 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1859 return InternalInconsistencyStatus();
1862 record->bits = slice.as_string();
1863 return transaction->GetBlobInfoForRecord(database_id, leveldb_key, record);
1866 WARN_UNUSED_RESULT static leveldb::Status GetNewVersionNumber(
1867 LevelDBTransaction* transaction,
1868 int64 database_id,
1869 int64 object_store_id,
1870 int64* new_version_number) {
1871 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1872 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1874 *new_version_number = -1;
1875 int64 last_version = -1;
1876 bool found = false;
1877 leveldb::Status s =
1878 GetInt(transaction, last_version_key, &last_version, &found);
1879 if (!s.ok()) {
1880 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER);
1881 return s;
1883 if (!found)
1884 last_version = 0;
1886 DCHECK_GE(last_version, 0);
1888 int64 version = last_version + 1;
1889 PutInt(transaction, last_version_key, version);
1891 // TODO(jsbell): Think about how we want to handle the overflow scenario.
1892 DCHECK(version > last_version);
1894 *new_version_number = version;
1895 return s;
1898 leveldb::Status IndexedDBBackingStore::PutRecord(
1899 IndexedDBBackingStore::Transaction* transaction,
1900 int64 database_id,
1901 int64 object_store_id,
1902 const IndexedDBKey& key,
1903 IndexedDBValue* value,
1904 ScopedVector<storage::BlobDataHandle>* handles,
1905 RecordIdentifier* record_identifier) {
1906 IDB_TRACE("IndexedDBBackingStore::PutRecord");
1907 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1908 return InvalidDBKeyStatus();
1909 DCHECK(key.IsValid());
1911 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1912 int64 version = -1;
1913 leveldb::Status s = GetNewVersionNumber(
1914 leveldb_transaction, database_id, object_store_id, &version);
1915 if (!s.ok())
1916 return s;
1917 DCHECK_GE(version, 0);
1918 const std::string object_store_data_key =
1919 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1921 std::string v;
1922 EncodeVarInt(version, &v);
1923 v.append(value->bits);
1925 leveldb_transaction->Put(object_store_data_key, &v);
1926 s = transaction->PutBlobInfoIfNeeded(database_id,
1927 object_store_id,
1928 object_store_data_key,
1929 &value->blob_info,
1930 handles);
1931 if (!s.ok())
1932 return s;
1933 DCHECK(!handles->size());
1935 const std::string exists_entry_key =
1936 ExistsEntryKey::Encode(database_id, object_store_id, key);
1937 std::string version_encoded;
1938 EncodeInt(version, &version_encoded);
1939 leveldb_transaction->Put(exists_entry_key, &version_encoded);
1941 std::string key_encoded;
1942 EncodeIDBKey(key, &key_encoded);
1943 record_identifier->Reset(key_encoded, version);
1944 return s;
1947 leveldb::Status IndexedDBBackingStore::ClearObjectStore(
1948 IndexedDBBackingStore::Transaction* transaction,
1949 int64 database_id,
1950 int64 object_store_id) {
1951 IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
1952 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1953 return InvalidDBKeyStatus();
1954 const std::string start_key =
1955 KeyPrefix(database_id, object_store_id).Encode();
1956 const std::string stop_key =
1957 KeyPrefix(database_id, object_store_id + 1).Encode();
1959 leveldb::Status s =
1960 DeleteRangeBasic(transaction->transaction(), start_key, stop_key, true);
1961 if (!s.ok()) {
1962 INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE);
1963 return s;
1965 return DeleteBlobsInObjectStore(transaction, database_id, object_store_id);
1968 leveldb::Status IndexedDBBackingStore::DeleteRecord(
1969 IndexedDBBackingStore::Transaction* transaction,
1970 int64 database_id,
1971 int64 object_store_id,
1972 const RecordIdentifier& record_identifier) {
1973 IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
1974 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1975 return InvalidDBKeyStatus();
1976 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1978 const std::string object_store_data_key = ObjectStoreDataKey::Encode(
1979 database_id, object_store_id, record_identifier.primary_key());
1980 leveldb_transaction->Remove(object_store_data_key);
1981 leveldb::Status s = transaction->PutBlobInfoIfNeeded(
1982 database_id, object_store_id, object_store_data_key, NULL, NULL);
1983 if (!s.ok())
1984 return s;
1986 const std::string exists_entry_key = ExistsEntryKey::Encode(
1987 database_id, object_store_id, record_identifier.primary_key());
1988 leveldb_transaction->Remove(exists_entry_key);
1989 return leveldb::Status::OK();
1992 leveldb::Status IndexedDBBackingStore::DeleteRange(
1993 IndexedDBBackingStore::Transaction* transaction,
1994 int64 database_id,
1995 int64 object_store_id,
1996 const IndexedDBKeyRange& key_range) {
1997 leveldb::Status s;
1998 scoped_ptr<IndexedDBBackingStore::Cursor> start_cursor =
1999 OpenObjectStoreCursor(transaction,
2000 database_id,
2001 object_store_id,
2002 key_range,
2003 blink::WebIDBCursorDirectionNext,
2004 &s);
2005 if (!s.ok())
2006 return s;
2007 if (!start_cursor)
2008 return leveldb::Status::OK(); // Empty range == delete success.
2010 scoped_ptr<IndexedDBBackingStore::Cursor> end_cursor =
2011 OpenObjectStoreCursor(transaction,
2012 database_id,
2013 object_store_id,
2014 key_range,
2015 blink::WebIDBCursorDirectionPrev,
2016 &s);
2018 if (!s.ok())
2019 return s;
2020 if (!end_cursor)
2021 return leveldb::Status::OK(); // Empty range == delete success.
2023 BlobEntryKey start_blob_key, end_blob_key;
2025 std::string start_key = ObjectStoreDataKey::Encode(
2026 database_id, object_store_id, start_cursor->key());
2027 base::StringPiece start_key_piece(start_key);
2028 if (!BlobEntryKey::FromObjectStoreDataKey(&start_key_piece, &start_blob_key))
2029 return InternalInconsistencyStatus();
2030 std::string stop_key = ObjectStoreDataKey::Encode(
2031 database_id, object_store_id, end_cursor->key());
2032 base::StringPiece stop_key_piece(stop_key);
2033 if (!BlobEntryKey::FromObjectStoreDataKey(&stop_key_piece, &end_blob_key))
2034 return InternalInconsistencyStatus();
2036 s = DeleteBlobsInRange(transaction,
2037 database_id,
2038 object_store_id,
2039 start_blob_key.Encode(),
2040 end_blob_key.Encode(),
2041 false);
2042 if (!s.ok())
2043 return s;
2044 s = DeleteRangeBasic(transaction->transaction(), start_key, stop_key, false);
2045 if (!s.ok())
2046 return s;
2047 start_key =
2048 ExistsEntryKey::Encode(database_id, object_store_id, start_cursor->key());
2049 stop_key =
2050 ExistsEntryKey::Encode(database_id, object_store_id, end_cursor->key());
2051 return DeleteRangeBasic(
2052 transaction->transaction(), start_key, stop_key, false);
2055 leveldb::Status IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
2056 IndexedDBBackingStore::Transaction* transaction,
2057 int64 database_id,
2058 int64 object_store_id,
2059 int64* key_generator_current_number) {
2060 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2061 return InvalidDBKeyStatus();
2062 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2064 const std::string key_generator_current_number_key =
2065 ObjectStoreMetaDataKey::Encode(
2066 database_id,
2067 object_store_id,
2068 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
2070 *key_generator_current_number = -1;
2071 std::string data;
2073 bool found = false;
2074 leveldb::Status s =
2075 leveldb_transaction->Get(key_generator_current_number_key, &data, &found);
2076 if (!s.ok()) {
2077 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2078 return s;
2080 if (found && !data.empty()) {
2081 StringPiece slice(data);
2082 if (!DecodeInt(&slice, key_generator_current_number) || !slice.empty()) {
2083 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2084 return InternalInconsistencyStatus();
2086 return s;
2089 // Previously, the key generator state was not stored explicitly
2090 // but derived from the maximum numeric key present in existing
2091 // data. This violates the spec as the data may be cleared but the
2092 // key generator state must be preserved.
2093 // TODO(jsbell): Fix this for all stores on database open?
2094 const std::string start_key =
2095 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
2096 const std::string stop_key =
2097 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
2099 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
2100 int64 max_numeric_key = 0;
2102 for (s = it->Seek(start_key);
2103 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
2104 s = it->Next()) {
2105 StringPiece slice(it->Key());
2106 ObjectStoreDataKey data_key;
2107 if (!ObjectStoreDataKey::Decode(&slice, &data_key) || !slice.empty()) {
2108 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2109 return InternalInconsistencyStatus();
2111 scoped_ptr<IndexedDBKey> user_key = data_key.user_key();
2112 if (user_key->type() == blink::WebIDBKeyTypeNumber) {
2113 int64 n = static_cast<int64>(user_key->number());
2114 if (n > max_numeric_key)
2115 max_numeric_key = n;
2119 if (s.ok())
2120 *key_generator_current_number = max_numeric_key + 1;
2121 else
2122 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
2124 return s;
2127 leveldb::Status IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
2128 IndexedDBBackingStore::Transaction* transaction,
2129 int64 database_id,
2130 int64 object_store_id,
2131 int64 new_number,
2132 bool check_current) {
2133 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2134 return InvalidDBKeyStatus();
2136 if (check_current) {
2137 int64 current_number;
2138 leveldb::Status s = GetKeyGeneratorCurrentNumber(
2139 transaction, database_id, object_store_id, &current_number);
2140 if (!s.ok())
2141 return s;
2142 if (new_number <= current_number)
2143 return s;
2146 const std::string key_generator_current_number_key =
2147 ObjectStoreMetaDataKey::Encode(
2148 database_id,
2149 object_store_id,
2150 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
2151 PutInt(
2152 transaction->transaction(), key_generator_current_number_key, new_number);
2153 return leveldb::Status::OK();
2156 leveldb::Status IndexedDBBackingStore::KeyExistsInObjectStore(
2157 IndexedDBBackingStore::Transaction* transaction,
2158 int64 database_id,
2159 int64 object_store_id,
2160 const IndexedDBKey& key,
2161 RecordIdentifier* found_record_identifier,
2162 bool* found) {
2163 IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
2164 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2165 return InvalidDBKeyStatus();
2166 *found = false;
2167 const std::string leveldb_key =
2168 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
2169 std::string data;
2171 leveldb::Status s =
2172 transaction->transaction()->Get(leveldb_key, &data, found);
2173 if (!s.ok()) {
2174 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
2175 return s;
2177 if (!*found)
2178 return leveldb::Status::OK();
2179 if (!data.size()) {
2180 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
2181 return InternalInconsistencyStatus();
2184 int64 version;
2185 StringPiece slice(data);
2186 if (!DecodeVarInt(&slice, &version))
2187 return InternalInconsistencyStatus();
2189 std::string encoded_key;
2190 EncodeIDBKey(key, &encoded_key);
2191 found_record_identifier->Reset(encoded_key, version);
2192 return s;
2195 class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
2196 : public IndexedDBBackingStore::Transaction::ChainedBlobWriter {
2197 public:
2198 typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
2199 WriteDescriptorVec;
2200 ChainedBlobWriterImpl(
2201 int64 database_id,
2202 IndexedDBBackingStore* backing_store,
2203 WriteDescriptorVec* blobs,
2204 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback)
2205 : waiting_for_callback_(false),
2206 database_id_(database_id),
2207 backing_store_(backing_store),
2208 callback_(callback),
2209 aborted_(false) {
2210 blobs_.swap(*blobs);
2211 iter_ = blobs_.begin();
2212 backing_store->task_runner()->PostTask(
2213 FROM_HERE, base::Bind(&ChainedBlobWriterImpl::WriteNextFile, this));
2216 virtual void set_delegate(scoped_ptr<FileWriterDelegate> delegate) OVERRIDE {
2217 delegate_.reset(delegate.release());
2220 virtual void ReportWriteCompletion(bool succeeded,
2221 int64 bytes_written) OVERRIDE {
2222 DCHECK(waiting_for_callback_);
2223 DCHECK(!succeeded || bytes_written >= 0);
2224 waiting_for_callback_ = false;
2225 if (delegate_.get()) // Only present for Blob, not File.
2226 content::BrowserThread::DeleteSoon(
2227 content::BrowserThread::IO, FROM_HERE, delegate_.release());
2228 if (aborted_) {
2229 self_ref_ = NULL;
2230 return;
2232 if (iter_->size() != -1 && iter_->size() != bytes_written)
2233 succeeded = false;
2234 if (succeeded) {
2235 ++iter_;
2236 WriteNextFile();
2237 } else {
2238 callback_->Run(false);
2242 virtual void Abort() OVERRIDE {
2243 if (!waiting_for_callback_)
2244 return;
2245 self_ref_ = this;
2246 aborted_ = true;
2249 private:
2250 virtual ~ChainedBlobWriterImpl() {}
2252 void WriteNextFile() {
2253 DCHECK(!waiting_for_callback_);
2254 DCHECK(!aborted_);
2255 if (iter_ == blobs_.end()) {
2256 DCHECK(!self_ref_.get());
2257 callback_->Run(true);
2258 return;
2259 } else {
2260 if (!backing_store_->WriteBlobFile(database_id_, *iter_, this)) {
2261 callback_->Run(false);
2262 return;
2264 waiting_for_callback_ = true;
2268 bool waiting_for_callback_;
2269 scoped_refptr<ChainedBlobWriterImpl> self_ref_;
2270 WriteDescriptorVec blobs_;
2271 WriteDescriptorVec::const_iterator iter_;
2272 int64 database_id_;
2273 IndexedDBBackingStore* backing_store_;
2274 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback_;
2275 scoped_ptr<FileWriterDelegate> delegate_;
2276 bool aborted_;
2278 DISALLOW_COPY_AND_ASSIGN(ChainedBlobWriterImpl);
2281 class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
2282 public base::RefCountedThreadSafe<LocalWriteClosure> {
2283 public:
2284 LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter*
2285 chained_blob_writer,
2286 base::SequencedTaskRunner* task_runner)
2287 : chained_blob_writer_(chained_blob_writer),
2288 task_runner_(task_runner),
2289 bytes_written_(0) {}
2291 void Run(base::File::Error rv,
2292 int64 bytes,
2293 FileWriterDelegate::WriteProgressStatus write_status) {
2294 DCHECK_GE(bytes, 0);
2295 bytes_written_ += bytes;
2296 if (write_status == FileWriterDelegate::SUCCESS_IO_PENDING)
2297 return; // We don't care about progress events.
2298 if (rv == base::File::FILE_OK) {
2299 DCHECK_EQ(write_status, FileWriterDelegate::SUCCESS_COMPLETED);
2300 } else {
2301 DCHECK(write_status == FileWriterDelegate::ERROR_WRITE_STARTED ||
2302 write_status == FileWriterDelegate::ERROR_WRITE_NOT_STARTED);
2304 task_runner_->PostTask(
2305 FROM_HERE,
2306 base::Bind(&IndexedDBBackingStore::Transaction::ChainedBlobWriter::
2307 ReportWriteCompletion,
2308 chained_blob_writer_,
2309 write_status == FileWriterDelegate::SUCCESS_COMPLETED,
2310 bytes_written_));
2313 void writeBlobToFileOnIOThread(const FilePath& file_path,
2314 const GURL& blob_url,
2315 net::URLRequestContext* request_context) {
2316 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
2317 scoped_ptr<storage::FileStreamWriter> writer(
2318 storage::FileStreamWriter::CreateForLocalFile(
2319 task_runner_.get(),
2320 file_path,
2322 storage::FileStreamWriter::CREATE_NEW_FILE));
2323 scoped_ptr<FileWriterDelegate> delegate(
2324 new FileWriterDelegate(writer.Pass(),
2325 FileWriterDelegate::FLUSH_ON_COMPLETION));
2327 DCHECK(blob_url.is_valid());
2328 scoped_ptr<net::URLRequest> blob_request(request_context->CreateRequest(
2329 blob_url, net::DEFAULT_PRIORITY, delegate.get(), NULL));
2331 delegate->Start(blob_request.Pass(),
2332 base::Bind(&LocalWriteClosure::Run, this));
2333 chained_blob_writer_->set_delegate(delegate.Pass());
2336 private:
2337 virtual ~LocalWriteClosure() {
2338 // Make sure the last reference to a ChainedBlobWriter is released (and
2339 // deleted) on the IDB thread since it owns a transaction which has thread
2340 // affinity.
2341 IndexedDBBackingStore::Transaction::ChainedBlobWriter* raw_tmp =
2342 chained_blob_writer_.get();
2343 raw_tmp->AddRef();
2344 chained_blob_writer_ = NULL;
2345 task_runner_->ReleaseSoon(FROM_HERE, raw_tmp);
2347 friend class base::RefCountedThreadSafe<LocalWriteClosure>;
2349 scoped_refptr<IndexedDBBackingStore::Transaction::ChainedBlobWriter>
2350 chained_blob_writer_;
2351 scoped_refptr<base::SequencedTaskRunner> task_runner_;
2352 int64 bytes_written_;
2354 DISALLOW_COPY_AND_ASSIGN(LocalWriteClosure);
2357 bool IndexedDBBackingStore::WriteBlobFile(
2358 int64 database_id,
2359 const Transaction::WriteDescriptor& descriptor,
2360 Transaction::ChainedBlobWriter* chained_blob_writer) {
2362 if (!MakeIDBBlobDirectory(blob_path_, database_id, descriptor.key()))
2363 return false;
2365 FilePath path = GetBlobFileName(database_id, descriptor.key());
2367 if (descriptor.is_file()) {
2368 DCHECK(!descriptor.file_path().empty());
2369 if (!base::CopyFile(descriptor.file_path(), path))
2370 return false;
2372 base::File::Info info;
2373 if (base::GetFileInfo(descriptor.file_path(), &info)) {
2374 if (descriptor.size() != -1) {
2375 if (descriptor.size() != info.size)
2376 return false;
2377 // The round-trip can be lossy; round to nearest millisecond.
2378 int64 delta = (descriptor.last_modified() -
2379 info.last_modified).InMilliseconds();
2380 if (std::abs(delta) > 1)
2381 return false;
2383 if (!base::TouchFile(path, info.last_accessed, info.last_modified)) {
2384 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2386 } else {
2387 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2390 task_runner_->PostTask(
2391 FROM_HERE,
2392 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion,
2393 chained_blob_writer,
2394 true,
2395 info.size));
2396 } else {
2397 DCHECK(descriptor.url().is_valid());
2398 scoped_refptr<LocalWriteClosure> write_closure(
2399 new LocalWriteClosure(chained_blob_writer, task_runner_.get()));
2400 content::BrowserThread::PostTask(
2401 content::BrowserThread::IO,
2402 FROM_HERE,
2403 base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread,
2404 write_closure.get(),
2405 path,
2406 descriptor.url(),
2407 request_context_));
2409 return true;
2412 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
2413 int64 blob_key) {
2414 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2415 bool all_blobs = blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2416 DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2417 scoped_refptr<LevelDBTransaction> transaction =
2418 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2420 std::string live_blob_key = LiveBlobJournalKey::Encode();
2421 BlobJournalType live_blob_journal;
2422 if (!GetBlobJournal(live_blob_key, transaction.get(), &live_blob_journal)
2423 .ok())
2424 return;
2425 DCHECK(live_blob_journal.size());
2427 std::string primary_key = BlobJournalKey::Encode();
2428 BlobJournalType primary_journal;
2429 if (!GetBlobJournal(primary_key, transaction.get(), &primary_journal).ok())
2430 return;
2432 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2433 // remove all entries with database_id from the live_blob journal and add only
2434 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2435 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2436 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2437 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2438 // matching (database_id, blob_key) tuple, we should move it to the primary
2439 // journal.
2440 BlobJournalType new_live_blob_journal;
2441 for (BlobJournalType::iterator journal_iter = live_blob_journal.begin();
2442 journal_iter != live_blob_journal.end();
2443 ++journal_iter) {
2444 int64 current_database_id = journal_iter->first;
2445 int64 current_blob_key = journal_iter->second;
2446 bool current_all_blobs =
2447 current_blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2448 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id) ||
2449 current_all_blobs);
2450 if (current_database_id == database_id &&
2451 (all_blobs || current_all_blobs || blob_key == current_blob_key)) {
2452 if (!all_blobs) {
2453 primary_journal.push_back(
2454 std::make_pair(database_id, current_blob_key));
2455 if (current_all_blobs)
2456 new_live_blob_journal.push_back(*journal_iter);
2457 new_live_blob_journal.insert(new_live_blob_journal.end(),
2458 ++journal_iter,
2459 live_blob_journal.end()); // All the rest.
2460 break;
2462 } else {
2463 new_live_blob_journal.push_back(*journal_iter);
2466 if (all_blobs) {
2467 primary_journal.push_back(
2468 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
2470 UpdatePrimaryJournalWithBlobList(transaction.get(), primary_journal);
2471 UpdateLiveBlobJournalWithBlobList(transaction.get(), new_live_blob_journal);
2472 transaction->Commit();
2473 // We could just do the deletions/cleaning here, but if there are a lot of
2474 // blobs about to be garbage collected, it'd be better to wait and do them all
2475 // at once.
2476 StartJournalCleaningTimer();
2479 // The this reference is a raw pointer that's declared Unretained inside the
2480 // timer code, so this won't confuse IndexedDBFactory's check for
2481 // HasLastBackingStoreReference. It's safe because if the backing store is
2482 // deleted, the timer will automatically be canceled on destruction.
2483 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2484 journal_cleaning_timer_.Start(
2485 FROM_HERE,
2486 base::TimeDelta::FromSeconds(5),
2487 this,
2488 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn);
2491 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
2492 FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, int64 key) {
2493 return GetBlobFileNameForKey(blob_path_, database_id, key);
2496 static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
2497 const std::string& stop_key,
2498 int64 index_id,
2499 unsigned char meta_data_type) {
2500 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
2501 return false;
2503 StringPiece slice(it->Key());
2504 IndexMetaDataKey meta_data_key;
2505 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2506 DCHECK(ok);
2507 if (meta_data_key.IndexId() != index_id)
2508 return false;
2509 if (meta_data_key.meta_data_type() != meta_data_type)
2510 return false;
2511 return true;
2514 // TODO(jsbell): This should do some error handling rather than plowing ahead
2515 // when bad data is encountered.
2516 leveldb::Status IndexedDBBackingStore::GetIndexes(
2517 int64 database_id,
2518 int64 object_store_id,
2519 IndexedDBObjectStoreMetadata::IndexMap* indexes) {
2520 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2521 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2522 return InvalidDBKeyStatus();
2523 const std::string start_key =
2524 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0);
2525 const std::string stop_key =
2526 IndexMetaDataKey::Encode(database_id, object_store_id + 1, 0, 0);
2528 DCHECK(indexes->empty());
2530 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
2531 leveldb::Status s = it->Seek(start_key);
2532 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
2533 StringPiece slice(it->Key());
2534 IndexMetaDataKey meta_data_key;
2535 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2536 DCHECK(ok);
2537 if (meta_data_key.meta_data_type() != IndexMetaDataKey::NAME) {
2538 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2539 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2540 // the load.
2541 s = it->Next();
2542 if (!s.ok())
2543 break;
2544 continue;
2547 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2548 // simplify.
2549 int64 index_id = meta_data_key.IndexId();
2550 base::string16 index_name;
2552 StringPiece slice(it->Value());
2553 if (!DecodeString(&slice, &index_name) || !slice.empty())
2554 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2557 s = it->Next(); // unique flag
2558 if (!s.ok())
2559 break;
2560 if (!CheckIndexAndMetaDataKey(
2561 it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
2562 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2563 break;
2565 bool index_unique;
2567 StringPiece slice(it->Value());
2568 if (!DecodeBool(&slice, &index_unique) || !slice.empty())
2569 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2572 s = it->Next(); // key_path
2573 if (!s.ok())
2574 break;
2575 if (!CheckIndexAndMetaDataKey(
2576 it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
2577 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2578 break;
2580 IndexedDBKeyPath key_path;
2582 StringPiece slice(it->Value());
2583 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
2584 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2587 s = it->Next(); // [optional] multi_entry flag
2588 if (!s.ok())
2589 break;
2590 bool index_multi_entry = false;
2591 if (CheckIndexAndMetaDataKey(
2592 it.get(), stop_key, index_id, IndexMetaDataKey::MULTI_ENTRY)) {
2593 StringPiece slice(it->Value());
2594 if (!DecodeBool(&slice, &index_multi_entry) || !slice.empty())
2595 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2597 s = it->Next();
2598 if (!s.ok())
2599 break;
2602 (*indexes)[index_id] = IndexedDBIndexMetadata(
2603 index_name, index_id, key_path, index_unique, index_multi_entry);
2606 if (!s.ok())
2607 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES);
2609 return s;
2612 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) {
2613 FilePath fileName = GetBlobFileName(database_id, key);
2614 return base::DeleteFile(fileName, false);
2617 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) {
2618 FilePath dirName = GetBlobDirectoryName(blob_path_, database_id);
2619 return base::DeleteFile(dirName, true);
2622 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal(
2623 const std::string& level_db_key) {
2624 scoped_refptr<LevelDBTransaction> journal_transaction =
2625 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(db_.get());
2626 BlobJournalType journal;
2627 leveldb::Status s =
2628 GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
2629 if (!s.ok())
2630 return s;
2631 if (!journal.size())
2632 return leveldb::Status::OK();
2633 BlobJournalType::iterator journal_iter;
2634 for (journal_iter = journal.begin(); journal_iter != journal.end();
2635 ++journal_iter) {
2636 int64 database_id = journal_iter->first;
2637 int64 blob_key = journal_iter->second;
2638 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2639 if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
2640 if (!RemoveBlobDirectory(database_id))
2641 return IOErrorStatus();
2642 } else {
2643 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2644 if (!RemoveBlobFile(database_id, blob_key))
2645 return IOErrorStatus();
2648 ClearBlobJournal(journal_transaction.get(), level_db_key);
2649 return journal_transaction->Commit();
2652 leveldb::Status IndexedDBBackingStore::Transaction::GetBlobInfoForRecord(
2653 int64 database_id,
2654 const std::string& object_store_data_key,
2655 IndexedDBValue* value) {
2656 BlobChangeRecord* change_record = NULL;
2657 BlobChangeMap::const_iterator blob_iter =
2658 blob_change_map_.find(object_store_data_key);
2659 if (blob_iter != blob_change_map_.end()) {
2660 change_record = blob_iter->second;
2661 } else {
2662 blob_iter = incognito_blob_map_.find(object_store_data_key);
2663 if (blob_iter != incognito_blob_map_.end())
2664 change_record = blob_iter->second;
2666 if (change_record) {
2667 // Either we haven't written the blob to disk yet or we're in incognito
2668 // mode, so we have to send back the one they sent us. This change record
2669 // includes the original UUID.
2670 value->blob_info = change_record->blob_info();
2671 return leveldb::Status::OK();
2674 BlobEntryKey blob_entry_key;
2675 StringPiece leveldb_key_piece(object_store_data_key);
2676 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
2677 &blob_entry_key)) {
2678 NOTREACHED();
2679 return InternalInconsistencyStatus();
2681 scoped_ptr<LevelDBIterator> it = transaction()->CreateIterator();
2682 std::string encoded_key = blob_entry_key.Encode();
2683 leveldb::Status s = it->Seek(encoded_key);
2684 if (!s.ok())
2685 return s;
2686 if (it->IsValid() && CompareKeys(it->Key(), encoded_key) == 0) {
2687 if (!DecodeBlobData(it->Value().as_string(), &value->blob_info)) {
2688 INTERNAL_READ_ERROR(GET_BLOB_INFO_FOR_RECORD);
2689 return InternalInconsistencyStatus();
2691 std::vector<IndexedDBBlobInfo>::iterator iter;
2692 for (iter = value->blob_info.begin(); iter != value->blob_info.end();
2693 ++iter) {
2694 iter->set_file_path(
2695 backing_store_->GetBlobFileName(database_id, iter->key()));
2696 iter->set_mark_used_callback(
2697 backing_store_->active_blob_registry()->GetAddBlobRefCallback(
2698 database_id, iter->key()));
2699 iter->set_release_callback(
2700 backing_store_->active_blob_registry()->GetFinalReleaseCallback(
2701 database_id, iter->key()));
2702 if (iter->is_file()) {
2703 base::File::Info info;
2704 if (base::GetFileInfo(iter->file_path(), &info)) {
2705 // This should always work, but it isn't fatal if it doesn't; it just
2706 // means a potential slow synchronous call from the renderer later.
2707 iter->set_last_modified(info.last_modified);
2708 iter->set_size(info.size);
2713 return leveldb::Status::OK();
2716 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2717 CleanUpBlobJournal(BlobJournalKey::Encode());
2720 WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId(
2721 LevelDBTransaction* transaction,
2722 int64 database_id,
2723 int64 object_store_id,
2724 int64 index_id) {
2725 int64 max_index_id = -1;
2726 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
2727 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
2728 bool found = false;
2729 leveldb::Status s =
2730 GetInt(transaction, max_index_id_key, &max_index_id, &found);
2731 if (!s.ok()) {
2732 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2733 return s;
2735 if (!found)
2736 max_index_id = kMinimumIndexId;
2738 if (index_id <= max_index_id) {
2739 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2740 return InternalInconsistencyStatus();
2743 PutInt(transaction, max_index_id_key, index_id);
2744 return s;
2747 leveldb::Status IndexedDBBackingStore::CreateIndex(
2748 IndexedDBBackingStore::Transaction* transaction,
2749 int64 database_id,
2750 int64 object_store_id,
2751 int64 index_id,
2752 const base::string16& name,
2753 const IndexedDBKeyPath& key_path,
2754 bool is_unique,
2755 bool is_multi_entry) {
2756 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2757 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2758 return InvalidDBKeyStatus();
2759 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2760 leveldb::Status s = SetMaxIndexId(
2761 leveldb_transaction, database_id, object_store_id, index_id);
2763 if (!s.ok())
2764 return s;
2766 const std::string name_key = IndexMetaDataKey::Encode(
2767 database_id, object_store_id, index_id, IndexMetaDataKey::NAME);
2768 const std::string unique_key = IndexMetaDataKey::Encode(
2769 database_id, object_store_id, index_id, IndexMetaDataKey::UNIQUE);
2770 const std::string key_path_key = IndexMetaDataKey::Encode(
2771 database_id, object_store_id, index_id, IndexMetaDataKey::KEY_PATH);
2772 const std::string multi_entry_key = IndexMetaDataKey::Encode(
2773 database_id, object_store_id, index_id, IndexMetaDataKey::MULTI_ENTRY);
2775 PutString(leveldb_transaction, name_key, name);
2776 PutBool(leveldb_transaction, unique_key, is_unique);
2777 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
2778 PutBool(leveldb_transaction, multi_entry_key, is_multi_entry);
2779 return s;
2782 leveldb::Status IndexedDBBackingStore::DeleteIndex(
2783 IndexedDBBackingStore::Transaction* transaction,
2784 int64 database_id,
2785 int64 object_store_id,
2786 int64 index_id) {
2787 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2788 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2789 return InvalidDBKeyStatus();
2790 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2792 const std::string index_meta_data_start =
2793 IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0);
2794 const std::string index_meta_data_end =
2795 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2796 leveldb::Status s = DeleteRangeBasic(
2797 leveldb_transaction, index_meta_data_start, index_meta_data_end, true);
2799 if (s.ok()) {
2800 const std::string index_data_start =
2801 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
2802 const std::string index_data_end =
2803 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2804 s = DeleteRangeBasic(
2805 leveldb_transaction, index_data_start, index_data_end, true);
2808 if (!s.ok())
2809 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX);
2811 return s;
2814 leveldb::Status IndexedDBBackingStore::PutIndexDataForRecord(
2815 IndexedDBBackingStore::Transaction* transaction,
2816 int64 database_id,
2817 int64 object_store_id,
2818 int64 index_id,
2819 const IndexedDBKey& key,
2820 const RecordIdentifier& record_identifier) {
2821 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2822 DCHECK(key.IsValid());
2823 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2824 return InvalidDBKeyStatus();
2826 std::string encoded_key;
2827 EncodeIDBKey(key, &encoded_key);
2829 const std::string index_data_key =
2830 IndexDataKey::Encode(database_id,
2831 object_store_id,
2832 index_id,
2833 encoded_key,
2834 record_identifier.primary_key(),
2837 std::string data;
2838 EncodeVarInt(record_identifier.version(), &data);
2839 data.append(record_identifier.primary_key());
2841 transaction->transaction()->Put(index_data_key, &data);
2842 return leveldb::Status::OK();
2845 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
2846 const std::string& target,
2847 std::string* found_key,
2848 leveldb::Status* s) {
2849 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
2850 *s = it->Seek(target);
2851 if (!s->ok())
2852 return false;
2854 if (!it->IsValid()) {
2855 *s = it->SeekToLast();
2856 if (!s->ok() || !it->IsValid())
2857 return false;
2860 while (CompareIndexKeys(it->Key(), target) > 0) {
2861 *s = it->Prev();
2862 if (!s->ok() || !it->IsValid())
2863 return false;
2866 do {
2867 *found_key = it->Key().as_string();
2869 // There can be several index keys that compare equal. We want the last one.
2870 *s = it->Next();
2871 } while (s->ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
2873 return true;
2876 static leveldb::Status VersionExists(LevelDBTransaction* transaction,
2877 int64 database_id,
2878 int64 object_store_id,
2879 int64 version,
2880 const std::string& encoded_primary_key,
2881 bool* exists) {
2882 const std::string key =
2883 ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
2884 std::string data;
2886 leveldb::Status s = transaction->Get(key, &data, exists);
2887 if (!s.ok()) {
2888 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS);
2889 return s;
2891 if (!*exists)
2892 return s;
2894 StringPiece slice(data);
2895 int64 decoded;
2896 if (!DecodeInt(&slice, &decoded) || !slice.empty())
2897 return InternalInconsistencyStatus();
2898 *exists = (decoded == version);
2899 return s;
2902 leveldb::Status IndexedDBBackingStore::FindKeyInIndex(
2903 IndexedDBBackingStore::Transaction* transaction,
2904 int64 database_id,
2905 int64 object_store_id,
2906 int64 index_id,
2907 const IndexedDBKey& key,
2908 std::string* found_encoded_primary_key,
2909 bool* found) {
2910 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
2911 DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id));
2913 DCHECK(found_encoded_primary_key->empty());
2914 *found = false;
2916 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2917 const std::string leveldb_key =
2918 IndexDataKey::Encode(database_id, object_store_id, index_id, key);
2919 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
2920 leveldb::Status s = it->Seek(leveldb_key);
2921 if (!s.ok()) {
2922 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
2923 return s;
2926 for (;;) {
2927 if (!it->IsValid())
2928 return leveldb::Status::OK();
2929 if (CompareIndexKeys(it->Key(), leveldb_key) > 0)
2930 return leveldb::Status::OK();
2932 StringPiece slice(it->Value());
2934 int64 version;
2935 if (!DecodeVarInt(&slice, &version)) {
2936 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
2937 return InternalInconsistencyStatus();
2939 *found_encoded_primary_key = slice.as_string();
2941 bool exists = false;
2942 s = VersionExists(leveldb_transaction,
2943 database_id,
2944 object_store_id,
2945 version,
2946 *found_encoded_primary_key,
2947 &exists);
2948 if (!s.ok())
2949 return s;
2950 if (!exists) {
2951 // Delete stale index data entry and continue.
2952 leveldb_transaction->Remove(it->Key());
2953 s = it->Next();
2954 continue;
2956 *found = true;
2957 return s;
2961 leveldb::Status IndexedDBBackingStore::GetPrimaryKeyViaIndex(
2962 IndexedDBBackingStore::Transaction* transaction,
2963 int64 database_id,
2964 int64 object_store_id,
2965 int64 index_id,
2966 const IndexedDBKey& key,
2967 scoped_ptr<IndexedDBKey>* primary_key) {
2968 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
2969 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2970 return InvalidDBKeyStatus();
2972 bool found = false;
2973 std::string found_encoded_primary_key;
2974 leveldb::Status s = FindKeyInIndex(transaction,
2975 database_id,
2976 object_store_id,
2977 index_id,
2978 key,
2979 &found_encoded_primary_key,
2980 &found);
2981 if (!s.ok()) {
2982 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
2983 return s;
2985 if (!found)
2986 return s;
2987 if (!found_encoded_primary_key.size()) {
2988 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
2989 return InvalidDBKeyStatus();
2992 StringPiece slice(found_encoded_primary_key);
2993 if (DecodeIDBKey(&slice, primary_key) && slice.empty())
2994 return s;
2995 else
2996 return InvalidDBKeyStatus();
2999 leveldb::Status IndexedDBBackingStore::KeyExistsInIndex(
3000 IndexedDBBackingStore::Transaction* transaction,
3001 int64 database_id,
3002 int64 object_store_id,
3003 int64 index_id,
3004 const IndexedDBKey& index_key,
3005 scoped_ptr<IndexedDBKey>* found_primary_key,
3006 bool* exists) {
3007 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
3008 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3009 return InvalidDBKeyStatus();
3011 *exists = false;
3012 std::string found_encoded_primary_key;
3013 leveldb::Status s = FindKeyInIndex(transaction,
3014 database_id,
3015 object_store_id,
3016 index_id,
3017 index_key,
3018 &found_encoded_primary_key,
3019 exists);
3020 if (!s.ok()) {
3021 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
3022 return s;
3024 if (!*exists)
3025 return leveldb::Status::OK();
3026 if (found_encoded_primary_key.empty()) {
3027 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
3028 return InvalidDBKeyStatus();
3031 StringPiece slice(found_encoded_primary_key);
3032 if (DecodeIDBKey(&slice, found_primary_key) && slice.empty())
3033 return s;
3034 else
3035 return InvalidDBKeyStatus();
3038 IndexedDBBackingStore::Cursor::Cursor(
3039 const IndexedDBBackingStore::Cursor* other)
3040 : backing_store_(other->backing_store_),
3041 transaction_(other->transaction_),
3042 database_id_(other->database_id_),
3043 cursor_options_(other->cursor_options_),
3044 current_key_(new IndexedDBKey(*other->current_key_)) {
3045 if (other->iterator_) {
3046 iterator_ = transaction_->transaction()->CreateIterator();
3048 if (other->iterator_->IsValid()) {
3049 leveldb::Status s = iterator_->Seek(other->iterator_->Key());
3050 // TODO(cmumford): Handle this error (crbug.com/363397)
3051 DCHECK(iterator_->IsValid());
3056 IndexedDBBackingStore::Cursor::Cursor(
3057 scoped_refptr<IndexedDBBackingStore> backing_store,
3058 IndexedDBBackingStore::Transaction* transaction,
3059 int64 database_id,
3060 const CursorOptions& cursor_options)
3061 : backing_store_(backing_store.get()),
3062 transaction_(transaction),
3063 database_id_(database_id),
3064 cursor_options_(cursor_options) {
3066 IndexedDBBackingStore::Cursor::~Cursor() {}
3068 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) {
3069 iterator_ = transaction_->transaction()->CreateIterator();
3070 if (cursor_options_.forward)
3071 *s = iterator_->Seek(cursor_options_.low_key);
3072 else
3073 *s = iterator_->Seek(cursor_options_.high_key);
3074 if (!s->ok())
3075 return false;
3077 return Continue(0, READY, s);
3080 bool IndexedDBBackingStore::Cursor::Advance(uint32 count, leveldb::Status* s) {
3081 *s = leveldb::Status::OK();
3082 while (count--) {
3083 if (!Continue(s))
3084 return false;
3086 return true;
3089 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
3090 const IndexedDBKey* primary_key,
3091 IteratorState next_state,
3092 leveldb::Status* s) {
3093 DCHECK(!key || key->IsValid());
3094 DCHECK(!primary_key || primary_key->IsValid());
3095 *s = leveldb::Status::OK();
3097 // TODO(alecflett): avoid a copy here?
3098 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
3100 // When iterating with PrevNoDuplicate, spec requires that the
3101 // value we yield for each key is the first duplicate in forwards
3102 // order.
3103 IndexedDBKey last_duplicate_key;
3105 bool forward = cursor_options_.forward;
3106 bool first_iteration_forward = forward;
3107 bool flipped = false;
3109 for (;;) {
3110 if (next_state == SEEK) {
3111 // TODO(jsbell): Optimize seeking for reverse cursors as well.
3112 if (first_iteration_forward && key) {
3113 first_iteration_forward = false;
3114 std::string leveldb_key;
3115 if (primary_key) {
3116 leveldb_key = EncodeKey(*key, *primary_key);
3117 } else {
3118 leveldb_key = EncodeKey(*key);
3120 *s = iterator_->Seek(leveldb_key);
3121 } else if (forward) {
3122 *s = iterator_->Next();
3123 } else {
3124 *s = iterator_->Prev();
3126 if (!s->ok())
3127 return false;
3128 } else {
3129 next_state = SEEK; // for subsequent iterations
3132 if (!iterator_->IsValid()) {
3133 if (!forward && last_duplicate_key.IsValid()) {
3134 // We need to walk forward because we hit the end of
3135 // the data.
3136 forward = true;
3137 flipped = true;
3138 continue;
3141 return false;
3144 if (IsPastBounds()) {
3145 if (!forward && last_duplicate_key.IsValid()) {
3146 // We need to walk forward because now we're beyond the
3147 // bounds defined by the cursor.
3148 forward = true;
3149 flipped = true;
3150 continue;
3153 return false;
3156 if (!HaveEnteredRange())
3157 continue;
3159 // The row may not load because there's a stale entry in the
3160 // index. This is not fatal.
3161 if (!LoadCurrentRow())
3162 continue;
3164 if (key) {
3165 if (forward) {
3166 if (primary_key && current_key_->Equals(*key) &&
3167 this->primary_key().IsLessThan(*primary_key))
3168 continue;
3169 if (!flipped && current_key_->IsLessThan(*key))
3170 continue;
3171 } else {
3172 if (primary_key && key->Equals(*current_key_) &&
3173 primary_key->IsLessThan(this->primary_key()))
3174 continue;
3175 if (key->IsLessThan(*current_key_))
3176 continue;
3180 if (cursor_options_.unique) {
3181 if (previous_key.IsValid() && current_key_->Equals(previous_key)) {
3182 // We should never be able to walk forward all the way
3183 // to the previous key.
3184 DCHECK(!last_duplicate_key.IsValid());
3185 continue;
3188 if (!forward) {
3189 if (!last_duplicate_key.IsValid()) {
3190 last_duplicate_key = *current_key_;
3191 continue;
3194 // We need to walk forward because we hit the boundary
3195 // between key ranges.
3196 if (!last_duplicate_key.Equals(*current_key_)) {
3197 forward = true;
3198 flipped = true;
3199 continue;
3202 continue;
3205 break;
3208 DCHECK(!last_duplicate_key.IsValid() ||
3209 (forward && last_duplicate_key.Equals(*current_key_)));
3210 return true;
3213 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
3214 if (cursor_options_.forward) {
3215 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3216 if (cursor_options_.low_open) {
3217 return compare > 0;
3219 return compare >= 0;
3221 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3222 if (cursor_options_.high_open) {
3223 return compare < 0;
3225 return compare <= 0;
3228 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
3229 if (cursor_options_.forward) {
3230 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
3231 if (cursor_options_.high_open) {
3232 return compare >= 0;
3234 return compare > 0;
3236 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
3237 if (cursor_options_.low_open) {
3238 return compare <= 0;
3240 return compare < 0;
3243 const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const {
3244 return *current_key_;
3247 const IndexedDBBackingStore::RecordIdentifier&
3248 IndexedDBBackingStore::Cursor::record_identifier() const {
3249 return record_identifier_;
3252 class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3253 public:
3254 ObjectStoreKeyCursorImpl(
3255 scoped_refptr<IndexedDBBackingStore> backing_store,
3256 IndexedDBBackingStore::Transaction* transaction,
3257 int64 database_id,
3258 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3259 : IndexedDBBackingStore::Cursor(backing_store,
3260 transaction,
3261 database_id,
3262 cursor_options) {}
3264 virtual Cursor* Clone() OVERRIDE {
3265 return new ObjectStoreKeyCursorImpl(this);
3268 // IndexedDBBackingStore::Cursor
3269 virtual IndexedDBValue* value() OVERRIDE {
3270 NOTREACHED();
3271 return NULL;
3273 virtual bool LoadCurrentRow() OVERRIDE;
3275 protected:
3276 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3277 return ObjectStoreDataKey::Encode(
3278 cursor_options_.database_id, cursor_options_.object_store_id, key);
3280 virtual std::string EncodeKey(const IndexedDBKey& key,
3281 const IndexedDBKey& primary_key) OVERRIDE {
3282 NOTREACHED();
3283 return std::string();
3286 private:
3287 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other)
3288 : IndexedDBBackingStore::Cursor(other) {}
3290 DISALLOW_COPY_AND_ASSIGN(ObjectStoreKeyCursorImpl);
3293 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
3294 StringPiece slice(iterator_->Key());
3295 ObjectStoreDataKey object_store_data_key;
3296 if (!ObjectStoreDataKey::Decode(&slice, &object_store_data_key)) {
3297 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3298 return false;
3301 current_key_ = object_store_data_key.user_key();
3303 int64 version;
3304 slice = StringPiece(iterator_->Value());
3305 if (!DecodeVarInt(&slice, &version)) {
3306 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3307 return false;
3310 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3311 std::string encoded_key;
3312 EncodeIDBKey(*current_key_, &encoded_key);
3313 record_identifier_.Reset(encoded_key, version);
3315 return true;
3318 class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
3319 public:
3320 ObjectStoreCursorImpl(
3321 scoped_refptr<IndexedDBBackingStore> backing_store,
3322 IndexedDBBackingStore::Transaction* transaction,
3323 int64 database_id,
3324 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3325 : IndexedDBBackingStore::Cursor(backing_store,
3326 transaction,
3327 database_id,
3328 cursor_options) {}
3330 virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); }
3332 // IndexedDBBackingStore::Cursor
3333 virtual IndexedDBValue* value() OVERRIDE { return &current_value_; }
3334 virtual bool LoadCurrentRow() OVERRIDE;
3336 protected:
3337 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3338 return ObjectStoreDataKey::Encode(
3339 cursor_options_.database_id, cursor_options_.object_store_id, key);
3341 virtual std::string EncodeKey(const IndexedDBKey& key,
3342 const IndexedDBKey& primary_key) OVERRIDE {
3343 NOTREACHED();
3344 return std::string();
3347 private:
3348 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other)
3349 : IndexedDBBackingStore::Cursor(other),
3350 current_value_(other->current_value_) {}
3352 IndexedDBValue current_value_;
3354 DISALLOW_COPY_AND_ASSIGN(ObjectStoreCursorImpl);
3357 bool ObjectStoreCursorImpl::LoadCurrentRow() {
3358 StringPiece key_slice(iterator_->Key());
3359 ObjectStoreDataKey object_store_data_key;
3360 if (!ObjectStoreDataKey::Decode(&key_slice, &object_store_data_key)) {
3361 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3362 return false;
3365 current_key_ = object_store_data_key.user_key();
3367 int64 version;
3368 StringPiece value_slice = StringPiece(iterator_->Value());
3369 if (!DecodeVarInt(&value_slice, &version)) {
3370 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3371 return false;
3374 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
3375 std::string encoded_key;
3376 EncodeIDBKey(*current_key_, &encoded_key);
3377 record_identifier_.Reset(encoded_key, version);
3379 if (!transaction_->GetBlobInfoForRecord(database_id_,
3380 iterator_->Key().as_string(),
3381 &current_value_).ok()) {
3382 return false;
3384 current_value_.bits = value_slice.as_string();
3385 return true;
3388 class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
3389 public:
3390 IndexKeyCursorImpl(
3391 scoped_refptr<IndexedDBBackingStore> backing_store,
3392 IndexedDBBackingStore::Transaction* transaction,
3393 int64 database_id,
3394 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3395 : IndexedDBBackingStore::Cursor(backing_store,
3396 transaction,
3397 database_id,
3398 cursor_options) {}
3400 virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); }
3402 // IndexedDBBackingStore::Cursor
3403 virtual IndexedDBValue* value() OVERRIDE {
3404 NOTREACHED();
3405 return NULL;
3407 virtual const IndexedDBKey& primary_key() const OVERRIDE {
3408 return *primary_key_;
3410 virtual const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3411 const OVERRIDE {
3412 NOTREACHED();
3413 return record_identifier_;
3415 virtual bool LoadCurrentRow() OVERRIDE;
3417 protected:
3418 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3419 return IndexDataKey::Encode(cursor_options_.database_id,
3420 cursor_options_.object_store_id,
3421 cursor_options_.index_id,
3422 key);
3424 virtual std::string EncodeKey(const IndexedDBKey& key,
3425 const IndexedDBKey& primary_key) OVERRIDE {
3426 return IndexDataKey::Encode(cursor_options_.database_id,
3427 cursor_options_.object_store_id,
3428 cursor_options_.index_id,
3429 key,
3430 primary_key);
3433 private:
3434 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl* other)
3435 : IndexedDBBackingStore::Cursor(other),
3436 primary_key_(new IndexedDBKey(*other->primary_key_)) {}
3438 scoped_ptr<IndexedDBKey> primary_key_;
3440 DISALLOW_COPY_AND_ASSIGN(IndexKeyCursorImpl);
3443 bool IndexKeyCursorImpl::LoadCurrentRow() {
3444 StringPiece slice(iterator_->Key());
3445 IndexDataKey index_data_key;
3446 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3447 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3448 return false;
3451 current_key_ = index_data_key.user_key();
3452 DCHECK(current_key_);
3454 slice = StringPiece(iterator_->Value());
3455 int64 index_data_version;
3456 if (!DecodeVarInt(&slice, &index_data_version)) {
3457 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3458 return false;
3461 if (!DecodeIDBKey(&slice, &primary_key_) || !slice.empty()) {
3462 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3463 return false;
3466 std::string primary_leveldb_key =
3467 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3468 index_data_key.ObjectStoreId(),
3469 *primary_key_);
3471 std::string result;
3472 bool found = false;
3473 leveldb::Status s =
3474 transaction_->transaction()->Get(primary_leveldb_key, &result, &found);
3475 if (!s.ok()) {
3476 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3477 return false;
3479 if (!found) {
3480 transaction_->transaction()->Remove(iterator_->Key());
3481 return false;
3483 if (!result.size()) {
3484 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3485 return false;
3488 int64 object_store_data_version;
3489 slice = StringPiece(result);
3490 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3491 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3492 return false;
3495 if (object_store_data_version != index_data_version) {
3496 transaction_->transaction()->Remove(iterator_->Key());
3497 return false;
3500 return true;
3503 class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
3504 public:
3505 IndexCursorImpl(
3506 scoped_refptr<IndexedDBBackingStore> backing_store,
3507 IndexedDBBackingStore::Transaction* transaction,
3508 int64 database_id,
3509 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3510 : IndexedDBBackingStore::Cursor(backing_store,
3511 transaction,
3512 database_id,
3513 cursor_options) {}
3515 virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); }
3517 // IndexedDBBackingStore::Cursor
3518 virtual IndexedDBValue* value() OVERRIDE { return &current_value_; }
3519 virtual const IndexedDBKey& primary_key() const OVERRIDE {
3520 return *primary_key_;
3522 virtual const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3523 const OVERRIDE {
3524 NOTREACHED();
3525 return record_identifier_;
3527 virtual bool LoadCurrentRow() OVERRIDE;
3529 protected:
3530 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3531 return IndexDataKey::Encode(cursor_options_.database_id,
3532 cursor_options_.object_store_id,
3533 cursor_options_.index_id,
3534 key);
3536 virtual std::string EncodeKey(const IndexedDBKey& key,
3537 const IndexedDBKey& primary_key) OVERRIDE {
3538 return IndexDataKey::Encode(cursor_options_.database_id,
3539 cursor_options_.object_store_id,
3540 cursor_options_.index_id,
3541 key,
3542 primary_key);
3545 private:
3546 explicit IndexCursorImpl(const IndexCursorImpl* other)
3547 : IndexedDBBackingStore::Cursor(other),
3548 primary_key_(new IndexedDBKey(*other->primary_key_)),
3549 current_value_(other->current_value_),
3550 primary_leveldb_key_(other->primary_leveldb_key_) {}
3552 scoped_ptr<IndexedDBKey> primary_key_;
3553 IndexedDBValue current_value_;
3554 std::string primary_leveldb_key_;
3556 DISALLOW_COPY_AND_ASSIGN(IndexCursorImpl);
3559 bool IndexCursorImpl::LoadCurrentRow() {
3560 StringPiece slice(iterator_->Key());
3561 IndexDataKey index_data_key;
3562 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3563 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3564 return false;
3567 current_key_ = index_data_key.user_key();
3568 DCHECK(current_key_);
3570 slice = StringPiece(iterator_->Value());
3571 int64 index_data_version;
3572 if (!DecodeVarInt(&slice, &index_data_version)) {
3573 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3574 return false;
3576 if (!DecodeIDBKey(&slice, &primary_key_)) {
3577 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3578 return false;
3581 DCHECK_EQ(index_data_key.DatabaseId(), database_id_);
3582 primary_leveldb_key_ =
3583 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3584 index_data_key.ObjectStoreId(),
3585 *primary_key_);
3587 std::string result;
3588 bool found = false;
3589 leveldb::Status s =
3590 transaction_->transaction()->Get(primary_leveldb_key_, &result, &found);
3591 if (!s.ok()) {
3592 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3593 return false;
3595 if (!found) {
3596 transaction_->transaction()->Remove(iterator_->Key());
3597 return false;
3599 if (!result.size()) {
3600 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3601 return false;
3604 int64 object_store_data_version;
3605 slice = StringPiece(result);
3606 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3607 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3608 return false;
3611 if (object_store_data_version != index_data_version) {
3612 transaction_->transaction()->Remove(iterator_->Key());
3613 return false;
3616 current_value_.bits = slice.as_string();
3617 return transaction_->GetBlobInfoForRecord(database_id_,
3618 primary_leveldb_key_,
3619 &current_value_).ok();
3622 bool ObjectStoreCursorOptions(
3623 LevelDBTransaction* transaction,
3624 int64 database_id,
3625 int64 object_store_id,
3626 const IndexedDBKeyRange& range,
3627 blink::WebIDBCursorDirection direction,
3628 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3629 cursor_options->database_id = database_id;
3630 cursor_options->object_store_id = object_store_id;
3632 bool lower_bound = range.lower().IsValid();
3633 bool upper_bound = range.upper().IsValid();
3634 cursor_options->forward =
3635 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3636 direction == blink::WebIDBCursorDirectionNext);
3637 cursor_options->unique =
3638 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3639 direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
3641 if (!lower_bound) {
3642 cursor_options->low_key =
3643 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
3644 cursor_options->low_open = true; // Not included.
3645 } else {
3646 cursor_options->low_key =
3647 ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower());
3648 cursor_options->low_open = range.lowerOpen();
3651 leveldb::Status s;
3653 if (!upper_bound) {
3654 cursor_options->high_key =
3655 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
3657 if (cursor_options->forward) {
3658 cursor_options->high_open = true; // Not included.
3659 } else {
3660 // We need a key that exists.
3661 // TODO(cmumford): Handle this error (crbug.com/363397)
3662 if (!FindGreatestKeyLessThanOrEqual(transaction,
3663 cursor_options->high_key,
3664 &cursor_options->high_key,
3665 &s))
3666 return false;
3667 cursor_options->high_open = false;
3669 } else {
3670 cursor_options->high_key =
3671 ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper());
3672 cursor_options->high_open = range.upperOpen();
3674 if (!cursor_options->forward) {
3675 // For reverse cursors, we need a key that exists.
3676 std::string found_high_key;
3677 // TODO(cmumford): Handle this error (crbug.com/363397)
3678 if (!FindGreatestKeyLessThanOrEqual(
3679 transaction, cursor_options->high_key, &found_high_key, &s))
3680 return false;
3682 // If the target key should not be included, but we end up with a smaller
3683 // key, we should include that.
3684 if (cursor_options->high_open &&
3685 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3686 cursor_options->high_open = false;
3688 cursor_options->high_key = found_high_key;
3692 return true;
3695 bool IndexCursorOptions(
3696 LevelDBTransaction* transaction,
3697 int64 database_id,
3698 int64 object_store_id,
3699 int64 index_id,
3700 const IndexedDBKeyRange& range,
3701 blink::WebIDBCursorDirection direction,
3702 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3703 DCHECK(transaction);
3704 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3705 return false;
3707 cursor_options->database_id = database_id;
3708 cursor_options->object_store_id = object_store_id;
3709 cursor_options->index_id = index_id;
3711 bool lower_bound = range.lower().IsValid();
3712 bool upper_bound = range.upper().IsValid();
3713 cursor_options->forward =
3714 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3715 direction == blink::WebIDBCursorDirectionNext);
3716 cursor_options->unique =
3717 (direction == blink::WebIDBCursorDirectionNextNoDuplicate ||
3718 direction == blink::WebIDBCursorDirectionPrevNoDuplicate);
3720 if (!lower_bound) {
3721 cursor_options->low_key =
3722 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
3723 cursor_options->low_open = false; // Included.
3724 } else {
3725 cursor_options->low_key = IndexDataKey::Encode(
3726 database_id, object_store_id, index_id, range.lower());
3727 cursor_options->low_open = range.lowerOpen();
3730 leveldb::Status s;
3732 if (!upper_bound) {
3733 cursor_options->high_key =
3734 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
3735 cursor_options->high_open = false; // Included.
3737 if (!cursor_options->forward) { // We need a key that exists.
3738 if (!FindGreatestKeyLessThanOrEqual(transaction,
3739 cursor_options->high_key,
3740 &cursor_options->high_key,
3741 &s))
3742 return false;
3743 cursor_options->high_open = false;
3745 } else {
3746 cursor_options->high_key = IndexDataKey::Encode(
3747 database_id, object_store_id, index_id, range.upper());
3748 cursor_options->high_open = range.upperOpen();
3750 std::string found_high_key;
3751 // Seek to the *last* key in the set of non-unique keys
3752 // TODO(cmumford): Handle this error (crbug.com/363397)
3753 if (!FindGreatestKeyLessThanOrEqual(
3754 transaction, cursor_options->high_key, &found_high_key, &s))
3755 return false;
3757 // If the target key should not be included, but we end up with a smaller
3758 // key, we should include that.
3759 if (cursor_options->high_open &&
3760 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3761 cursor_options->high_open = false;
3763 cursor_options->high_key = found_high_key;
3766 return true;
3769 scoped_ptr<IndexedDBBackingStore::Cursor>
3770 IndexedDBBackingStore::OpenObjectStoreCursor(
3771 IndexedDBBackingStore::Transaction* transaction,
3772 int64 database_id,
3773 int64 object_store_id,
3774 const IndexedDBKeyRange& range,
3775 blink::WebIDBCursorDirection direction,
3776 leveldb::Status* s) {
3777 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3778 *s = leveldb::Status::OK();
3779 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3780 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3781 if (!ObjectStoreCursorOptions(leveldb_transaction,
3782 database_id,
3783 object_store_id,
3784 range,
3785 direction,
3786 &cursor_options))
3787 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3788 scoped_ptr<ObjectStoreCursorImpl> cursor(new ObjectStoreCursorImpl(
3789 this, transaction, database_id, cursor_options));
3790 if (!cursor->FirstSeek(s))
3791 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3793 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3796 scoped_ptr<IndexedDBBackingStore::Cursor>
3797 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3798 IndexedDBBackingStore::Transaction* transaction,
3799 int64 database_id,
3800 int64 object_store_id,
3801 const IndexedDBKeyRange& range,
3802 blink::WebIDBCursorDirection direction,
3803 leveldb::Status* s) {
3804 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3805 *s = leveldb::Status::OK();
3806 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3807 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3808 if (!ObjectStoreCursorOptions(leveldb_transaction,
3809 database_id,
3810 object_store_id,
3811 range,
3812 direction,
3813 &cursor_options))
3814 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3815 scoped_ptr<ObjectStoreKeyCursorImpl> cursor(new ObjectStoreKeyCursorImpl(
3816 this, transaction, database_id, cursor_options));
3817 if (!cursor->FirstSeek(s))
3818 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3820 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3823 scoped_ptr<IndexedDBBackingStore::Cursor>
3824 IndexedDBBackingStore::OpenIndexKeyCursor(
3825 IndexedDBBackingStore::Transaction* transaction,
3826 int64 database_id,
3827 int64 object_store_id,
3828 int64 index_id,
3829 const IndexedDBKeyRange& range,
3830 blink::WebIDBCursorDirection direction,
3831 leveldb::Status* s) {
3832 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3833 *s = leveldb::Status::OK();
3834 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3835 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3836 if (!IndexCursorOptions(leveldb_transaction,
3837 database_id,
3838 object_store_id,
3839 index_id,
3840 range,
3841 direction,
3842 &cursor_options))
3843 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3844 scoped_ptr<IndexKeyCursorImpl> cursor(
3845 new IndexKeyCursorImpl(this, transaction, database_id, cursor_options));
3846 if (!cursor->FirstSeek(s))
3847 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3849 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3852 scoped_ptr<IndexedDBBackingStore::Cursor>
3853 IndexedDBBackingStore::OpenIndexCursor(
3854 IndexedDBBackingStore::Transaction* transaction,
3855 int64 database_id,
3856 int64 object_store_id,
3857 int64 index_id,
3858 const IndexedDBKeyRange& range,
3859 blink::WebIDBCursorDirection direction,
3860 leveldb::Status* s) {
3861 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
3862 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3863 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3864 if (!IndexCursorOptions(leveldb_transaction,
3865 database_id,
3866 object_store_id,
3867 index_id,
3868 range,
3869 direction,
3870 &cursor_options))
3871 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3872 scoped_ptr<IndexCursorImpl> cursor(
3873 new IndexCursorImpl(this, transaction, database_id, cursor_options));
3874 if (!cursor->FirstSeek(s))
3875 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3877 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3880 IndexedDBBackingStore::Transaction::Transaction(
3881 IndexedDBBackingStore* backing_store)
3882 : backing_store_(backing_store), database_id_(-1) {
3885 IndexedDBBackingStore::Transaction::~Transaction() {
3886 STLDeleteContainerPairSecondPointers(
3887 blob_change_map_.begin(), blob_change_map_.end());
3888 STLDeleteContainerPairSecondPointers(incognito_blob_map_.begin(),
3889 incognito_blob_map_.end());
3892 void IndexedDBBackingStore::Transaction::Begin() {
3893 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
3894 DCHECK(!transaction_.get());
3895 transaction_ = IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3896 backing_store_->db_.get());
3898 // If incognito, this snapshots blobs just as the above transaction_
3899 // constructor snapshots the leveldb.
3900 BlobChangeMap::const_iterator iter;
3901 for (iter = backing_store_->incognito_blob_map_.begin();
3902 iter != backing_store_->incognito_blob_map_.end();
3903 ++iter)
3904 incognito_blob_map_[iter->first] = iter->second->Clone().release();
3907 static GURL getURLFromUUID(const string& uuid) {
3908 return GURL("blob:uuid/" + uuid);
3911 leveldb::Status IndexedDBBackingStore::Transaction::HandleBlobPreTransaction(
3912 BlobEntryKeyValuePairVec* new_blob_entries,
3913 WriteDescriptorVec* new_files_to_write) {
3914 if (backing_store_->is_incognito())
3915 return leveldb::Status::OK();
3917 BlobChangeMap::iterator iter = blob_change_map_.begin();
3918 new_blob_entries->clear();
3919 new_files_to_write->clear();
3920 if (iter != blob_change_map_.end()) {
3921 // Create LevelDBTransaction for the name generator seed and add-journal.
3922 scoped_refptr<LevelDBTransaction> pre_transaction =
3923 IndexedDBClassFactory::Get()->CreateLevelDBTransaction(
3924 backing_store_->db_.get());
3925 BlobJournalType journal;
3926 for (; iter != blob_change_map_.end(); ++iter) {
3927 std::vector<IndexedDBBlobInfo>::iterator info_iter;
3928 std::vector<IndexedDBBlobInfo*> new_blob_keys;
3929 for (info_iter = iter->second->mutable_blob_info().begin();
3930 info_iter != iter->second->mutable_blob_info().end();
3931 ++info_iter) {
3932 int64 next_blob_key = -1;
3933 bool result = GetBlobKeyGeneratorCurrentNumber(
3934 pre_transaction.get(), database_id_, &next_blob_key);
3935 if (!result || next_blob_key < 0)
3936 return InternalInconsistencyStatus();
3937 BlobJournalEntryType journal_entry =
3938 std::make_pair(database_id_, next_blob_key);
3939 journal.push_back(journal_entry);
3940 if (info_iter->is_file()) {
3941 new_files_to_write->push_back(
3942 WriteDescriptor(info_iter->file_path(),
3943 next_blob_key,
3944 info_iter->size(),
3945 info_iter->last_modified()));
3946 } else {
3947 new_files_to_write->push_back(
3948 WriteDescriptor(getURLFromUUID(info_iter->uuid()),
3949 next_blob_key,
3950 info_iter->size()));
3952 info_iter->set_key(next_blob_key);
3953 new_blob_keys.push_back(&*info_iter);
3954 result = UpdateBlobKeyGeneratorCurrentNumber(
3955 pre_transaction.get(), database_id_, next_blob_key + 1);
3956 if (!result)
3957 return InternalInconsistencyStatus();
3959 BlobEntryKey blob_entry_key;
3960 StringPiece key_piece(iter->second->key());
3961 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
3962 NOTREACHED();
3963 return InternalInconsistencyStatus();
3965 new_blob_entries->push_back(
3966 std::make_pair(blob_entry_key, EncodeBlobData(new_blob_keys)));
3968 UpdatePrimaryJournalWithBlobList(pre_transaction.get(), journal);
3969 leveldb::Status s = pre_transaction->Commit();
3970 if (!s.ok())
3971 return InternalInconsistencyStatus();
3973 return leveldb::Status::OK();
3976 bool IndexedDBBackingStore::Transaction::CollectBlobFilesToRemove() {
3977 if (backing_store_->is_incognito())
3978 return true;
3980 BlobChangeMap::const_iterator iter = blob_change_map_.begin();
3981 // Look up all old files to remove as part of the transaction, store their
3982 // names in blobs_to_remove_, and remove their old blob data entries.
3983 if (iter != blob_change_map_.end()) {
3984 scoped_ptr<LevelDBIterator> db_iter = transaction_->CreateIterator();
3985 for (; iter != blob_change_map_.end(); ++iter) {
3986 BlobEntryKey blob_entry_key;
3987 StringPiece key_piece(iter->second->key());
3988 if (!BlobEntryKey::FromObjectStoreDataKey(&key_piece, &blob_entry_key)) {
3989 NOTREACHED();
3990 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
3991 transaction_ = NULL;
3992 return false;
3994 if (database_id_ < 0)
3995 database_id_ = blob_entry_key.database_id();
3996 else
3997 DCHECK_EQ(database_id_, blob_entry_key.database_id());
3998 std::string blob_entry_key_bytes = blob_entry_key.Encode();
3999 db_iter->Seek(blob_entry_key_bytes);
4000 if (db_iter->IsValid() &&
4001 !CompareKeys(db_iter->Key(), blob_entry_key_bytes)) {
4002 std::vector<IndexedDBBlobInfo> blob_info;
4003 if (!DecodeBlobData(db_iter->Value().as_string(), &blob_info)) {
4004 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4005 transaction_ = NULL;
4006 return false;
4008 std::vector<IndexedDBBlobInfo>::iterator blob_info_iter;
4009 for (blob_info_iter = blob_info.begin();
4010 blob_info_iter != blob_info.end();
4011 ++blob_info_iter)
4012 blobs_to_remove_.push_back(
4013 std::make_pair(database_id_, blob_info_iter->key()));
4014 transaction_->Remove(blob_entry_key_bytes);
4018 return true;
4021 leveldb::Status IndexedDBBackingStore::Transaction::SortBlobsToRemove() {
4022 IndexedDBActiveBlobRegistry* registry =
4023 backing_store_->active_blob_registry();
4024 BlobJournalType::iterator iter;
4025 BlobJournalType primary_journal, live_blob_journal;
4026 for (iter = blobs_to_remove_.begin(); iter != blobs_to_remove_.end();
4027 ++iter) {
4028 if (registry->MarkDeletedCheckIfUsed(iter->first, iter->second))
4029 live_blob_journal.push_back(*iter);
4030 else
4031 primary_journal.push_back(*iter);
4033 UpdatePrimaryJournalWithBlobList(transaction_.get(), primary_journal);
4034 leveldb::Status s =
4035 MergeBlobsIntoLiveBlobJournal(transaction_.get(), live_blob_journal);
4036 if (!s.ok())
4037 return s;
4038 // To signal how many blobs need attention right now.
4039 blobs_to_remove_.swap(primary_journal);
4040 return leveldb::Status::OK();
4043 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseOne(
4044 scoped_refptr<BlobWriteCallback> callback) {
4045 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseOne");
4046 DCHECK(transaction_.get());
4047 DCHECK(backing_store_->task_runner()->RunsTasksOnCurrentThread());
4049 leveldb::Status s;
4051 s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
4052 if (!s.ok()) {
4053 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
4054 transaction_ = NULL;
4055 return s;
4058 BlobEntryKeyValuePairVec new_blob_entries;
4059 WriteDescriptorVec new_files_to_write;
4060 s = HandleBlobPreTransaction(&new_blob_entries, &new_files_to_write);
4061 if (!s.ok()) {
4062 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4063 transaction_ = NULL;
4064 return s;
4067 DCHECK(!new_files_to_write.size() ||
4068 KeyPrefix::IsValidDatabaseId(database_id_));
4069 if (!CollectBlobFilesToRemove()) {
4070 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4071 transaction_ = NULL;
4072 return InternalInconsistencyStatus();
4075 if (new_files_to_write.size()) {
4076 // This kicks off the writes of the new blobs, if any.
4077 // This call will zero out new_blob_entries and new_files_to_write.
4078 WriteNewBlobs(&new_blob_entries, &new_files_to_write, callback);
4079 // Remove the add journal, if any; once the blobs are written, and we
4080 // commit, this will do the cleanup.
4081 ClearBlobJournal(transaction_.get(), BlobJournalKey::Encode());
4082 } else {
4083 callback->Run(true);
4086 return leveldb::Status::OK();
4089 leveldb::Status IndexedDBBackingStore::Transaction::CommitPhaseTwo() {
4090 IDB_TRACE("IndexedDBBackingStore::Transaction::CommitPhaseTwo");
4091 leveldb::Status s;
4092 if (blobs_to_remove_.size()) {
4093 s = SortBlobsToRemove();
4094 if (!s.ok()) {
4095 INTERNAL_READ_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
4096 transaction_ = NULL;
4097 return s;
4101 s = transaction_->Commit();
4102 transaction_ = NULL;
4104 if (s.ok() && backing_store_->is_incognito() && !blob_change_map_.empty()) {
4105 BlobChangeMap& target_map = backing_store_->incognito_blob_map_;
4106 BlobChangeMap::iterator iter;
4107 for (iter = blob_change_map_.begin(); iter != blob_change_map_.end();
4108 ++iter) {
4109 BlobChangeMap::iterator target_record = target_map.find(iter->first);
4110 if (target_record != target_map.end()) {
4111 delete target_record->second;
4112 target_map.erase(target_record);
4114 if (iter->second) {
4115 target_map[iter->first] = iter->second;
4116 iter->second = NULL;
4120 if (!s.ok())
4121 INTERNAL_WRITE_ERROR(TRANSACTION_COMMIT_METHOD);
4122 else if (blobs_to_remove_.size())
4123 s = backing_store_->CleanUpBlobJournal(BlobJournalKey::Encode());
4125 return s;
4129 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
4130 : public IndexedDBBackingStore::BlobWriteCallback {
4131 public:
4132 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction,
4133 scoped_refptr<BlobWriteCallback> callback)
4134 : transaction_(transaction), callback_(callback) {}
4135 virtual void Run(bool succeeded) OVERRIDE {
4136 callback_->Run(succeeded);
4137 if (succeeded) // Else it's already been deleted during rollback.
4138 transaction_->chained_blob_writer_ = NULL;
4141 private:
4142 virtual ~BlobWriteCallbackWrapper() {}
4143 friend class base::RefCounted<IndexedDBBackingStore::BlobWriteCallback>;
4145 IndexedDBBackingStore::Transaction* transaction_;
4146 scoped_refptr<BlobWriteCallback> callback_;
4148 DISALLOW_COPY_AND_ASSIGN(BlobWriteCallbackWrapper);
4151 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
4152 BlobEntryKeyValuePairVec* new_blob_entries,
4153 WriteDescriptorVec* new_files_to_write,
4154 scoped_refptr<BlobWriteCallback> callback) {
4155 DCHECK_GT(new_files_to_write->size(), 0UL);
4156 DCHECK_GT(database_id_, 0);
4157 BlobEntryKeyValuePairVec::iterator blob_entry_iter;
4158 for (blob_entry_iter = new_blob_entries->begin();
4159 blob_entry_iter != new_blob_entries->end();
4160 ++blob_entry_iter) {
4161 // Add the new blob-table entry for each blob to the main transaction, or
4162 // remove any entry that may exist if there's no new one.
4163 if (!blob_entry_iter->second.size())
4164 transaction_->Remove(blob_entry_iter->first.Encode());
4165 else
4166 transaction_->Put(blob_entry_iter->first.Encode(),
4167 &blob_entry_iter->second);
4169 // Creating the writer will start it going asynchronously.
4170 chained_blob_writer_ =
4171 new ChainedBlobWriterImpl(database_id_,
4172 backing_store_,
4173 new_files_to_write,
4174 new BlobWriteCallbackWrapper(this, callback));
4177 void IndexedDBBackingStore::Transaction::Rollback() {
4178 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
4179 if (chained_blob_writer_.get()) {
4180 chained_blob_writer_->Abort();
4181 chained_blob_writer_ = NULL;
4183 if (transaction_.get() == NULL)
4184 return;
4185 transaction_->Rollback();
4186 transaction_ = NULL;
4189 IndexedDBBackingStore::BlobChangeRecord::BlobChangeRecord(
4190 const std::string& key,
4191 int64 object_store_id)
4192 : key_(key), object_store_id_(object_store_id) {
4195 IndexedDBBackingStore::BlobChangeRecord::~BlobChangeRecord() {
4198 void IndexedDBBackingStore::BlobChangeRecord::SetBlobInfo(
4199 std::vector<IndexedDBBlobInfo>* blob_info) {
4200 blob_info_.clear();
4201 if (blob_info)
4202 blob_info_.swap(*blob_info);
4205 void IndexedDBBackingStore::BlobChangeRecord::SetHandles(
4206 ScopedVector<storage::BlobDataHandle>* handles) {
4207 handles_.clear();
4208 if (handles)
4209 handles_.swap(*handles);
4212 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord>
4213 IndexedDBBackingStore::BlobChangeRecord::Clone() const {
4214 scoped_ptr<IndexedDBBackingStore::BlobChangeRecord> record(
4215 new BlobChangeRecord(key_, object_store_id_));
4216 record->blob_info_ = blob_info_;
4218 ScopedVector<storage::BlobDataHandle>::const_iterator iter;
4219 for (iter = handles_.begin(); iter != handles_.end(); ++iter)
4220 record->handles_.push_back(new storage::BlobDataHandle(**iter));
4221 return record.Pass();
4224 leveldb::Status IndexedDBBackingStore::Transaction::PutBlobInfoIfNeeded(
4225 int64 database_id,
4226 int64 object_store_id,
4227 const std::string& object_store_data_key,
4228 std::vector<IndexedDBBlobInfo>* blob_info,
4229 ScopedVector<storage::BlobDataHandle>* handles) {
4230 if (!blob_info || blob_info->empty()) {
4231 blob_change_map_.erase(object_store_data_key);
4232 incognito_blob_map_.erase(object_store_data_key);
4234 BlobEntryKey blob_entry_key;
4235 StringPiece leveldb_key_piece(object_store_data_key);
4236 if (!BlobEntryKey::FromObjectStoreDataKey(&leveldb_key_piece,
4237 &blob_entry_key)) {
4238 NOTREACHED();
4239 return InternalInconsistencyStatus();
4241 std::string value;
4242 bool found = false;
4243 leveldb::Status s =
4244 transaction()->Get(blob_entry_key.Encode(), &value, &found);
4245 if (!s.ok())
4246 return s;
4247 if (!found)
4248 return leveldb::Status::OK();
4250 PutBlobInfo(
4251 database_id, object_store_id, object_store_data_key, blob_info, handles);
4252 return leveldb::Status::OK();
4255 // This is storing an info, even if empty, even if the previous key had no blob
4256 // info that we know of. It duplicates a bunch of information stored in the
4257 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
4258 // changes to exists or index keys here.
4259 void IndexedDBBackingStore::Transaction::PutBlobInfo(
4260 int64 database_id,
4261 int64 object_store_id,
4262 const std::string& object_store_data_key,
4263 std::vector<IndexedDBBlobInfo>* blob_info,
4264 ScopedVector<storage::BlobDataHandle>* handles) {
4265 DCHECK_GT(object_store_data_key.size(), 0UL);
4266 if (database_id_ < 0)
4267 database_id_ = database_id;
4268 DCHECK_EQ(database_id_, database_id);
4270 BlobChangeMap::iterator it = blob_change_map_.find(object_store_data_key);
4271 BlobChangeRecord* record = NULL;
4272 if (it == blob_change_map_.end()) {
4273 record = new BlobChangeRecord(object_store_data_key, object_store_id);
4274 blob_change_map_[object_store_data_key] = record;
4275 } else {
4276 record = it->second;
4278 DCHECK_EQ(record->object_store_id(), object_store_id);
4279 record->SetBlobInfo(blob_info);
4280 record->SetHandles(handles);
4281 DCHECK(!handles || !handles->size());
4284 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4285 const GURL& url,
4286 int64_t key,
4287 int64_t size)
4288 : is_file_(false), url_(url), key_(key), size_(size) {
4291 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
4292 const FilePath& file_path,
4293 int64_t key,
4294 int64_t size,
4295 base::Time last_modified)
4296 : is_file_(true),
4297 file_path_(file_path),
4298 key_(key),
4299 size_(size),
4300 last_modified_(last_modified) {
4303 } // namespace content