Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_backing_store.cc
blob3c5aa2d9537b13b0a734d8ca98ec963d2f55ac60
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/indexed_db/indexed_db_backing_store.h"
7 #include "base/file_util.h"
8 #include "base/files/file_path.h"
9 #include "base/format_macros.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "content/browser/child_process_security_policy_impl.h"
18 #include "content/browser/indexed_db/indexed_db_blob_info.h"
19 #include "content/browser/indexed_db/indexed_db_database_error.h"
20 #include "content/browser/indexed_db/indexed_db_leveldb_coding.h"
21 #include "content/browser/indexed_db/indexed_db_metadata.h"
22 #include "content/browser/indexed_db/indexed_db_tracing.h"
23 #include "content/browser/indexed_db/indexed_db_value.h"
24 #include "content/browser/indexed_db/leveldb/leveldb_comparator.h"
25 #include "content/browser/indexed_db/leveldb/leveldb_database.h"
26 #include "content/browser/indexed_db/leveldb/leveldb_iterator.h"
27 #include "content/browser/indexed_db/leveldb/leveldb_transaction.h"
28 #include "content/common/indexed_db/indexed_db_key.h"
29 #include "content/common/indexed_db/indexed_db_key_path.h"
30 #include "content/common/indexed_db/indexed_db_key_range.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "net/url_request/url_request_context.h"
33 #include "third_party/WebKit/public/platform/WebIDBTypes.h"
34 #include "third_party/WebKit/public/web/WebSerializedScriptValueVersion.h"
35 #include "third_party/leveldatabase/env_chromium.h"
36 #include "webkit/browser/blob/blob_data_handle.h"
37 #include "webkit/browser/fileapi/file_stream_writer.h"
38 #include "webkit/browser/fileapi/file_writer_delegate.h"
39 #include "webkit/browser/fileapi/local_file_stream_writer.h"
40 #include "webkit/common/database/database_identifier.h"
42 using base::FilePath;
43 using base::StringPiece;
44 using fileapi::FileWriterDelegate;
46 namespace content {
48 namespace {
50 FilePath GetBlobDirectoryName(const FilePath& pathBase, int64 database_id) {
51 return pathBase.AppendASCII(base::StringPrintf("%" PRIx64, database_id));
54 FilePath GetBlobDirectoryNameForKey(const FilePath& pathBase,
55 int64 database_id,
56 int64 key) {
57 FilePath path = GetBlobDirectoryName(pathBase, database_id);
58 path = path.AppendASCII(base::StringPrintf(
59 "%02x", static_cast<int>(key & 0x000000000000ff00) >> 8));
60 return path;
63 FilePath GetBlobFileNameForKey(const FilePath& pathBase,
64 int64 database_id,
65 int64 key) {
66 FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
67 path = path.AppendASCII(base::StringPrintf("%" PRIx64, key));
68 return path;
71 bool MakeIDBBlobDirectory(const FilePath& pathBase,
72 int64 database_id,
73 int64 key) {
74 FilePath path = GetBlobDirectoryNameForKey(pathBase, database_id, key);
75 return base::CreateDirectory(path);
78 static std::string ComputeOriginIdentifier(const GURL& origin_url) {
79 return webkit_database::GetIdentifierFromOrigin(origin_url) + "@1";
82 static base::FilePath ComputeFileName(const GURL& origin_url) {
83 return base::FilePath()
84 .AppendASCII(webkit_database::GetIdentifierFromOrigin(origin_url))
85 .AddExtension(FILE_PATH_LITERAL(".indexeddb.leveldb"));
88 static base::FilePath ComputeBlobPath(const GURL& origin_url) {
89 return base::FilePath()
90 .AppendASCII(webkit_database::GetIdentifierFromOrigin(origin_url))
91 .AddExtension(FILE_PATH_LITERAL(".indexeddb.blob"));
94 static base::FilePath ComputeCorruptionFileName(const GURL& origin_url) {
95 return ComputeFileName(origin_url)
96 .Append(FILE_PATH_LITERAL("corruption_info.json"));
99 } // namespace
101 static const int64 kKeyGeneratorInitialNumber =
102 1; // From the IndexedDB specification.
104 enum IndexedDBBackingStoreErrorSource {
105 // 0 - 2 are no longer used.
106 FIND_KEY_IN_INDEX = 3,
107 GET_IDBDATABASE_METADATA,
108 GET_INDEXES,
109 GET_KEY_GENERATOR_CURRENT_NUMBER,
110 GET_OBJECT_STORES,
111 GET_RECORD,
112 KEY_EXISTS_IN_OBJECT_STORE,
113 LOAD_CURRENT_ROW,
114 SET_UP_METADATA,
115 GET_PRIMARY_KEY_VIA_INDEX,
116 KEY_EXISTS_IN_INDEX,
117 VERSION_EXISTS,
118 DELETE_OBJECT_STORE,
119 SET_MAX_OBJECT_STORE_ID,
120 SET_MAX_INDEX_ID,
121 GET_NEW_DATABASE_ID,
122 GET_NEW_VERSION_NUMBER,
123 CREATE_IDBDATABASE_METADATA,
124 DELETE_DATABASE,
125 TRANSACTION_COMMIT_METHOD, // TRANSACTION_COMMIT is a WinNT.h macro
126 GET_DATABASE_NAMES,
127 DELETE_INDEX,
128 CLEAR_OBJECT_STORE,
129 READ_BLOB_JOURNAL,
130 DECODE_BLOB_JOURNAL,
131 INTERNAL_ERROR_MAX,
134 static void RecordInternalError(const char* type,
135 IndexedDBBackingStoreErrorSource location) {
136 std::string name;
137 name.append("WebCore.IndexedDB.BackingStore.").append(type).append("Error");
138 base::Histogram::FactoryGet(name,
140 INTERNAL_ERROR_MAX,
141 INTERNAL_ERROR_MAX + 1,
142 base::HistogramBase::kUmaTargetedHistogramFlag)
143 ->Add(location);
146 // Use to signal conditions caused by data corruption.
147 // A macro is used instead of an inline function so that the assert and log
148 // report the line number.
149 #define REPORT_ERROR(type, location) \
150 do { \
151 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
152 RecordInternalError(type, location); \
153 } while (0)
155 #define INTERNAL_READ_ERROR(location) REPORT_ERROR("Read", location)
156 #define INTERNAL_CONSISTENCY_ERROR(location) \
157 REPORT_ERROR("Consistency", location)
158 #define INTERNAL_WRITE_ERROR(location) REPORT_ERROR("Write", location)
160 // Use to signal conditions that usually indicate developer error, but
161 // could be caused by data corruption. A macro is used instead of an
162 // inline function so that the assert and log report the line number.
163 // TODO: Improve test coverage so that all error conditions are "tested" and
164 // then delete this macro.
165 #define REPORT_ERROR_UNTESTED(type, location) \
166 do { \
167 LOG(ERROR) << "IndexedDB " type " Error: " #location; \
168 NOTREACHED(); \
169 RecordInternalError(type, location); \
170 } while (0)
172 #define INTERNAL_READ_ERROR_UNTESTED(location) \
173 REPORT_ERROR_UNTESTED("Read", location)
174 #define INTERNAL_CONSISTENCY_ERROR_UNTESTED(location) \
175 REPORT_ERROR_UNTESTED("Consistency", location)
176 #define INTERNAL_WRITE_ERROR_UNTESTED(location) \
177 REPORT_ERROR_UNTESTED("Write", location)
179 static void PutBool(LevelDBTransaction* transaction,
180 const StringPiece& key,
181 bool value) {
182 std::string buffer;
183 EncodeBool(value, &buffer);
184 transaction->Put(key, &buffer);
187 // Was able to use LevelDB to read the data w/o error, but the data read was not
188 // in the expected format.
189 static leveldb::Status InternalInconsistencyStatus() {
190 return leveldb::Status::Corruption("Internal inconsistency");
193 static leveldb::Status InvalidDBKeyStatus() {
194 return leveldb::Status::InvalidArgument("Invalid database key ID");
197 static leveldb::Status IOErrorStatus() {
198 return leveldb::Status::IOError("IO Error");
201 template <typename DBOrTransaction>
202 static leveldb::Status GetInt(DBOrTransaction* db,
203 const StringPiece& key,
204 int64* found_int,
205 bool* found) {
206 std::string result;
207 leveldb::Status s = db->Get(key, &result, found);
208 if (!s.ok())
209 return s;
210 if (!*found)
211 return leveldb::Status::OK();
212 StringPiece slice(result);
213 if (DecodeInt(&slice, found_int) && slice.empty())
214 return s;
215 return InternalInconsistencyStatus();
218 static void PutInt(LevelDBTransaction* transaction,
219 const StringPiece& key,
220 int64 value) {
221 DCHECK_GE(value, 0);
222 std::string buffer;
223 EncodeInt(value, &buffer);
224 transaction->Put(key, &buffer);
227 template <typename DBOrTransaction>
228 WARN_UNUSED_RESULT static leveldb::Status GetVarInt(DBOrTransaction* db,
229 const StringPiece& key,
230 int64* found_int,
231 bool* found) {
232 std::string result;
233 leveldb::Status s = db->Get(key, &result, found);
234 if (!s.ok())
235 return s;
236 if (!*found)
237 return leveldb::Status::OK();
238 StringPiece slice(result);
239 if (DecodeVarInt(&slice, found_int) && slice.empty())
240 return s;
241 return InternalInconsistencyStatus();
244 static void PutVarInt(LevelDBTransaction* transaction,
245 const StringPiece& key,
246 int64 value) {
247 std::string buffer;
248 EncodeVarInt(value, &buffer);
249 transaction->Put(key, &buffer);
252 template <typename DBOrTransaction>
253 WARN_UNUSED_RESULT static leveldb::Status GetString(
254 DBOrTransaction* db,
255 const StringPiece& key,
256 base::string16* found_string,
257 bool* found) {
258 std::string result;
259 *found = false;
260 leveldb::Status s = db->Get(key, &result, found);
261 if (!s.ok())
262 return s;
263 if (!*found)
264 return leveldb::Status::OK();
265 StringPiece slice(result);
266 if (DecodeString(&slice, found_string) && slice.empty())
267 return s;
268 return InternalInconsistencyStatus();
271 static void PutString(LevelDBTransaction* transaction,
272 const StringPiece& key,
273 const base::string16& value) {
274 std::string buffer;
275 EncodeString(value, &buffer);
276 transaction->Put(key, &buffer);
279 static void PutIDBKeyPath(LevelDBTransaction* transaction,
280 const StringPiece& key,
281 const IndexedDBKeyPath& value) {
282 std::string buffer;
283 EncodeIDBKeyPath(value, &buffer);
284 transaction->Put(key, &buffer);
287 static int CompareKeys(const StringPiece& a, const StringPiece& b) {
288 return Compare(a, b, false /*index_keys*/);
291 static int CompareIndexKeys(const StringPiece& a, const StringPiece& b) {
292 return Compare(a, b, true /*index_keys*/);
295 int IndexedDBBackingStore::Comparator::Compare(const StringPiece& a,
296 const StringPiece& b) const {
297 return content::Compare(a, b, false /*index_keys*/);
300 const char* IndexedDBBackingStore::Comparator::Name() const {
301 return "idb_cmp1";
304 // 0 - Initial version.
305 // 1 - Adds UserIntVersion to DatabaseMetaData.
306 // 2 - Adds DataVersion to to global metadata.
307 static const int64 kLatestKnownSchemaVersion = 2;
308 WARN_UNUSED_RESULT static bool IsSchemaKnown(LevelDBDatabase* db, bool* known) {
309 int64 db_schema_version = 0;
310 bool found = false;
311 leveldb::Status s =
312 GetInt(db, SchemaVersionKey::Encode(), &db_schema_version, &found);
313 if (!s.ok())
314 return false;
315 if (!found) {
316 *known = true;
317 return true;
319 if (db_schema_version > kLatestKnownSchemaVersion) {
320 *known = false;
321 return true;
324 const uint32 latest_known_data_version =
325 blink::kSerializedScriptValueVersion;
326 int64 db_data_version = 0;
327 s = GetInt(db, DataVersionKey::Encode(), &db_data_version, &found);
328 if (!s.ok())
329 return false;
330 if (!found) {
331 *known = true;
332 return true;
335 if (db_data_version > latest_known_data_version) {
336 *known = false;
337 return true;
340 *known = true;
341 return true;
344 WARN_UNUSED_RESULT static bool SetUpMetadata(
345 LevelDBDatabase* db,
346 const std::string& origin_identifier) {
347 const uint32 latest_known_data_version =
348 blink::kSerializedScriptValueVersion;
349 const std::string schema_version_key = SchemaVersionKey::Encode();
350 const std::string data_version_key = DataVersionKey::Encode();
352 scoped_refptr<LevelDBTransaction> transaction = new LevelDBTransaction(db);
354 int64 db_schema_version = 0;
355 int64 db_data_version = 0;
356 bool found = false;
357 leveldb::Status s =
358 GetInt(transaction.get(), schema_version_key, &db_schema_version, &found);
359 if (!s.ok()) {
360 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
361 return false;
363 if (!found) {
364 // Initialize new backing store.
365 db_schema_version = kLatestKnownSchemaVersion;
366 PutInt(transaction.get(), schema_version_key, db_schema_version);
367 db_data_version = latest_known_data_version;
368 PutInt(transaction.get(), data_version_key, db_data_version);
369 } else {
370 // Upgrade old backing store.
371 DCHECK_LE(db_schema_version, kLatestKnownSchemaVersion);
372 if (db_schema_version < 1) {
373 db_schema_version = 1;
374 PutInt(transaction.get(), schema_version_key, db_schema_version);
375 const std::string start_key =
376 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier);
377 const std::string stop_key =
378 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier);
379 scoped_ptr<LevelDBIterator> it = db->CreateIterator();
380 for (s = it->Seek(start_key);
381 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
382 s = it->Next()) {
383 int64 database_id = 0;
384 found = false;
385 s = GetInt(transaction.get(), it->Key(), &database_id, &found);
386 if (!s.ok()) {
387 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
388 return false;
390 if (!found) {
391 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
392 return false;
394 std::string int_version_key = DatabaseMetaDataKey::Encode(
395 database_id, DatabaseMetaDataKey::USER_INT_VERSION);
396 PutVarInt(transaction.get(),
397 int_version_key,
398 IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION);
401 if (s.ok() && db_schema_version < 2) {
402 db_schema_version = 2;
403 PutInt(transaction.get(), schema_version_key, db_schema_version);
404 db_data_version = blink::kSerializedScriptValueVersion;
405 PutInt(transaction.get(), data_version_key, db_data_version);
409 if (!s.ok()) {
410 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
411 return false;
414 // All new values will be written using this serialization version.
415 found = false;
416 s = GetInt(transaction.get(), data_version_key, &db_data_version, &found);
417 if (!s.ok()) {
418 INTERNAL_READ_ERROR_UNTESTED(SET_UP_METADATA);
419 return false;
421 if (!found) {
422 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_UP_METADATA);
423 return false;
425 if (db_data_version < latest_known_data_version) {
426 db_data_version = latest_known_data_version;
427 PutInt(transaction.get(), data_version_key, db_data_version);
430 DCHECK_EQ(db_schema_version, kLatestKnownSchemaVersion);
431 DCHECK_EQ(db_data_version, latest_known_data_version);
433 s = transaction->Commit();
434 if (!s.ok()) {
435 INTERNAL_WRITE_ERROR_UNTESTED(SET_UP_METADATA);
436 return false;
438 return true;
441 template <typename DBOrTransaction>
442 WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
443 DBOrTransaction* db,
444 int64 database_id,
445 int64* max_object_store_id) {
446 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
447 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
448 return GetMaxObjectStoreId(db, max_object_store_id_key, max_object_store_id);
451 template <typename DBOrTransaction>
452 WARN_UNUSED_RESULT static leveldb::Status GetMaxObjectStoreId(
453 DBOrTransaction* db,
454 const std::string& max_object_store_id_key,
455 int64* max_object_store_id) {
456 *max_object_store_id = -1;
457 bool found = false;
458 leveldb::Status s =
459 GetInt(db, max_object_store_id_key, max_object_store_id, &found);
460 if (!s.ok())
461 return s;
462 if (!found)
463 *max_object_store_id = 0;
465 DCHECK_GE(*max_object_store_id, 0);
466 return s;
469 class DefaultLevelDBFactory : public LevelDBFactory {
470 public:
471 virtual leveldb::Status OpenLevelDB(const base::FilePath& file_name,
472 const LevelDBComparator* comparator,
473 scoped_ptr<LevelDBDatabase>* db,
474 bool* is_disk_full) OVERRIDE {
475 return LevelDBDatabase::Open(file_name, comparator, db, is_disk_full);
477 virtual leveldb::Status DestroyLevelDB(const base::FilePath& file_name)
478 OVERRIDE {
479 return LevelDBDatabase::Destroy(file_name);
483 // TODO(ericu): Error recovery. If we persistently can't read the
484 // blob journal, the safe thing to do is to clear it and leak the blobs,
485 // though that may be costly. Still, database/directory deletion should always
486 // clean things up, and we can write an fsck that will do a full correction if
487 // need be.
488 template <typename T>
489 static leveldb::Status GetBlobJournal(const StringPiece& leveldb_key,
490 T* leveldb_transaction,
491 BlobJournalType* journal) {
492 std::string data;
493 bool found = false;
494 leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
495 if (!s.ok()) {
496 INTERNAL_READ_ERROR_UNTESTED(READ_BLOB_JOURNAL);
497 return s;
499 journal->clear();
500 if (!found || !data.size())
501 return leveldb::Status::OK();
502 StringPiece slice(data);
503 if (!DecodeBlobJournal(&slice, journal)) {
504 INTERNAL_READ_ERROR_UNTESTED(DECODE_BLOB_JOURNAL);
505 s = InternalInconsistencyStatus();
507 return s;
510 static void ClearBlobJournal(LevelDBTransaction* leveldb_transaction,
511 const std::string& level_db_key) {
512 leveldb_transaction->Remove(level_db_key);
515 static void UpdatePrimaryJournalWithBlobList(
516 LevelDBTransaction* leveldb_transaction,
517 const BlobJournalType& journal) {
518 const std::string leveldb_key = BlobJournalKey::Encode();
519 std::string data;
520 EncodeBlobJournal(journal, &data);
521 leveldb_transaction->Put(leveldb_key, &data);
524 static void UpdateLiveBlobJournalWithBlobList(
525 LevelDBTransaction* leveldb_transaction,
526 const BlobJournalType& journal) {
527 const std::string leveldb_key = LiveBlobJournalKey::Encode();
528 std::string data;
529 EncodeBlobJournal(journal, &data);
530 leveldb_transaction->Put(leveldb_key, &data);
533 static leveldb::Status MergeBlobsIntoLiveBlobJournal(
534 LevelDBTransaction* leveldb_transaction,
535 const BlobJournalType& journal) {
536 BlobJournalType old_journal;
537 const std::string key = LiveBlobJournalKey::Encode();
538 leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &old_journal);
539 if (!s.ok())
540 return s;
542 old_journal.insert(old_journal.end(), journal.begin(), journal.end());
544 UpdateLiveBlobJournalWithBlobList(leveldb_transaction, old_journal);
545 return leveldb::Status::OK();
548 static void UpdateBlobJournalWithDatabase(
549 LevelDBDirectTransaction* leveldb_transaction,
550 int64 database_id) {
551 BlobJournalType journal;
552 journal.push_back(
553 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
554 const std::string key = BlobJournalKey::Encode();
555 std::string data;
556 EncodeBlobJournal(journal, &data);
557 leveldb_transaction->Put(key, &data);
560 static leveldb::Status MergeDatabaseIntoLiveBlobJournal(
561 LevelDBDirectTransaction* leveldb_transaction,
562 int64 database_id) {
563 BlobJournalType journal;
564 const std::string key = LiveBlobJournalKey::Encode();
565 leveldb::Status s = GetBlobJournal(key, leveldb_transaction, &journal);
566 if (!s.ok())
567 return s;
568 journal.push_back(
569 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
570 std::string data;
571 EncodeBlobJournal(journal, &data);
572 leveldb_transaction->Put(key, &data);
573 return leveldb::Status::OK();
576 IndexedDBBackingStore::IndexedDBBackingStore(
577 IndexedDBFactory* indexed_db_factory,
578 const GURL& origin_url,
579 const base::FilePath& blob_path,
580 net::URLRequestContext* request_context,
581 scoped_ptr<LevelDBDatabase> db,
582 scoped_ptr<LevelDBComparator> comparator,
583 base::TaskRunner* task_runner)
584 : indexed_db_factory_(indexed_db_factory),
585 origin_url_(origin_url),
586 blob_path_(blob_path),
587 origin_identifier_(ComputeOriginIdentifier(origin_url)),
588 request_context_(request_context),
589 task_runner_(task_runner),
590 db_(db.Pass()),
591 comparator_(comparator.Pass()),
592 active_blob_registry_(this) {}
594 IndexedDBBackingStore::~IndexedDBBackingStore() {
595 if (!blob_path_.empty() && !child_process_ids_granted_.empty()) {
596 ChildProcessSecurityPolicyImpl* policy =
597 ChildProcessSecurityPolicyImpl::GetInstance();
598 std::set<int>::const_iterator iter;
599 for (iter = child_process_ids_granted_.begin();
600 iter != child_process_ids_granted_.end();
601 ++iter) {
602 policy->RevokeAllPermissionsForFile(*iter, blob_path_);
605 // db_'s destructor uses comparator_. The order of destruction is important.
606 db_.reset();
607 comparator_.reset();
610 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier(
611 const std::string& primary_key,
612 int64 version)
613 : primary_key_(primary_key), version_(version) {
614 DCHECK(!primary_key.empty());
616 IndexedDBBackingStore::RecordIdentifier::RecordIdentifier()
617 : primary_key_(), version_(-1) {}
618 IndexedDBBackingStore::RecordIdentifier::~RecordIdentifier() {}
620 IndexedDBBackingStore::Cursor::CursorOptions::CursorOptions() {}
621 IndexedDBBackingStore::Cursor::CursorOptions::~CursorOptions() {}
623 enum IndexedDBBackingStoreOpenResult {
624 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS,
625 INDEXED_DB_BACKING_STORE_OPEN_SUCCESS,
626 INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
627 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
628 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
629 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
630 INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
631 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
632 INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR,
633 INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
634 INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
635 INDEXED_DB_BACKING_STORE_OPEN_DISK_FULL_DEPRECATED,
636 INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
637 INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY,
638 INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
639 INDEXED_DB_BACKING_STORE_OPEN_MAX,
642 // static
643 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
644 IndexedDBFactory* indexed_db_factory,
645 const GURL& origin_url,
646 const base::FilePath& path_base,
647 net::URLRequestContext* request_context,
648 blink::WebIDBDataLoss* data_loss,
649 std::string* data_loss_message,
650 bool* disk_full,
651 base::TaskRunner* task_runner,
652 bool clean_journal) {
653 *data_loss = blink::WebIDBDataLossNone;
654 DefaultLevelDBFactory leveldb_factory;
655 return IndexedDBBackingStore::Open(indexed_db_factory,
656 origin_url,
657 path_base,
658 request_context,
659 data_loss,
660 data_loss_message,
661 disk_full,
662 &leveldb_factory,
663 task_runner,
664 clean_journal);
667 static std::string OriginToCustomHistogramSuffix(const GURL& origin_url) {
668 if (origin_url.host() == "docs.google.com")
669 return ".Docs";
670 return std::string();
673 static void HistogramOpenStatus(IndexedDBBackingStoreOpenResult result,
674 const GURL& origin_url) {
675 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.BackingStore.OpenStatus",
676 result,
677 INDEXED_DB_BACKING_STORE_OPEN_MAX);
678 const std::string suffix = OriginToCustomHistogramSuffix(origin_url);
679 // Data from the WebCore.IndexedDB.BackingStore.OpenStatus histogram is used
680 // to generate a graph. So as not to alter the meaning of that graph,
681 // continue to collect all stats there (above) but also now collect docs stats
682 // separately (below).
683 if (!suffix.empty()) {
684 base::LinearHistogram::FactoryGet(
685 "WebCore.IndexedDB.BackingStore.OpenStatus" + suffix,
687 INDEXED_DB_BACKING_STORE_OPEN_MAX,
688 INDEXED_DB_BACKING_STORE_OPEN_MAX + 1,
689 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(result);
693 static bool IsPathTooLong(const base::FilePath& leveldb_dir) {
694 int limit = base::GetMaximumPathComponentLength(leveldb_dir.DirName());
695 if (limit == -1) {
696 DLOG(WARNING) << "GetMaximumPathComponentLength returned -1";
697 // In limited testing, ChromeOS returns 143, other OSes 255.
698 #if defined(OS_CHROMEOS)
699 limit = 143;
700 #else
701 limit = 255;
702 #endif
704 size_t component_length = leveldb_dir.BaseName().value().length();
705 if (component_length > static_cast<uint32_t>(limit)) {
706 DLOG(WARNING) << "Path component length (" << component_length
707 << ") exceeds maximum (" << limit
708 << ") allowed by this filesystem.";
709 const int min = 140;
710 const int max = 300;
711 const int num_buckets = 12;
712 UMA_HISTOGRAM_CUSTOM_COUNTS(
713 "WebCore.IndexedDB.BackingStore.OverlyLargeOriginLength",
714 component_length,
715 min,
716 max,
717 num_buckets);
718 return true;
720 return false;
723 leveldb::Status IndexedDBBackingStore::DestroyBackingStore(
724 const base::FilePath& path_base,
725 const GURL& origin_url) {
726 const base::FilePath file_path =
727 path_base.Append(ComputeFileName(origin_url));
728 DefaultLevelDBFactory leveldb_factory;
729 return leveldb_factory.DestroyLevelDB(file_path);
732 bool IndexedDBBackingStore::ReadCorruptionInfo(const base::FilePath& path_base,
733 const GURL& origin_url,
734 std::string& message) {
736 const base::FilePath info_path =
737 path_base.Append(ComputeCorruptionFileName(origin_url));
739 if (IsPathTooLong(info_path))
740 return false;
742 const int64 max_json_len = 4096;
743 int64 file_size(0);
744 if (!GetFileSize(info_path, &file_size) || file_size > max_json_len)
745 return false;
746 if (!file_size) {
747 NOTREACHED();
748 return false;
751 base::File file(info_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
752 bool success = false;
753 if (file.IsValid()) {
754 std::vector<char> bytes(file_size);
755 if (file_size == file.Read(0, &bytes[0], file_size)) {
756 std::string input_js(&bytes[0], file_size);
757 base::JSONReader reader;
758 scoped_ptr<base::Value> val(reader.ReadToValue(input_js));
759 if (val && val->GetType() == base::Value::TYPE_DICTIONARY) {
760 base::DictionaryValue* dict_val =
761 static_cast<base::DictionaryValue*>(val.get());
762 success = dict_val->GetString("message", &message);
765 file.Close();
768 base::DeleteFile(info_path, false);
770 return success;
773 bool IndexedDBBackingStore::RecordCorruptionInfo(
774 const base::FilePath& path_base,
775 const GURL& origin_url,
776 const std::string& message) {
777 const base::FilePath info_path =
778 path_base.Append(ComputeCorruptionFileName(origin_url));
779 if (IsPathTooLong(info_path))
780 return false;
782 base::DictionaryValue root_dict;
783 root_dict.SetString("message", message);
784 std::string output_js;
785 base::JSONWriter::Write(&root_dict, &output_js);
787 base::File file(info_path,
788 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
789 if (!file.IsValid())
790 return false;
791 int written = file.Write(0, output_js.c_str(), output_js.length());
792 return size_t(written) == output_js.length();
795 // static
796 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Open(
797 IndexedDBFactory* indexed_db_factory,
798 const GURL& origin_url,
799 const base::FilePath& path_base,
800 net::URLRequestContext* request_context,
801 blink::WebIDBDataLoss* data_loss,
802 std::string* data_loss_message,
803 bool* is_disk_full,
804 LevelDBFactory* leveldb_factory,
805 base::TaskRunner* task_runner,
806 bool clean_journal) {
807 IDB_TRACE("IndexedDBBackingStore::Open");
808 DCHECK(!path_base.empty());
809 *data_loss = blink::WebIDBDataLossNone;
810 *data_loss_message = "";
811 *is_disk_full = false;
813 scoped_ptr<LevelDBComparator> comparator(new Comparator());
815 if (!IsStringASCII(path_base.AsUTF8Unsafe())) {
816 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ATTEMPT_NON_ASCII,
817 origin_url);
819 if (!base::CreateDirectory(path_base)) {
820 LOG(ERROR) << "Unable to create IndexedDB database path "
821 << path_base.AsUTF8Unsafe();
822 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_DIRECTORY,
823 origin_url);
824 return scoped_refptr<IndexedDBBackingStore>();
827 const base::FilePath file_path =
828 path_base.Append(ComputeFileName(origin_url));
829 const base::FilePath blob_path =
830 path_base.Append(ComputeBlobPath(origin_url));
832 if (IsPathTooLong(file_path)) {
833 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_ORIGIN_TOO_LONG,
834 origin_url);
835 return scoped_refptr<IndexedDBBackingStore>();
838 scoped_ptr<LevelDBDatabase> db;
839 leveldb::Status status = leveldb_factory->OpenLevelDB(
840 file_path, comparator.get(), &db, is_disk_full);
842 DCHECK(!db == !status.ok());
843 if (!status.ok()) {
844 if (leveldb_env::IndicatesDiskFull(status)) {
845 *is_disk_full = true;
846 } else if (leveldb_env::IsCorruption(status)) {
847 *data_loss = blink::WebIDBDataLossTotal;
848 *data_loss_message = leveldb_env::GetCorruptionMessage(status);
852 bool is_schema_known = false;
853 if (db) {
854 std::string corruption_message;
855 if (ReadCorruptionInfo(path_base, origin_url, corruption_message)) {
856 LOG(ERROR) << "IndexedDB recovering from a corrupted (and deleted) "
857 "database.";
858 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_PRIOR_CORRUPTION,
859 origin_url);
860 db.reset();
861 *data_loss = blink::WebIDBDataLossTotal;
862 *data_loss_message =
863 "IndexedDB (database was corrupt): " + corruption_message;
864 } else if (!IsSchemaKnown(db.get(), &is_schema_known)) {
865 LOG(ERROR) << "IndexedDB had IO error checking schema, treating it as "
866 "failure to open";
867 HistogramOpenStatus(
868 INDEXED_DB_BACKING_STORE_OPEN_FAILED_IO_ERROR_CHECKING_SCHEMA,
869 origin_url);
870 db.reset();
871 *data_loss = blink::WebIDBDataLossTotal;
872 *data_loss_message = "I/O error checking schema";
873 } else if (!is_schema_known) {
874 LOG(ERROR) << "IndexedDB backing store had unknown schema, treating it "
875 "as failure to open";
876 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_SCHEMA,
877 origin_url);
878 db.reset();
879 *data_loss = blink::WebIDBDataLossTotal;
880 *data_loss_message = "Unknown schema";
884 DCHECK(status.ok() || !is_schema_known || leveldb_env::IsIOError(status) ||
885 leveldb_env::IsCorruption(status));
887 if (db) {
888 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_SUCCESS, origin_url);
889 } else if (leveldb_env::IsIOError(status)) {
890 LOG(ERROR) << "Unable to open backing store, not trying to recover - "
891 << status.ToString();
892 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_NO_RECOVERY, origin_url);
893 return scoped_refptr<IndexedDBBackingStore>();
894 } else {
895 DCHECK(!is_schema_known || leveldb_env::IsCorruption(status));
896 LOG(ERROR) << "IndexedDB backing store open failed, attempting cleanup";
897 status = leveldb_factory->DestroyLevelDB(file_path);
898 if (!status.ok()) {
899 LOG(ERROR) << "IndexedDB backing store cleanup failed";
900 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_DESTROY_FAILED,
901 origin_url);
902 return scoped_refptr<IndexedDBBackingStore>();
905 LOG(ERROR) << "IndexedDB backing store cleanup succeeded, reopening";
906 leveldb_factory->OpenLevelDB(file_path, comparator.get(), &db, NULL);
907 if (!db) {
908 LOG(ERROR) << "IndexedDB backing store reopen after recovery failed";
909 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_FAILED,
910 origin_url);
911 return scoped_refptr<IndexedDBBackingStore>();
913 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_CLEANUP_REOPEN_SUCCESS,
914 origin_url);
917 if (!db) {
918 NOTREACHED();
919 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_FAILED_UNKNOWN_ERR,
920 origin_url);
921 return scoped_refptr<IndexedDBBackingStore>();
924 return Create(indexed_db_factory,
925 origin_url,
926 blob_path,
927 request_context,
928 db.Pass(),
929 comparator.Pass(),
930 task_runner);
933 // static
934 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
935 const GURL& origin_url,
936 base::TaskRunner* task_runner) {
937 DefaultLevelDBFactory leveldb_factory;
938 return IndexedDBBackingStore::OpenInMemory(
939 origin_url, &leveldb_factory, task_runner);
942 // static
943 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::OpenInMemory(
944 const GURL& origin_url,
945 LevelDBFactory* leveldb_factory,
946 base::TaskRunner* task_runner) {
947 IDB_TRACE("IndexedDBBackingStore::OpenInMemory");
949 scoped_ptr<LevelDBComparator> comparator(new Comparator());
950 scoped_ptr<LevelDBDatabase> db =
951 LevelDBDatabase::OpenInMemory(comparator.get());
952 if (!db) {
953 LOG(ERROR) << "LevelDBDatabase::OpenInMemory failed.";
954 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_FAILED,
955 origin_url);
956 return scoped_refptr<IndexedDBBackingStore>();
958 HistogramOpenStatus(INDEXED_DB_BACKING_STORE_OPEN_MEMORY_SUCCESS, origin_url);
960 return Create(NULL /* indexed_db_factory */,
961 origin_url,
962 base::FilePath(),
963 NULL /* request_context */,
964 db.Pass(),
965 comparator.Pass(),
966 task_runner);
969 // static
970 scoped_refptr<IndexedDBBackingStore> IndexedDBBackingStore::Create(
971 IndexedDBFactory* indexed_db_factory,
972 const GURL& origin_url,
973 const base::FilePath& blob_path,
974 net::URLRequestContext* request_context,
975 scoped_ptr<LevelDBDatabase> db,
976 scoped_ptr<LevelDBComparator> comparator,
977 base::TaskRunner* task_runner) {
978 // TODO(jsbell): Handle comparator name changes.
979 scoped_refptr<IndexedDBBackingStore> backing_store(
980 new IndexedDBBackingStore(indexed_db_factory,
981 origin_url,
982 blob_path,
983 request_context,
984 db.Pass(),
985 comparator.Pass(),
986 task_runner));
987 if (!SetUpMetadata(backing_store->db_.get(),
988 backing_store->origin_identifier_))
989 return scoped_refptr<IndexedDBBackingStore>();
991 return backing_store;
994 void IndexedDBBackingStore::GrantChildProcessPermissions(int child_process_id) {
995 if (!child_process_ids_granted_.count(child_process_id)) {
996 child_process_ids_granted_.insert(child_process_id);
997 ChildProcessSecurityPolicyImpl::GetInstance()->GrantReadFile(
998 child_process_id, blob_path_);
1002 std::vector<base::string16> IndexedDBBackingStore::GetDatabaseNames(
1003 leveldb::Status* s) {
1004 *s = leveldb::Status::OK();
1005 std::vector<base::string16> found_names;
1006 const std::string start_key =
1007 DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier_);
1008 const std::string stop_key =
1009 DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier_);
1011 DCHECK(found_names.empty());
1013 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1014 for (*s = it->Seek(start_key);
1015 s->ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1016 *s = it->Next()) {
1017 StringPiece slice(it->Key());
1018 DatabaseNameKey database_name_key;
1019 if (!DatabaseNameKey::Decode(&slice, &database_name_key)) {
1020 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
1021 continue;
1023 found_names.push_back(database_name_key.database_name());
1026 if (!s->ok())
1027 INTERNAL_READ_ERROR_UNTESTED(GET_DATABASE_NAMES);
1029 return found_names;
1032 leveldb::Status IndexedDBBackingStore::GetIDBDatabaseMetaData(
1033 const base::string16& name,
1034 IndexedDBDatabaseMetadata* metadata,
1035 bool* found) {
1036 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1037 *found = false;
1039 leveldb::Status s = GetInt(db_.get(), key, &metadata->id, found);
1040 if (!s.ok()) {
1041 INTERNAL_READ_ERROR(GET_IDBDATABASE_METADATA);
1042 return s;
1044 if (!*found)
1045 return leveldb::Status::OK();
1047 s = GetString(db_.get(),
1048 DatabaseMetaDataKey::Encode(metadata->id,
1049 DatabaseMetaDataKey::USER_VERSION),
1050 &metadata->version,
1051 found);
1052 if (!s.ok()) {
1053 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1054 return s;
1056 if (!*found) {
1057 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1058 return InternalInconsistencyStatus();
1061 s = GetVarInt(db_.get(),
1062 DatabaseMetaDataKey::Encode(
1063 metadata->id, DatabaseMetaDataKey::USER_INT_VERSION),
1064 &metadata->int_version,
1065 found);
1066 if (!s.ok()) {
1067 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1068 return s;
1070 if (!*found) {
1071 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1072 return InternalInconsistencyStatus();
1075 if (metadata->int_version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION)
1076 metadata->int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1078 s = GetMaxObjectStoreId(
1079 db_.get(), metadata->id, &metadata->max_object_store_id);
1080 if (!s.ok()) {
1081 INTERNAL_READ_ERROR_UNTESTED(GET_IDBDATABASE_METADATA);
1084 return s;
1087 WARN_UNUSED_RESULT static leveldb::Status GetNewDatabaseId(
1088 LevelDBTransaction* transaction,
1089 int64* new_id) {
1090 *new_id = -1;
1091 int64 max_database_id = -1;
1092 bool found = false;
1093 leveldb::Status s =
1094 GetInt(transaction, MaxDatabaseIdKey::Encode(), &max_database_id, &found);
1095 if (!s.ok()) {
1096 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_DATABASE_ID);
1097 return s;
1099 if (!found)
1100 max_database_id = 0;
1102 DCHECK_GE(max_database_id, 0);
1104 int64 database_id = max_database_id + 1;
1105 PutInt(transaction, MaxDatabaseIdKey::Encode(), database_id);
1106 *new_id = database_id;
1107 return leveldb::Status::OK();
1110 leveldb::Status IndexedDBBackingStore::CreateIDBDatabaseMetaData(
1111 const base::string16& name,
1112 const base::string16& version,
1113 int64 int_version,
1114 int64* row_id) {
1115 scoped_refptr<LevelDBTransaction> transaction =
1116 new LevelDBTransaction(db_.get());
1118 leveldb::Status s = GetNewDatabaseId(transaction.get(), row_id);
1119 if (!s.ok())
1120 return s;
1121 DCHECK_GE(*row_id, 0);
1123 if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1124 int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1126 PutInt(transaction.get(),
1127 DatabaseNameKey::Encode(origin_identifier_, name),
1128 *row_id);
1129 PutString(
1130 transaction.get(),
1131 DatabaseMetaDataKey::Encode(*row_id, DatabaseMetaDataKey::USER_VERSION),
1132 version);
1133 PutVarInt(transaction.get(),
1134 DatabaseMetaDataKey::Encode(*row_id,
1135 DatabaseMetaDataKey::USER_INT_VERSION),
1136 int_version);
1137 s = transaction->Commit();
1138 if (!s.ok())
1139 INTERNAL_WRITE_ERROR_UNTESTED(CREATE_IDBDATABASE_METADATA);
1140 return s;
1143 bool IndexedDBBackingStore::UpdateIDBDatabaseIntVersion(
1144 IndexedDBBackingStore::Transaction* transaction,
1145 int64 row_id,
1146 int64 int_version) {
1147 if (int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1148 int_version = IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION;
1149 DCHECK_GE(int_version, 0) << "int_version was " << int_version;
1150 PutVarInt(transaction->transaction(),
1151 DatabaseMetaDataKey::Encode(row_id,
1152 DatabaseMetaDataKey::USER_INT_VERSION),
1153 int_version);
1154 return true;
1157 static leveldb::Status DeleteRange(LevelDBTransaction* transaction,
1158 const std::string& begin,
1159 const std::string& end) {
1160 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
1161 leveldb::Status s;
1162 for (s = it->Seek(begin);
1163 s.ok() && it->IsValid() && CompareKeys(it->Key(), end) < 0;
1164 s = it->Next())
1165 transaction->Remove(it->Key());
1166 return s;
1169 leveldb::Status IndexedDBBackingStore::DeleteDatabase(
1170 const base::string16& name) {
1171 IDB_TRACE("IndexedDBBackingStore::DeleteDatabase");
1172 scoped_ptr<LevelDBDirectTransaction> transaction =
1173 LevelDBDirectTransaction::Create(db_.get());
1175 IndexedDBDatabaseMetadata metadata;
1176 bool success = false;
1177 leveldb::Status s = GetIDBDatabaseMetaData(name, &metadata, &success);
1178 if (!s.ok())
1179 return s;
1180 if (!success)
1181 return leveldb::Status::OK();
1183 const std::string start_key = DatabaseMetaDataKey::Encode(
1184 metadata.id, DatabaseMetaDataKey::ORIGIN_NAME);
1185 const std::string stop_key = DatabaseMetaDataKey::Encode(
1186 metadata.id + 1, DatabaseMetaDataKey::ORIGIN_NAME);
1187 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1188 for (s = it->Seek(start_key);
1189 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1190 s = it->Next())
1191 transaction->Remove(it->Key());
1192 if (!s.ok()) {
1193 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1194 return s;
1197 const std::string key = DatabaseNameKey::Encode(origin_identifier_, name);
1198 transaction->Remove(key);
1200 // TODO(ericu): Put the real calls to the blob journal code here. For now,
1201 // I've inserted fake calls so that we don't get "you didn't use this static
1202 // function" compiler errors.
1203 if (false) {
1204 scoped_refptr<LevelDBTransaction> fake_transaction =
1205 new LevelDBTransaction(NULL);
1206 BlobJournalType fake_journal;
1207 MergeDatabaseIntoLiveBlobJournal(transaction.get(), metadata.id);
1208 UpdateBlobJournalWithDatabase(transaction.get(), metadata.id);
1209 MergeBlobsIntoLiveBlobJournal(fake_transaction.get(), fake_journal);
1212 s = transaction->Commit();
1213 if (!s.ok()) {
1214 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_DATABASE);
1215 return s;
1217 db_->Compact(start_key, stop_key);
1218 return s;
1221 static bool CheckObjectStoreAndMetaDataType(const LevelDBIterator* it,
1222 const std::string& stop_key,
1223 int64 object_store_id,
1224 int64 meta_data_type) {
1225 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
1226 return false;
1228 StringPiece slice(it->Key());
1229 ObjectStoreMetaDataKey meta_data_key;
1230 bool ok = ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key);
1231 DCHECK(ok);
1232 if (meta_data_key.ObjectStoreId() != object_store_id)
1233 return false;
1234 if (meta_data_key.MetaDataType() != meta_data_type)
1235 return false;
1236 return true;
1239 // TODO(jsbell): This should do some error handling rather than
1240 // plowing ahead when bad data is encountered.
1241 leveldb::Status IndexedDBBackingStore::GetObjectStores(
1242 int64 database_id,
1243 IndexedDBDatabaseMetadata::ObjectStoreMap* object_stores) {
1244 IDB_TRACE("IndexedDBBackingStore::GetObjectStores");
1245 if (!KeyPrefix::IsValidDatabaseId(database_id))
1246 return InvalidDBKeyStatus();
1247 const std::string start_key =
1248 ObjectStoreMetaDataKey::Encode(database_id, 1, 0);
1249 const std::string stop_key =
1250 ObjectStoreMetaDataKey::EncodeMaxKey(database_id);
1252 DCHECK(object_stores->empty());
1254 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
1255 leveldb::Status s = it->Seek(start_key);
1256 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
1257 StringPiece slice(it->Key());
1258 ObjectStoreMetaDataKey meta_data_key;
1259 bool ok = ObjectStoreMetaDataKey::Decode(&slice, &meta_data_key);
1260 DCHECK(ok);
1261 if (meta_data_key.MetaDataType() != ObjectStoreMetaDataKey::NAME) {
1262 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1263 // Possible stale metadata, but don't fail the load.
1264 s = it->Next();
1265 if (!s.ok())
1266 break;
1267 continue;
1270 int64 object_store_id = meta_data_key.ObjectStoreId();
1272 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
1273 // simplify.
1274 base::string16 object_store_name;
1276 StringPiece slice(it->Value());
1277 if (!DecodeString(&slice, &object_store_name) || !slice.empty())
1278 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1281 s = it->Next();
1282 if (!s.ok())
1283 break;
1284 if (!CheckObjectStoreAndMetaDataType(it.get(),
1285 stop_key,
1286 object_store_id,
1287 ObjectStoreMetaDataKey::KEY_PATH)) {
1288 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1289 break;
1291 IndexedDBKeyPath key_path;
1293 StringPiece slice(it->Value());
1294 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
1295 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1298 s = it->Next();
1299 if (!s.ok())
1300 break;
1301 if (!CheckObjectStoreAndMetaDataType(
1302 it.get(),
1303 stop_key,
1304 object_store_id,
1305 ObjectStoreMetaDataKey::AUTO_INCREMENT)) {
1306 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1307 break;
1309 bool auto_increment;
1311 StringPiece slice(it->Value());
1312 if (!DecodeBool(&slice, &auto_increment) || !slice.empty())
1313 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1316 s = it->Next(); // Is evictable.
1317 if (!s.ok())
1318 break;
1319 if (!CheckObjectStoreAndMetaDataType(it.get(),
1320 stop_key,
1321 object_store_id,
1322 ObjectStoreMetaDataKey::EVICTABLE)) {
1323 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1324 break;
1327 s = it->Next(); // Last version.
1328 if (!s.ok())
1329 break;
1330 if (!CheckObjectStoreAndMetaDataType(
1331 it.get(),
1332 stop_key,
1333 object_store_id,
1334 ObjectStoreMetaDataKey::LAST_VERSION)) {
1335 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1336 break;
1339 s = it->Next(); // Maximum index id allocated.
1340 if (!s.ok())
1341 break;
1342 if (!CheckObjectStoreAndMetaDataType(
1343 it.get(),
1344 stop_key,
1345 object_store_id,
1346 ObjectStoreMetaDataKey::MAX_INDEX_ID)) {
1347 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1348 break;
1350 int64 max_index_id;
1352 StringPiece slice(it->Value());
1353 if (!DecodeInt(&slice, &max_index_id) || !slice.empty())
1354 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1357 s = it->Next(); // [optional] has key path (is not null)
1358 if (!s.ok())
1359 break;
1360 if (CheckObjectStoreAndMetaDataType(it.get(),
1361 stop_key,
1362 object_store_id,
1363 ObjectStoreMetaDataKey::HAS_KEY_PATH)) {
1364 bool has_key_path;
1366 StringPiece slice(it->Value());
1367 if (!DecodeBool(&slice, &has_key_path))
1368 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1370 // This check accounts for two layers of legacy coding:
1371 // (1) Initially, has_key_path was added to distinguish null vs. string.
1372 // (2) Later, null vs. string vs. array was stored in the key_path itself.
1373 // So this check is only relevant for string-type key_paths.
1374 if (!has_key_path &&
1375 (key_path.type() == blink::WebIDBKeyPathTypeString &&
1376 !key_path.string().empty())) {
1377 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1378 break;
1380 if (!has_key_path)
1381 key_path = IndexedDBKeyPath();
1382 s = it->Next();
1383 if (!s.ok())
1384 break;
1387 int64 key_generator_current_number = -1;
1388 if (CheckObjectStoreAndMetaDataType(
1389 it.get(),
1390 stop_key,
1391 object_store_id,
1392 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER)) {
1393 StringPiece slice(it->Value());
1394 if (!DecodeInt(&slice, &key_generator_current_number) || !slice.empty())
1395 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_OBJECT_STORES);
1397 // TODO(jsbell): Return key_generator_current_number, cache in
1398 // object store, and write lazily to backing store. For now,
1399 // just assert that if it was written it was valid.
1400 DCHECK_GE(key_generator_current_number, kKeyGeneratorInitialNumber);
1401 s = it->Next();
1402 if (!s.ok())
1403 break;
1406 IndexedDBObjectStoreMetadata metadata(object_store_name,
1407 object_store_id,
1408 key_path,
1409 auto_increment,
1410 max_index_id);
1411 s = GetIndexes(database_id, object_store_id, &metadata.indexes);
1412 if (!s.ok())
1413 break;
1414 (*object_stores)[object_store_id] = metadata;
1417 if (!s.ok())
1418 INTERNAL_READ_ERROR_UNTESTED(GET_OBJECT_STORES);
1420 return s;
1423 WARN_UNUSED_RESULT static leveldb::Status SetMaxObjectStoreId(
1424 LevelDBTransaction* transaction,
1425 int64 database_id,
1426 int64 object_store_id) {
1427 const std::string max_object_store_id_key = DatabaseMetaDataKey::Encode(
1428 database_id, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID);
1429 int64 max_object_store_id = -1;
1430 leveldb::Status s = GetMaxObjectStoreId(
1431 transaction, max_object_store_id_key, &max_object_store_id);
1432 if (!s.ok()) {
1433 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1434 return s;
1437 if (object_store_id <= max_object_store_id) {
1438 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_OBJECT_STORE_ID);
1439 return InternalInconsistencyStatus();
1441 PutInt(transaction, max_object_store_id_key, object_store_id);
1442 return s;
1445 void IndexedDBBackingStore::Compact() { db_->CompactAll(); }
1447 leveldb::Status IndexedDBBackingStore::CreateObjectStore(
1448 IndexedDBBackingStore::Transaction* transaction,
1449 int64 database_id,
1450 int64 object_store_id,
1451 const base::string16& name,
1452 const IndexedDBKeyPath& key_path,
1453 bool auto_increment) {
1454 IDB_TRACE("IndexedDBBackingStore::CreateObjectStore");
1455 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1456 return InvalidDBKeyStatus();
1457 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1458 leveldb::Status s =
1459 SetMaxObjectStoreId(leveldb_transaction, database_id, object_store_id);
1460 if (!s.ok())
1461 return s;
1463 const std::string name_key = ObjectStoreMetaDataKey::Encode(
1464 database_id, object_store_id, ObjectStoreMetaDataKey::NAME);
1465 const std::string key_path_key = ObjectStoreMetaDataKey::Encode(
1466 database_id, object_store_id, ObjectStoreMetaDataKey::KEY_PATH);
1467 const std::string auto_increment_key = ObjectStoreMetaDataKey::Encode(
1468 database_id, object_store_id, ObjectStoreMetaDataKey::AUTO_INCREMENT);
1469 const std::string evictable_key = ObjectStoreMetaDataKey::Encode(
1470 database_id, object_store_id, ObjectStoreMetaDataKey::EVICTABLE);
1471 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1472 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1473 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
1474 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
1475 const std::string has_key_path_key = ObjectStoreMetaDataKey::Encode(
1476 database_id, object_store_id, ObjectStoreMetaDataKey::HAS_KEY_PATH);
1477 const std::string key_generator_current_number_key =
1478 ObjectStoreMetaDataKey::Encode(
1479 database_id,
1480 object_store_id,
1481 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
1482 const std::string names_key = ObjectStoreNamesKey::Encode(database_id, name);
1484 PutString(leveldb_transaction, name_key, name);
1485 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
1486 PutInt(leveldb_transaction, auto_increment_key, auto_increment);
1487 PutInt(leveldb_transaction, evictable_key, false);
1488 PutInt(leveldb_transaction, last_version_key, 1);
1489 PutInt(leveldb_transaction, max_index_id_key, kMinimumIndexId);
1490 PutBool(leveldb_transaction, has_key_path_key, !key_path.IsNull());
1491 PutInt(leveldb_transaction,
1492 key_generator_current_number_key,
1493 kKeyGeneratorInitialNumber);
1494 PutInt(leveldb_transaction, names_key, object_store_id);
1495 return s;
1498 leveldb::Status IndexedDBBackingStore::DeleteObjectStore(
1499 IndexedDBBackingStore::Transaction* transaction,
1500 int64 database_id,
1501 int64 object_store_id) {
1502 IDB_TRACE("IndexedDBBackingStore::DeleteObjectStore");
1503 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1504 return InvalidDBKeyStatus();
1505 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1507 base::string16 object_store_name;
1508 bool found = false;
1509 leveldb::Status s =
1510 GetString(leveldb_transaction,
1511 ObjectStoreMetaDataKey::Encode(
1512 database_id, object_store_id, ObjectStoreMetaDataKey::NAME),
1513 &object_store_name,
1514 &found);
1515 if (!s.ok()) {
1516 INTERNAL_READ_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1517 return s;
1519 if (!found) {
1520 INTERNAL_CONSISTENCY_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1521 return InternalInconsistencyStatus();
1524 s = DeleteRange(
1525 leveldb_transaction,
1526 ObjectStoreMetaDataKey::Encode(database_id, object_store_id, 0),
1527 ObjectStoreMetaDataKey::EncodeMaxKey(database_id, object_store_id));
1529 if (s.ok()) {
1530 leveldb_transaction->Remove(
1531 ObjectStoreNamesKey::Encode(database_id, object_store_name));
1533 s = DeleteRange(
1534 leveldb_transaction,
1535 IndexFreeListKey::Encode(database_id, object_store_id, 0),
1536 IndexFreeListKey::EncodeMaxKey(database_id, object_store_id));
1539 if (s.ok()) {
1540 s = DeleteRange(
1541 leveldb_transaction,
1542 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0),
1543 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id));
1546 if (!s.ok()) {
1547 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_OBJECT_STORE);
1548 return s;
1551 return ClearObjectStore(transaction, database_id, object_store_id);
1554 leveldb::Status IndexedDBBackingStore::GetRecord(
1555 IndexedDBBackingStore::Transaction* transaction,
1556 int64 database_id,
1557 int64 object_store_id,
1558 const IndexedDBKey& key,
1559 IndexedDBValue* record) {
1560 IDB_TRACE("IndexedDBBackingStore::GetRecord");
1561 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1562 return InvalidDBKeyStatus();
1563 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1565 const std::string leveldb_key =
1566 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1567 std::string data;
1569 record->clear();
1571 bool found = false;
1572 leveldb::Status s = leveldb_transaction->Get(leveldb_key, &data, &found);
1573 if (!s.ok()) {
1574 INTERNAL_READ_ERROR(GET_RECORD);
1575 return s;
1577 if (!found)
1578 return s;
1579 if (data.empty()) {
1580 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1581 return leveldb::Status::NotFound("Record contained no data");
1584 int64 version;
1585 StringPiece slice(data);
1586 if (!DecodeVarInt(&slice, &version)) {
1587 INTERNAL_READ_ERROR_UNTESTED(GET_RECORD);
1588 return InternalInconsistencyStatus();
1591 record->bits = slice.as_string();
1592 return s;
1595 WARN_UNUSED_RESULT static leveldb::Status GetNewVersionNumber(
1596 LevelDBTransaction* transaction,
1597 int64 database_id,
1598 int64 object_store_id,
1599 int64* new_version_number) {
1600 const std::string last_version_key = ObjectStoreMetaDataKey::Encode(
1601 database_id, object_store_id, ObjectStoreMetaDataKey::LAST_VERSION);
1603 *new_version_number = -1;
1604 int64 last_version = -1;
1605 bool found = false;
1606 leveldb::Status s =
1607 GetInt(transaction, last_version_key, &last_version, &found);
1608 if (!s.ok()) {
1609 INTERNAL_READ_ERROR_UNTESTED(GET_NEW_VERSION_NUMBER);
1610 return s;
1612 if (!found)
1613 last_version = 0;
1615 DCHECK_GE(last_version, 0);
1617 int64 version = last_version + 1;
1618 PutInt(transaction, last_version_key, version);
1620 // TODO(jsbell): Think about how we want to handle the overflow scenario.
1621 DCHECK(version > last_version);
1623 *new_version_number = version;
1624 return s;
1627 leveldb::Status IndexedDBBackingStore::PutRecord(
1628 IndexedDBBackingStore::Transaction* transaction,
1629 int64 database_id,
1630 int64 object_store_id,
1631 const IndexedDBKey& key,
1632 IndexedDBValue& value,
1633 ScopedVector<webkit_blob::BlobDataHandle>* handles,
1634 RecordIdentifier* record_identifier) {
1635 IDB_TRACE("IndexedDBBackingStore::PutRecord");
1636 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1637 return InvalidDBKeyStatus();
1638 DCHECK(key.IsValid());
1640 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1641 int64 version = -1;
1642 leveldb::Status s = GetNewVersionNumber(
1643 leveldb_transaction, database_id, object_store_id, &version);
1644 if (!s.ok())
1645 return s;
1646 DCHECK_GE(version, 0);
1647 const std::string object_store_data_key =
1648 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1650 std::string v;
1651 EncodeVarInt(version, &v);
1652 v.append(value.bits);
1654 leveldb_transaction->Put(object_store_data_key, &v);
1655 transaction->PutBlobInfo(database_id,
1656 object_store_id,
1657 object_store_data_key,
1658 &value.blob_info,
1659 handles);
1660 DCHECK(!handles->size());
1662 const std::string exists_entry_key =
1663 ExistsEntryKey::Encode(database_id, object_store_id, key);
1664 std::string version_encoded;
1665 EncodeInt(version, &version_encoded);
1666 leveldb_transaction->Put(exists_entry_key, &version_encoded);
1668 std::string key_encoded;
1669 EncodeIDBKey(key, &key_encoded);
1670 record_identifier->Reset(key_encoded, version);
1671 return s;
1674 leveldb::Status IndexedDBBackingStore::ClearObjectStore(
1675 IndexedDBBackingStore::Transaction* transaction,
1676 int64 database_id,
1677 int64 object_store_id) {
1678 IDB_TRACE("IndexedDBBackingStore::ClearObjectStore");
1679 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1680 return InvalidDBKeyStatus();
1681 const std::string start_key =
1682 KeyPrefix(database_id, object_store_id).Encode();
1683 const std::string stop_key =
1684 KeyPrefix(database_id, object_store_id + 1).Encode();
1686 leveldb::Status s =
1687 DeleteRange(transaction->transaction(), start_key, stop_key);
1688 if (!s.ok())
1689 INTERNAL_WRITE_ERROR(CLEAR_OBJECT_STORE);
1690 return s;
1693 leveldb::Status IndexedDBBackingStore::DeleteRecord(
1694 IndexedDBBackingStore::Transaction* transaction,
1695 int64 database_id,
1696 int64 object_store_id,
1697 const RecordIdentifier& record_identifier) {
1698 IDB_TRACE("IndexedDBBackingStore::DeleteRecord");
1699 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1700 return InvalidDBKeyStatus();
1701 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1703 const std::string object_store_data_key = ObjectStoreDataKey::Encode(
1704 database_id, object_store_id, record_identifier.primary_key());
1705 leveldb_transaction->Remove(object_store_data_key);
1706 transaction->PutBlobInfo(
1707 database_id, object_store_id, object_store_data_key, NULL, NULL);
1709 const std::string exists_entry_key = ExistsEntryKey::Encode(
1710 database_id, object_store_id, record_identifier.primary_key());
1711 leveldb_transaction->Remove(exists_entry_key);
1712 return leveldb::Status::OK();
1715 leveldb::Status IndexedDBBackingStore::GetKeyGeneratorCurrentNumber(
1716 IndexedDBBackingStore::Transaction* transaction,
1717 int64 database_id,
1718 int64 object_store_id,
1719 int64* key_generator_current_number) {
1720 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1721 return InvalidDBKeyStatus();
1722 LevelDBTransaction* leveldb_transaction = transaction->transaction();
1724 const std::string key_generator_current_number_key =
1725 ObjectStoreMetaDataKey::Encode(
1726 database_id,
1727 object_store_id,
1728 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
1730 *key_generator_current_number = -1;
1731 std::string data;
1733 bool found = false;
1734 leveldb::Status s =
1735 leveldb_transaction->Get(key_generator_current_number_key, &data, &found);
1736 if (!s.ok()) {
1737 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
1738 return s;
1740 if (found && !data.empty()) {
1741 StringPiece slice(data);
1742 if (!DecodeInt(&slice, key_generator_current_number) || !slice.empty()) {
1743 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
1744 return InternalInconsistencyStatus();
1746 return s;
1749 // Previously, the key generator state was not stored explicitly
1750 // but derived from the maximum numeric key present in existing
1751 // data. This violates the spec as the data may be cleared but the
1752 // key generator state must be preserved.
1753 // TODO(jsbell): Fix this for all stores on database open?
1754 const std::string start_key =
1755 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
1756 const std::string stop_key =
1757 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
1759 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
1760 int64 max_numeric_key = 0;
1762 for (s = it->Seek(start_key);
1763 s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0;
1764 s = it->Next()) {
1765 StringPiece slice(it->Key());
1766 ObjectStoreDataKey data_key;
1767 if (!ObjectStoreDataKey::Decode(&slice, &data_key)) {
1768 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
1769 return InternalInconsistencyStatus();
1771 scoped_ptr<IndexedDBKey> user_key = data_key.user_key();
1772 if (user_key->type() == blink::WebIDBKeyTypeNumber) {
1773 int64 n = static_cast<int64>(user_key->number());
1774 if (n > max_numeric_key)
1775 max_numeric_key = n;
1779 if (s.ok())
1780 *key_generator_current_number = max_numeric_key + 1;
1781 else
1782 INTERNAL_READ_ERROR_UNTESTED(GET_KEY_GENERATOR_CURRENT_NUMBER);
1784 return s;
1787 leveldb::Status IndexedDBBackingStore::MaybeUpdateKeyGeneratorCurrentNumber(
1788 IndexedDBBackingStore::Transaction* transaction,
1789 int64 database_id,
1790 int64 object_store_id,
1791 int64 new_number,
1792 bool check_current) {
1793 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1794 return InvalidDBKeyStatus();
1796 if (check_current) {
1797 int64 current_number;
1798 leveldb::Status s = GetKeyGeneratorCurrentNumber(
1799 transaction, database_id, object_store_id, &current_number);
1800 if (!s.ok())
1801 return s;
1802 if (new_number <= current_number)
1803 return s;
1806 const std::string key_generator_current_number_key =
1807 ObjectStoreMetaDataKey::Encode(
1808 database_id,
1809 object_store_id,
1810 ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER);
1811 PutInt(
1812 transaction->transaction(), key_generator_current_number_key, new_number);
1813 return leveldb::Status::OK();
1816 leveldb::Status IndexedDBBackingStore::KeyExistsInObjectStore(
1817 IndexedDBBackingStore::Transaction* transaction,
1818 int64 database_id,
1819 int64 object_store_id,
1820 const IndexedDBKey& key,
1821 RecordIdentifier* found_record_identifier,
1822 bool* found) {
1823 IDB_TRACE("IndexedDBBackingStore::KeyExistsInObjectStore");
1824 if (!KeyPrefix::ValidIds(database_id, object_store_id))
1825 return InvalidDBKeyStatus();
1826 *found = false;
1827 const std::string leveldb_key =
1828 ObjectStoreDataKey::Encode(database_id, object_store_id, key);
1829 std::string data;
1831 leveldb::Status s =
1832 transaction->transaction()->Get(leveldb_key, &data, found);
1833 if (!s.ok()) {
1834 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
1835 return s;
1837 if (!*found)
1838 return leveldb::Status::OK();
1839 if (!data.size()) {
1840 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_OBJECT_STORE);
1841 return InternalInconsistencyStatus();
1844 int64 version;
1845 StringPiece slice(data);
1846 if (!DecodeVarInt(&slice, &version))
1847 return InternalInconsistencyStatus();
1849 std::string encoded_key;
1850 EncodeIDBKey(key, &encoded_key);
1851 found_record_identifier->Reset(encoded_key, version);
1852 return s;
1855 class IndexedDBBackingStore::Transaction::ChainedBlobWriterImpl
1856 : public IndexedDBBackingStore::Transaction::ChainedBlobWriter {
1857 public:
1858 typedef IndexedDBBackingStore::Transaction::WriteDescriptorVec
1859 WriteDescriptorVec;
1860 ChainedBlobWriterImpl(
1861 int64 database_id,
1862 IndexedDBBackingStore* backingStore,
1863 WriteDescriptorVec& blobs,
1864 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback)
1865 : waiting_for_callback_(false),
1866 database_id_(database_id),
1867 backing_store_(backingStore),
1868 callback_(callback),
1869 aborted_(false) {
1870 blobs_.swap(blobs);
1871 iter_ = blobs_.begin();
1872 WriteNextFile();
1875 virtual void set_delegate(scoped_ptr<FileWriterDelegate> delegate) OVERRIDE {
1876 delegate_.reset(delegate.release());
1879 virtual void ReportWriteCompletion(bool succeeded,
1880 int64 bytes_written) OVERRIDE {
1881 // TODO(ericu): Check bytes_written against the blob's snapshot value.
1882 DCHECK(waiting_for_callback_);
1883 DCHECK(!succeeded || bytes_written >= 0);
1884 waiting_for_callback_ = false;
1885 if (delegate_.get()) // Only present for Blob, not File.
1886 content::BrowserThread::DeleteSoon(
1887 content::BrowserThread::IO, FROM_HERE, delegate_.release());
1888 if (aborted_) {
1889 self_ref_ = NULL;
1890 return;
1892 if (succeeded)
1893 WriteNextFile();
1894 else
1895 callback_->Run(false);
1898 virtual void Abort() OVERRIDE {
1899 if (!waiting_for_callback_)
1900 return;
1901 self_ref_ = this;
1902 aborted_ = true;
1905 private:
1906 virtual ~ChainedBlobWriterImpl() {}
1908 void WriteNextFile() {
1909 DCHECK(!waiting_for_callback_);
1910 DCHECK(!aborted_);
1911 if (iter_ == blobs_.end()) {
1912 DCHECK(!self_ref_);
1913 callback_->Run(true);
1914 return;
1915 } else {
1916 if (!backing_store_->WriteBlobFile(database_id_, *iter_, this)) {
1917 callback_->Run(false);
1918 return;
1920 waiting_for_callback_ = true;
1921 ++iter_;
1925 bool waiting_for_callback_;
1926 scoped_refptr<ChainedBlobWriterImpl> self_ref_;
1927 WriteDescriptorVec blobs_;
1928 WriteDescriptorVec::const_iterator iter_;
1929 int64 database_id_;
1930 IndexedDBBackingStore* backing_store_;
1931 scoped_refptr<IndexedDBBackingStore::BlobWriteCallback> callback_;
1932 scoped_ptr<FileWriterDelegate> delegate_;
1933 bool aborted_;
1936 class LocalWriteClosure : public FileWriterDelegate::DelegateWriteCallback,
1937 public base::RefCounted<LocalWriteClosure> {
1938 public:
1939 LocalWriteClosure(IndexedDBBackingStore::Transaction::ChainedBlobWriter*
1940 chained_blob_writer_,
1941 base::TaskRunner* task_runner)
1942 : chained_blob_writer_(chained_blob_writer_),
1943 task_runner_(task_runner),
1944 bytes_written_(-1) {}
1946 void Run(base::File::Error rv,
1947 int64 bytes,
1948 FileWriterDelegate::WriteProgressStatus write_status) {
1949 if (write_status == FileWriterDelegate::SUCCESS_IO_PENDING)
1950 return; // We don't care about progress events.
1951 if (rv == base::File::FILE_OK) {
1952 DCHECK(bytes >= 0);
1953 DCHECK(write_status == FileWriterDelegate::SUCCESS_COMPLETED);
1954 bytes_written_ = bytes;
1955 } else {
1956 DCHECK(write_status == FileWriterDelegate::ERROR_WRITE_STARTED ||
1957 write_status == FileWriterDelegate::ERROR_WRITE_NOT_STARTED);
1959 task_runner_->PostTask(
1960 FROM_HERE,
1961 base::Bind(&LocalWriteClosure::callBlobCallbackOnIDBTaskRunner,
1962 this,
1963 write_status == FileWriterDelegate::SUCCESS_COMPLETED));
1966 void writeBlobToFileOnIOThread(const FilePath& file_path,
1967 const GURL& blob_url,
1968 net::URLRequestContext* request_context) {
1969 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
1970 scoped_ptr<fileapi::FileStreamWriter> writer(
1971 fileapi::FileStreamWriter::CreateForLocalFile(
1972 task_runner_, file_path, 0,
1973 fileapi::FileStreamWriter::CREATE_NEW_FILE));
1974 scoped_ptr<FileWriterDelegate> delegate(
1975 new FileWriterDelegate(writer.Pass()));
1977 DCHECK(blob_url.is_valid());
1978 scoped_ptr<net::URLRequest> blob_request(request_context->CreateRequest(
1979 blob_url, net::DEFAULT_PRIORITY, delegate.get(), NULL));
1981 delegate->Start(blob_request.Pass(),
1982 base::Bind(&LocalWriteClosure::Run, this));
1983 chained_blob_writer_->set_delegate(delegate.Pass());
1986 private:
1987 virtual ~LocalWriteClosure() {}
1988 friend class base::RefCounted<LocalWriteClosure>;
1990 void callBlobCallbackOnIDBTaskRunner(bool succeeded) {
1991 DCHECK(task_runner_->RunsTasksOnCurrentThread());
1992 chained_blob_writer_->ReportWriteCompletion(succeeded, bytes_written_);
1995 IndexedDBBackingStore::Transaction::ChainedBlobWriter* chained_blob_writer_;
1996 base::TaskRunner* task_runner_;
1997 int64 bytes_written_;
2000 bool IndexedDBBackingStore::WriteBlobFile(
2001 int64 database_id,
2002 const Transaction::WriteDescriptor& descriptor,
2003 Transaction::ChainedBlobWriter* chained_blob_writer) {
2005 if (!MakeIDBBlobDirectory(blob_path_, database_id, descriptor.key()))
2006 return false;
2008 FilePath path = GetBlobFileName(database_id, descriptor.key());
2010 if (descriptor.is_file()) {
2011 DCHECK(!descriptor.file_path().empty());
2012 if (!base::CopyFile(descriptor.file_path(), path))
2013 return false;
2015 base::File::Info info;
2016 if (base::GetFileInfo(descriptor.file_path(), &info)) {
2017 // TODO(ericu): Validate the snapshot date here. Expand WriteDescriptor
2018 // to include snapshot date and file size, and check both.
2019 if (!base::TouchFile(path, info.last_accessed, info.last_modified)) {
2020 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2022 } else {
2023 // TODO(ericu): Complain quietly; timestamp's probably not vital.
2026 task_runner_->PostTask(
2027 FROM_HERE,
2028 base::Bind(&Transaction::ChainedBlobWriter::ReportWriteCompletion,
2029 chained_blob_writer,
2030 true,
2031 info.size));
2032 } else {
2033 DCHECK(descriptor.url().is_valid());
2034 scoped_refptr<LocalWriteClosure> write_closure(
2035 new LocalWriteClosure(chained_blob_writer, task_runner_));
2036 content::BrowserThread::PostTask(
2037 content::BrowserThread::IO,
2038 FROM_HERE,
2039 base::Bind(&LocalWriteClosure::writeBlobToFileOnIOThread,
2040 write_closure.get(),
2041 path,
2042 descriptor.url(),
2043 request_context_));
2045 return true;
2048 void IndexedDBBackingStore::ReportBlobUnused(int64 database_id,
2049 int64 blob_key) {
2050 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2051 bool all_blobs = blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2052 DCHECK(all_blobs || DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2053 scoped_refptr<LevelDBTransaction> transaction =
2054 new LevelDBTransaction(db_.get());
2056 std::string live_blob_key = LiveBlobJournalKey::Encode();
2057 BlobJournalType live_blob_journal;
2058 if (!GetBlobJournal(live_blob_key, transaction.get(), &live_blob_journal)
2059 .ok())
2060 return;
2061 DCHECK(live_blob_journal.size());
2063 std::string primary_key = BlobJournalKey::Encode();
2064 BlobJournalType primary_journal;
2065 if (!GetBlobJournal(primary_key, transaction.get(), &primary_journal).ok())
2066 return;
2068 // There are several cases to handle. If blob_key is kAllBlobsKey, we want to
2069 // remove all entries with database_id from the live_blob journal and add only
2070 // kAllBlobsKey to the primary journal. Otherwise if IsValidBlobKey(blob_key)
2071 // and we hit kAllBlobsKey for the right database_id in the journal, we leave
2072 // the kAllBlobsKey entry in the live_blob journal but add the specific blob
2073 // to the primary. Otherwise if IsValidBlobKey(blob_key) and we find a
2074 // matching (database_id, blob_key) tuple, we should move it to the primary
2075 // journal.
2076 BlobJournalType new_live_blob_journal;
2077 for (BlobJournalType::iterator journal_iter = live_blob_journal.begin();
2078 journal_iter != live_blob_journal.end();
2079 ++journal_iter) {
2080 int64 current_database_id = journal_iter->first;
2081 int64 current_blob_key = journal_iter->second;
2082 bool current_all_blobs =
2083 current_blob_key == DatabaseMetaDataKey::kAllBlobsKey;
2084 DCHECK(KeyPrefix::IsValidDatabaseId(current_database_id) ||
2085 current_all_blobs);
2086 if (current_database_id == database_id &&
2087 (all_blobs || current_all_blobs || blob_key == current_blob_key)) {
2088 if (!all_blobs) {
2089 primary_journal.push_back(
2090 std::make_pair(database_id, current_blob_key));
2091 if (current_all_blobs)
2092 new_live_blob_journal.push_back(*journal_iter);
2093 new_live_blob_journal.insert(new_live_blob_journal.end(),
2094 ++journal_iter,
2095 live_blob_journal.end()); // All the rest.
2096 break;
2098 } else {
2099 new_live_blob_journal.push_back(*journal_iter);
2102 if (all_blobs) {
2103 primary_journal.push_back(
2104 std::make_pair(database_id, DatabaseMetaDataKey::kAllBlobsKey));
2106 UpdatePrimaryJournalWithBlobList(transaction.get(), primary_journal);
2107 UpdateLiveBlobJournalWithBlobList(transaction.get(), new_live_blob_journal);
2108 transaction->Commit();
2109 // We could just do the deletions/cleaning here, but if there are a lot of
2110 // blobs about to be garbage collected, it'd be better to wait and do them all
2111 // at once.
2112 StartJournalCleaningTimer();
2115 // The this reference is a raw pointer that's declared Unretained inside the
2116 // timer code, so this won't confuse IndexedDBFactory's check for
2117 // HasLastBackingStoreReference. It's safe because if the backing store is
2118 // deleted, the timer will automatically be canceled on destruction.
2119 void IndexedDBBackingStore::StartJournalCleaningTimer() {
2120 journal_cleaning_timer_.Start(
2121 FROM_HERE,
2122 base::TimeDelta::FromSeconds(5),
2123 this,
2124 &IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn);
2127 // This assumes a file path of dbId/second-to-LSB-of-counter/counter.
2128 FilePath IndexedDBBackingStore::GetBlobFileName(int64 database_id, int64 key) {
2129 return GetBlobFileNameForKey(blob_path_, database_id, key);
2132 static bool CheckIndexAndMetaDataKey(const LevelDBIterator* it,
2133 const std::string& stop_key,
2134 int64 index_id,
2135 unsigned char meta_data_type) {
2136 if (!it->IsValid() || CompareKeys(it->Key(), stop_key) >= 0)
2137 return false;
2139 StringPiece slice(it->Key());
2140 IndexMetaDataKey meta_data_key;
2141 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2142 DCHECK(ok);
2143 if (meta_data_key.IndexId() != index_id)
2144 return false;
2145 if (meta_data_key.meta_data_type() != meta_data_type)
2146 return false;
2147 return true;
2150 // TODO(jsbell): This should do some error handling rather than plowing ahead
2151 // when bad data is encountered.
2152 leveldb::Status IndexedDBBackingStore::GetIndexes(
2153 int64 database_id,
2154 int64 object_store_id,
2155 IndexedDBObjectStoreMetadata::IndexMap* indexes) {
2156 IDB_TRACE("IndexedDBBackingStore::GetIndexes");
2157 if (!KeyPrefix::ValidIds(database_id, object_store_id))
2158 return InvalidDBKeyStatus();
2159 const std::string start_key =
2160 IndexMetaDataKey::Encode(database_id, object_store_id, 0, 0);
2161 const std::string stop_key =
2162 IndexMetaDataKey::Encode(database_id, object_store_id + 1, 0, 0);
2164 DCHECK(indexes->empty());
2166 scoped_ptr<LevelDBIterator> it = db_->CreateIterator();
2167 leveldb::Status s = it->Seek(start_key);
2168 while (s.ok() && it->IsValid() && CompareKeys(it->Key(), stop_key) < 0) {
2169 StringPiece slice(it->Key());
2170 IndexMetaDataKey meta_data_key;
2171 bool ok = IndexMetaDataKey::Decode(&slice, &meta_data_key);
2172 DCHECK(ok);
2173 if (meta_data_key.meta_data_type() != IndexMetaDataKey::NAME) {
2174 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2175 // Possible stale metadata due to http://webkit.org/b/85557 but don't fail
2176 // the load.
2177 s = it->Next();
2178 if (!s.ok())
2179 break;
2180 continue;
2183 // TODO(jsbell): Do this by direct key lookup rather than iteration, to
2184 // simplify.
2185 int64 index_id = meta_data_key.IndexId();
2186 base::string16 index_name;
2188 StringPiece slice(it->Value());
2189 if (!DecodeString(&slice, &index_name) || !slice.empty())
2190 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2193 s = it->Next(); // unique flag
2194 if (!s.ok())
2195 break;
2196 if (!CheckIndexAndMetaDataKey(
2197 it.get(), stop_key, index_id, IndexMetaDataKey::UNIQUE)) {
2198 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2199 break;
2201 bool index_unique;
2203 StringPiece slice(it->Value());
2204 if (!DecodeBool(&slice, &index_unique) || !slice.empty())
2205 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2208 s = it->Next(); // key_path
2209 if (!s.ok())
2210 break;
2211 if (!CheckIndexAndMetaDataKey(
2212 it.get(), stop_key, index_id, IndexMetaDataKey::KEY_PATH)) {
2213 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2214 break;
2216 IndexedDBKeyPath key_path;
2218 StringPiece slice(it->Value());
2219 if (!DecodeIDBKeyPath(&slice, &key_path) || !slice.empty())
2220 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2223 s = it->Next(); // [optional] multi_entry flag
2224 if (!s.ok())
2225 break;
2226 bool index_multi_entry = false;
2227 if (CheckIndexAndMetaDataKey(
2228 it.get(), stop_key, index_id, IndexMetaDataKey::MULTI_ENTRY)) {
2229 StringPiece slice(it->Value());
2230 if (!DecodeBool(&slice, &index_multi_entry) || !slice.empty())
2231 INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_INDEXES);
2233 s = it->Next();
2234 if (!s.ok())
2235 break;
2238 (*indexes)[index_id] = IndexedDBIndexMetadata(
2239 index_name, index_id, key_path, index_unique, index_multi_entry);
2242 if (!s.ok())
2243 INTERNAL_READ_ERROR_UNTESTED(GET_INDEXES);
2245 return s;
2248 bool IndexedDBBackingStore::RemoveBlobFile(int64 database_id, int64 key) {
2249 FilePath fileName = GetBlobFileName(database_id, key);
2250 return base::DeleteFile(fileName, false);
2253 bool IndexedDBBackingStore::RemoveBlobDirectory(int64 database_id) {
2254 FilePath dirName = GetBlobDirectoryName(blob_path_, database_id);
2255 return base::DeleteFile(dirName, true);
2258 leveldb::Status IndexedDBBackingStore::CleanUpBlobJournal(
2259 const std::string& level_db_key) {
2260 scoped_refptr<LevelDBTransaction> journal_transaction =
2261 new LevelDBTransaction(db_.get());
2262 BlobJournalType journal;
2263 leveldb::Status s =
2264 GetBlobJournal(level_db_key, journal_transaction.get(), &journal);
2265 if (!s.ok())
2266 return s;
2267 if (!journal.size())
2268 return leveldb::Status::OK();
2269 BlobJournalType::iterator journal_iter;
2270 for (journal_iter = journal.begin(); journal_iter != journal.end();
2271 ++journal_iter) {
2272 int64 database_id = journal_iter->first;
2273 int64 blob_key = journal_iter->second;
2274 DCHECK(KeyPrefix::IsValidDatabaseId(database_id));
2275 if (blob_key == DatabaseMetaDataKey::kAllBlobsKey) {
2276 if (!RemoveBlobDirectory(database_id))
2277 return IOErrorStatus();
2278 } else {
2279 DCHECK(DatabaseMetaDataKey::IsValidBlobKey(blob_key));
2280 if (!RemoveBlobFile(database_id, blob_key))
2281 return IOErrorStatus();
2284 ClearBlobJournal(journal_transaction.get(), level_db_key);
2285 return journal_transaction->Commit();
2288 void IndexedDBBackingStore::CleanPrimaryJournalIgnoreReturn() {
2289 CleanUpBlobJournal(BlobJournalKey::Encode());
2292 WARN_UNUSED_RESULT static leveldb::Status SetMaxIndexId(
2293 LevelDBTransaction* transaction,
2294 int64 database_id,
2295 int64 object_store_id,
2296 int64 index_id) {
2297 int64 max_index_id = -1;
2298 const std::string max_index_id_key = ObjectStoreMetaDataKey::Encode(
2299 database_id, object_store_id, ObjectStoreMetaDataKey::MAX_INDEX_ID);
2300 bool found = false;
2301 leveldb::Status s =
2302 GetInt(transaction, max_index_id_key, &max_index_id, &found);
2303 if (!s.ok()) {
2304 INTERNAL_READ_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2305 return s;
2307 if (!found)
2308 max_index_id = kMinimumIndexId;
2310 if (index_id <= max_index_id) {
2311 INTERNAL_CONSISTENCY_ERROR_UNTESTED(SET_MAX_INDEX_ID);
2312 return InternalInconsistencyStatus();
2315 PutInt(transaction, max_index_id_key, index_id);
2316 return s;
2319 leveldb::Status IndexedDBBackingStore::CreateIndex(
2320 IndexedDBBackingStore::Transaction* transaction,
2321 int64 database_id,
2322 int64 object_store_id,
2323 int64 index_id,
2324 const base::string16& name,
2325 const IndexedDBKeyPath& key_path,
2326 bool is_unique,
2327 bool is_multi_entry) {
2328 IDB_TRACE("IndexedDBBackingStore::CreateIndex");
2329 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2330 return InvalidDBKeyStatus();
2331 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2332 leveldb::Status s = SetMaxIndexId(
2333 leveldb_transaction, database_id, object_store_id, index_id);
2335 if (!s.ok())
2336 return s;
2338 const std::string name_key = IndexMetaDataKey::Encode(
2339 database_id, object_store_id, index_id, IndexMetaDataKey::NAME);
2340 const std::string unique_key = IndexMetaDataKey::Encode(
2341 database_id, object_store_id, index_id, IndexMetaDataKey::UNIQUE);
2342 const std::string key_path_key = IndexMetaDataKey::Encode(
2343 database_id, object_store_id, index_id, IndexMetaDataKey::KEY_PATH);
2344 const std::string multi_entry_key = IndexMetaDataKey::Encode(
2345 database_id, object_store_id, index_id, IndexMetaDataKey::MULTI_ENTRY);
2347 PutString(leveldb_transaction, name_key, name);
2348 PutBool(leveldb_transaction, unique_key, is_unique);
2349 PutIDBKeyPath(leveldb_transaction, key_path_key, key_path);
2350 PutBool(leveldb_transaction, multi_entry_key, is_multi_entry);
2351 return s;
2354 leveldb::Status IndexedDBBackingStore::DeleteIndex(
2355 IndexedDBBackingStore::Transaction* transaction,
2356 int64 database_id,
2357 int64 object_store_id,
2358 int64 index_id) {
2359 IDB_TRACE("IndexedDBBackingStore::DeleteIndex");
2360 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2361 return InvalidDBKeyStatus();
2362 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2364 const std::string index_meta_data_start =
2365 IndexMetaDataKey::Encode(database_id, object_store_id, index_id, 0);
2366 const std::string index_meta_data_end =
2367 IndexMetaDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2368 leveldb::Status s = DeleteRange(
2369 leveldb_transaction, index_meta_data_start, index_meta_data_end);
2371 if (s.ok()) {
2372 const std::string index_data_start =
2373 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
2374 const std::string index_data_end =
2375 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
2376 s = DeleteRange(leveldb_transaction, index_data_start, index_data_end);
2379 if (!s.ok())
2380 INTERNAL_WRITE_ERROR_UNTESTED(DELETE_INDEX);
2382 return s;
2385 leveldb::Status IndexedDBBackingStore::PutIndexDataForRecord(
2386 IndexedDBBackingStore::Transaction* transaction,
2387 int64 database_id,
2388 int64 object_store_id,
2389 int64 index_id,
2390 const IndexedDBKey& key,
2391 const RecordIdentifier& record_identifier) {
2392 IDB_TRACE("IndexedDBBackingStore::PutIndexDataForRecord");
2393 DCHECK(key.IsValid());
2394 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2395 return InvalidDBKeyStatus();
2397 std::string encoded_key;
2398 EncodeIDBKey(key, &encoded_key);
2400 const std::string index_data_key =
2401 IndexDataKey::Encode(database_id,
2402 object_store_id,
2403 index_id,
2404 encoded_key,
2405 record_identifier.primary_key(),
2408 std::string data;
2409 EncodeVarInt(record_identifier.version(), &data);
2410 data.append(record_identifier.primary_key());
2412 transaction->transaction()->Put(index_data_key, &data);
2413 return leveldb::Status::OK();
2416 static bool FindGreatestKeyLessThanOrEqual(LevelDBTransaction* transaction,
2417 const std::string& target,
2418 std::string* found_key,
2419 leveldb::Status& s) {
2420 scoped_ptr<LevelDBIterator> it = transaction->CreateIterator();
2421 s = it->Seek(target);
2422 if (!s.ok())
2423 return false;
2425 if (!it->IsValid()) {
2426 s = it->SeekToLast();
2427 if (!s.ok() || !it->IsValid())
2428 return false;
2431 while (CompareIndexKeys(it->Key(), target) > 0) {
2432 s = it->Prev();
2433 if (!s.ok() || !it->IsValid())
2434 return false;
2437 do {
2438 *found_key = it->Key().as_string();
2440 // There can be several index keys that compare equal. We want the last one.
2441 s = it->Next();
2442 } while (s.ok() && it->IsValid() && !CompareIndexKeys(it->Key(), target));
2444 return true;
2447 static leveldb::Status VersionExists(LevelDBTransaction* transaction,
2448 int64 database_id,
2449 int64 object_store_id,
2450 int64 version,
2451 const std::string& encoded_primary_key,
2452 bool* exists) {
2453 const std::string key =
2454 ExistsEntryKey::Encode(database_id, object_store_id, encoded_primary_key);
2455 std::string data;
2457 leveldb::Status s = transaction->Get(key, &data, exists);
2458 if (!s.ok()) {
2459 INTERNAL_READ_ERROR_UNTESTED(VERSION_EXISTS);
2460 return s;
2462 if (!*exists)
2463 return s;
2465 StringPiece slice(data);
2466 int64 decoded;
2467 if (!DecodeInt(&slice, &decoded) || !slice.empty())
2468 return InternalInconsistencyStatus();
2469 *exists = (decoded == version);
2470 return s;
2473 leveldb::Status IndexedDBBackingStore::FindKeyInIndex(
2474 IndexedDBBackingStore::Transaction* transaction,
2475 int64 database_id,
2476 int64 object_store_id,
2477 int64 index_id,
2478 const IndexedDBKey& key,
2479 std::string* found_encoded_primary_key,
2480 bool* found) {
2481 IDB_TRACE("IndexedDBBackingStore::FindKeyInIndex");
2482 DCHECK(KeyPrefix::ValidIds(database_id, object_store_id, index_id));
2484 DCHECK(found_encoded_primary_key->empty());
2485 *found = false;
2487 LevelDBTransaction* leveldb_transaction = transaction->transaction();
2488 const std::string leveldb_key =
2489 IndexDataKey::Encode(database_id, object_store_id, index_id, key);
2490 scoped_ptr<LevelDBIterator> it = leveldb_transaction->CreateIterator();
2491 leveldb::Status s = it->Seek(leveldb_key);
2492 if (!s.ok()) {
2493 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
2494 return s;
2497 for (;;) {
2498 if (!it->IsValid())
2499 return leveldb::Status::OK();
2500 if (CompareIndexKeys(it->Key(), leveldb_key) > 0)
2501 return leveldb::Status::OK();
2503 StringPiece slice(it->Value());
2505 int64 version;
2506 if (!DecodeVarInt(&slice, &version)) {
2507 INTERNAL_READ_ERROR_UNTESTED(FIND_KEY_IN_INDEX);
2508 return InternalInconsistencyStatus();
2510 *found_encoded_primary_key = slice.as_string();
2512 bool exists = false;
2513 s = VersionExists(leveldb_transaction,
2514 database_id,
2515 object_store_id,
2516 version,
2517 *found_encoded_primary_key,
2518 &exists);
2519 if (!s.ok())
2520 return s;
2521 if (!exists) {
2522 // Delete stale index data entry and continue.
2523 leveldb_transaction->Remove(it->Key());
2524 s = it->Next();
2525 continue;
2527 *found = true;
2528 return s;
2532 leveldb::Status IndexedDBBackingStore::GetPrimaryKeyViaIndex(
2533 IndexedDBBackingStore::Transaction* transaction,
2534 int64 database_id,
2535 int64 object_store_id,
2536 int64 index_id,
2537 const IndexedDBKey& key,
2538 scoped_ptr<IndexedDBKey>* primary_key) {
2539 IDB_TRACE("IndexedDBBackingStore::GetPrimaryKeyViaIndex");
2540 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2541 return InvalidDBKeyStatus();
2543 bool found = false;
2544 std::string found_encoded_primary_key;
2545 leveldb::Status s = FindKeyInIndex(transaction,
2546 database_id,
2547 object_store_id,
2548 index_id,
2549 key,
2550 &found_encoded_primary_key,
2551 &found);
2552 if (!s.ok()) {
2553 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
2554 return s;
2556 if (!found)
2557 return s;
2558 if (!found_encoded_primary_key.size()) {
2559 INTERNAL_READ_ERROR_UNTESTED(GET_PRIMARY_KEY_VIA_INDEX);
2560 return InvalidDBKeyStatus();
2563 StringPiece slice(found_encoded_primary_key);
2564 if (DecodeIDBKey(&slice, primary_key) && slice.empty())
2565 return s;
2566 else
2567 return InvalidDBKeyStatus();
2570 leveldb::Status IndexedDBBackingStore::KeyExistsInIndex(
2571 IndexedDBBackingStore::Transaction* transaction,
2572 int64 database_id,
2573 int64 object_store_id,
2574 int64 index_id,
2575 const IndexedDBKey& index_key,
2576 scoped_ptr<IndexedDBKey>* found_primary_key,
2577 bool* exists) {
2578 IDB_TRACE("IndexedDBBackingStore::KeyExistsInIndex");
2579 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
2580 return InvalidDBKeyStatus();
2582 *exists = false;
2583 std::string found_encoded_primary_key;
2584 leveldb::Status s = FindKeyInIndex(transaction,
2585 database_id,
2586 object_store_id,
2587 index_id,
2588 index_key,
2589 &found_encoded_primary_key,
2590 exists);
2591 if (!s.ok()) {
2592 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
2593 return s;
2595 if (!*exists)
2596 return leveldb::Status::OK();
2597 if (found_encoded_primary_key.empty()) {
2598 INTERNAL_READ_ERROR_UNTESTED(KEY_EXISTS_IN_INDEX);
2599 return InvalidDBKeyStatus();
2602 StringPiece slice(found_encoded_primary_key);
2603 if (DecodeIDBKey(&slice, found_primary_key) && slice.empty())
2604 return s;
2605 else
2606 return InvalidDBKeyStatus();
2609 IndexedDBBackingStore::Cursor::Cursor(
2610 const IndexedDBBackingStore::Cursor* other)
2611 : transaction_(other->transaction_),
2612 cursor_options_(other->cursor_options_),
2613 current_key_(new IndexedDBKey(*other->current_key_)) {
2614 if (other->iterator_) {
2615 iterator_ = transaction_->CreateIterator();
2617 if (other->iterator_->IsValid()) {
2618 leveldb::Status s = iterator_->Seek(other->iterator_->Key());
2619 // TODO(cmumford): Handle this error (crbug.com/363397)
2620 DCHECK(iterator_->IsValid());
2625 IndexedDBBackingStore::Cursor::Cursor(LevelDBTransaction* transaction,
2626 const CursorOptions& cursor_options)
2627 : transaction_(transaction), cursor_options_(cursor_options) {}
2628 IndexedDBBackingStore::Cursor::~Cursor() {}
2630 bool IndexedDBBackingStore::Cursor::FirstSeek(leveldb::Status* s) {
2631 iterator_ = transaction_->CreateIterator();
2632 if (cursor_options_.forward)
2633 *s = iterator_->Seek(cursor_options_.low_key);
2634 else
2635 *s = iterator_->Seek(cursor_options_.high_key);
2636 if (!s->ok())
2637 return false;
2639 return Continue(0, READY, s);
2642 bool IndexedDBBackingStore::Cursor::Advance(uint32 count, leveldb::Status* s) {
2643 *s = leveldb::Status::OK();
2644 while (count--) {
2645 if (!Continue(s))
2646 return false;
2648 return true;
2651 bool IndexedDBBackingStore::Cursor::Continue(const IndexedDBKey* key,
2652 const IndexedDBKey* primary_key,
2653 IteratorState next_state,
2654 leveldb::Status* s) {
2655 DCHECK(!key || key->IsValid());
2656 DCHECK(!primary_key || primary_key->IsValid());
2657 *s = leveldb::Status::OK();
2659 // TODO(alecflett): avoid a copy here?
2660 IndexedDBKey previous_key = current_key_ ? *current_key_ : IndexedDBKey();
2662 bool first_iteration = true;
2664 // When iterating with PrevNoDuplicate, spec requires that the
2665 // value we yield for each key is the first duplicate in forwards
2666 // order.
2667 IndexedDBKey last_duplicate_key;
2669 bool forward = cursor_options_.forward;
2671 for (;;) {
2672 if (next_state == SEEK) {
2673 // TODO(jsbell): Optimize seeking for reverse cursors as well.
2674 if (first_iteration && key && forward) {
2675 std::string leveldb_key;
2676 if (primary_key) {
2677 leveldb_key = EncodeKey(*key, *primary_key);
2678 } else {
2679 leveldb_key = EncodeKey(*key);
2681 *s = iterator_->Seek(leveldb_key);
2682 first_iteration = false;
2683 } else if (forward) {
2684 *s = iterator_->Next();
2685 } else {
2686 *s = iterator_->Prev();
2688 if (!s->ok())
2689 return false;
2690 } else {
2691 next_state = SEEK; // for subsequent iterations
2694 if (!iterator_->IsValid()) {
2695 if (!forward && last_duplicate_key.IsValid()) {
2696 // We need to walk forward because we hit the end of
2697 // the data.
2698 forward = true;
2699 continue;
2702 return false;
2705 if (IsPastBounds()) {
2706 if (!forward && last_duplicate_key.IsValid()) {
2707 // We need to walk forward because now we're beyond the
2708 // bounds defined by the cursor.
2709 forward = true;
2710 continue;
2713 return false;
2716 if (!HaveEnteredRange())
2717 continue;
2719 // The row may not load because there's a stale entry in the
2720 // index. This is not fatal.
2721 if (!LoadCurrentRow())
2722 continue;
2724 if (key) {
2725 if (forward) {
2726 if (primary_key && current_key_->Equals(*key) &&
2727 this->primary_key().IsLessThan(*primary_key))
2728 continue;
2729 if (current_key_->IsLessThan(*key))
2730 continue;
2731 } else {
2732 if (primary_key && key->Equals(*current_key_) &&
2733 primary_key->IsLessThan(this->primary_key()))
2734 continue;
2735 if (key->IsLessThan(*current_key_))
2736 continue;
2740 if (cursor_options_.unique) {
2741 if (previous_key.IsValid() && current_key_->Equals(previous_key)) {
2742 // We should never be able to walk forward all the way
2743 // to the previous key.
2744 DCHECK(!last_duplicate_key.IsValid());
2745 continue;
2748 if (!forward) {
2749 if (!last_duplicate_key.IsValid()) {
2750 last_duplicate_key = *current_key_;
2751 continue;
2754 // We need to walk forward because we hit the boundary
2755 // between key ranges.
2756 if (!last_duplicate_key.Equals(*current_key_)) {
2757 forward = true;
2758 continue;
2761 continue;
2764 break;
2767 DCHECK(!last_duplicate_key.IsValid() ||
2768 (forward && last_duplicate_key.Equals(*current_key_)));
2769 return true;
2772 bool IndexedDBBackingStore::Cursor::HaveEnteredRange() const {
2773 if (cursor_options_.forward) {
2774 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
2775 if (cursor_options_.low_open) {
2776 return compare > 0;
2778 return compare >= 0;
2780 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
2781 if (cursor_options_.high_open) {
2782 return compare < 0;
2784 return compare <= 0;
2787 bool IndexedDBBackingStore::Cursor::IsPastBounds() const {
2788 if (cursor_options_.forward) {
2789 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.high_key);
2790 if (cursor_options_.high_open) {
2791 return compare >= 0;
2793 return compare > 0;
2795 int compare = CompareIndexKeys(iterator_->Key(), cursor_options_.low_key);
2796 if (cursor_options_.low_open) {
2797 return compare <= 0;
2799 return compare < 0;
2802 const IndexedDBKey& IndexedDBBackingStore::Cursor::primary_key() const {
2803 return *current_key_;
2806 const IndexedDBBackingStore::RecordIdentifier&
2807 IndexedDBBackingStore::Cursor::record_identifier() const {
2808 return record_identifier_;
2811 class ObjectStoreKeyCursorImpl : public IndexedDBBackingStore::Cursor {
2812 public:
2813 ObjectStoreKeyCursorImpl(
2814 LevelDBTransaction* transaction,
2815 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
2816 : IndexedDBBackingStore::Cursor(transaction, cursor_options) {}
2818 virtual Cursor* Clone() OVERRIDE {
2819 return new ObjectStoreKeyCursorImpl(this);
2822 // IndexedDBBackingStore::Cursor
2823 virtual IndexedDBValue* value() OVERRIDE {
2824 NOTREACHED();
2825 return NULL;
2827 virtual bool LoadCurrentRow() OVERRIDE;
2829 protected:
2830 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
2831 return ObjectStoreDataKey::Encode(
2832 cursor_options_.database_id, cursor_options_.object_store_id, key);
2834 virtual std::string EncodeKey(const IndexedDBKey& key,
2835 const IndexedDBKey& primary_key) OVERRIDE {
2836 NOTREACHED();
2837 return std::string();
2840 private:
2841 explicit ObjectStoreKeyCursorImpl(const ObjectStoreKeyCursorImpl* other)
2842 : IndexedDBBackingStore::Cursor(other) {}
2845 bool ObjectStoreKeyCursorImpl::LoadCurrentRow() {
2846 StringPiece slice(iterator_->Key());
2847 ObjectStoreDataKey object_store_data_key;
2848 if (!ObjectStoreDataKey::Decode(&slice, &object_store_data_key)) {
2849 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
2850 return false;
2853 current_key_ = object_store_data_key.user_key();
2855 int64 version;
2856 slice = StringPiece(iterator_->Value());
2857 if (!DecodeVarInt(&slice, &version)) {
2858 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
2859 return false;
2862 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
2863 std::string encoded_key;
2864 EncodeIDBKey(*current_key_, &encoded_key);
2865 record_identifier_.Reset(encoded_key, version);
2867 return true;
2870 class ObjectStoreCursorImpl : public IndexedDBBackingStore::Cursor {
2871 public:
2872 ObjectStoreCursorImpl(
2873 LevelDBTransaction* transaction,
2874 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
2875 : IndexedDBBackingStore::Cursor(transaction, cursor_options) {}
2877 virtual Cursor* Clone() OVERRIDE { return new ObjectStoreCursorImpl(this); }
2879 // IndexedDBBackingStore::Cursor
2880 virtual IndexedDBValue* value() OVERRIDE { return &current_value_; }
2881 virtual bool LoadCurrentRow() OVERRIDE;
2883 protected:
2884 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
2885 return ObjectStoreDataKey::Encode(
2886 cursor_options_.database_id, cursor_options_.object_store_id, key);
2888 virtual std::string EncodeKey(const IndexedDBKey& key,
2889 const IndexedDBKey& primary_key) OVERRIDE {
2890 NOTREACHED();
2891 return std::string();
2894 private:
2895 explicit ObjectStoreCursorImpl(const ObjectStoreCursorImpl* other)
2896 : IndexedDBBackingStore::Cursor(other),
2897 current_value_(other->current_value_) {}
2899 IndexedDBValue current_value_;
2902 bool ObjectStoreCursorImpl::LoadCurrentRow() {
2903 StringPiece key_slice(iterator_->Key());
2904 ObjectStoreDataKey object_store_data_key;
2905 if (!ObjectStoreDataKey::Decode(&key_slice, &object_store_data_key)) {
2906 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
2907 return false;
2910 current_key_ = object_store_data_key.user_key();
2912 int64 version;
2913 StringPiece value_slice = StringPiece(iterator_->Value());
2914 if (!DecodeVarInt(&value_slice, &version)) {
2915 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
2916 return false;
2919 // TODO(jsbell): This re-encodes what was just decoded; try and optimize.
2920 std::string encoded_key;
2921 EncodeIDBKey(*current_key_, &encoded_key);
2922 record_identifier_.Reset(encoded_key, version);
2924 current_value_.bits = value_slice.as_string();
2925 return true;
2928 class IndexKeyCursorImpl : public IndexedDBBackingStore::Cursor {
2929 public:
2930 IndexKeyCursorImpl(
2931 LevelDBTransaction* transaction,
2932 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
2933 : IndexedDBBackingStore::Cursor(transaction, cursor_options) {}
2935 virtual Cursor* Clone() OVERRIDE { return new IndexKeyCursorImpl(this); }
2937 // IndexedDBBackingStore::Cursor
2938 virtual IndexedDBValue* value() OVERRIDE {
2939 NOTREACHED();
2940 return NULL;
2942 virtual const IndexedDBKey& primary_key() const OVERRIDE {
2943 return *primary_key_;
2945 virtual const IndexedDBBackingStore::RecordIdentifier& record_identifier()
2946 const OVERRIDE {
2947 NOTREACHED();
2948 return record_identifier_;
2950 virtual bool LoadCurrentRow() OVERRIDE;
2952 protected:
2953 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
2954 return IndexDataKey::Encode(cursor_options_.database_id,
2955 cursor_options_.object_store_id,
2956 cursor_options_.index_id,
2957 key);
2959 virtual std::string EncodeKey(const IndexedDBKey& key,
2960 const IndexedDBKey& primary_key) OVERRIDE {
2961 return IndexDataKey::Encode(cursor_options_.database_id,
2962 cursor_options_.object_store_id,
2963 cursor_options_.index_id,
2964 key,
2965 primary_key);
2968 private:
2969 explicit IndexKeyCursorImpl(const IndexKeyCursorImpl* other)
2970 : IndexedDBBackingStore::Cursor(other),
2971 primary_key_(new IndexedDBKey(*other->primary_key_)) {}
2973 scoped_ptr<IndexedDBKey> primary_key_;
2976 bool IndexKeyCursorImpl::LoadCurrentRow() {
2977 StringPiece slice(iterator_->Key());
2978 IndexDataKey index_data_key;
2979 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
2980 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
2981 return false;
2984 current_key_ = index_data_key.user_key();
2985 DCHECK(current_key_);
2987 slice = StringPiece(iterator_->Value());
2988 int64 index_data_version;
2989 if (!DecodeVarInt(&slice, &index_data_version)) {
2990 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
2991 return false;
2994 if (!DecodeIDBKey(&slice, &primary_key_) || !slice.empty()) {
2995 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
2996 return false;
2999 std::string primary_leveldb_key =
3000 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3001 index_data_key.ObjectStoreId(),
3002 *primary_key_);
3004 std::string result;
3005 bool found = false;
3006 leveldb::Status s = transaction_->Get(primary_leveldb_key, &result, &found);
3007 if (!s.ok()) {
3008 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3009 return false;
3011 if (!found) {
3012 transaction_->Remove(iterator_->Key());
3013 return false;
3015 if (!result.size()) {
3016 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3017 return false;
3020 int64 object_store_data_version;
3021 slice = StringPiece(result);
3022 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3023 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3024 return false;
3027 if (object_store_data_version != index_data_version) {
3028 transaction_->Remove(iterator_->Key());
3029 return false;
3032 return true;
3035 class IndexCursorImpl : public IndexedDBBackingStore::Cursor {
3036 public:
3037 IndexCursorImpl(
3038 LevelDBTransaction* transaction,
3039 const IndexedDBBackingStore::Cursor::CursorOptions& cursor_options)
3040 : IndexedDBBackingStore::Cursor(transaction, cursor_options) {}
3042 virtual Cursor* Clone() OVERRIDE { return new IndexCursorImpl(this); }
3044 // IndexedDBBackingStore::Cursor
3045 virtual IndexedDBValue* value() OVERRIDE { return &current_value_; }
3046 virtual const IndexedDBKey& primary_key() const OVERRIDE {
3047 return *primary_key_;
3049 virtual const IndexedDBBackingStore::RecordIdentifier& record_identifier()
3050 const OVERRIDE {
3051 NOTREACHED();
3052 return record_identifier_;
3054 virtual bool LoadCurrentRow() OVERRIDE;
3056 protected:
3057 virtual std::string EncodeKey(const IndexedDBKey& key) OVERRIDE {
3058 return IndexDataKey::Encode(cursor_options_.database_id,
3059 cursor_options_.object_store_id,
3060 cursor_options_.index_id,
3061 key);
3063 virtual std::string EncodeKey(const IndexedDBKey& key,
3064 const IndexedDBKey& primary_key) OVERRIDE {
3065 return IndexDataKey::Encode(cursor_options_.database_id,
3066 cursor_options_.object_store_id,
3067 cursor_options_.index_id,
3068 key,
3069 primary_key);
3072 private:
3073 explicit IndexCursorImpl(const IndexCursorImpl* other)
3074 : IndexedDBBackingStore::Cursor(other),
3075 primary_key_(new IndexedDBKey(*other->primary_key_)),
3076 current_value_(other->current_value_),
3077 primary_leveldb_key_(other->primary_leveldb_key_) {}
3079 scoped_ptr<IndexedDBKey> primary_key_;
3080 IndexedDBValue current_value_;
3081 std::string primary_leveldb_key_;
3084 bool IndexCursorImpl::LoadCurrentRow() {
3085 StringPiece slice(iterator_->Key());
3086 IndexDataKey index_data_key;
3087 if (!IndexDataKey::Decode(&slice, &index_data_key)) {
3088 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3089 return false;
3092 current_key_ = index_data_key.user_key();
3093 DCHECK(current_key_);
3095 slice = StringPiece(iterator_->Value());
3096 int64 index_data_version;
3097 if (!DecodeVarInt(&slice, &index_data_version)) {
3098 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3099 return false;
3101 if (!DecodeIDBKey(&slice, &primary_key_)) {
3102 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3103 return false;
3106 primary_leveldb_key_ =
3107 ObjectStoreDataKey::Encode(index_data_key.DatabaseId(),
3108 index_data_key.ObjectStoreId(),
3109 *primary_key_);
3111 std::string result;
3112 bool found = false;
3113 leveldb::Status s = transaction_->Get(primary_leveldb_key_, &result, &found);
3114 if (!s.ok()) {
3115 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3116 return false;
3118 if (!found) {
3119 transaction_->Remove(iterator_->Key());
3120 return false;
3122 if (!result.size()) {
3123 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3124 return false;
3127 int64 object_store_data_version;
3128 slice = StringPiece(result);
3129 if (!DecodeVarInt(&slice, &object_store_data_version)) {
3130 INTERNAL_READ_ERROR_UNTESTED(LOAD_CURRENT_ROW);
3131 return false;
3134 if (object_store_data_version != index_data_version) {
3135 transaction_->Remove(iterator_->Key());
3136 return false;
3139 current_value_.bits = slice.as_string();
3140 return true;
3143 bool ObjectStoreCursorOptions(
3144 LevelDBTransaction* transaction,
3145 int64 database_id,
3146 int64 object_store_id,
3147 const IndexedDBKeyRange& range,
3148 indexed_db::CursorDirection direction,
3149 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3150 cursor_options->database_id = database_id;
3151 cursor_options->object_store_id = object_store_id;
3153 bool lower_bound = range.lower().IsValid();
3154 bool upper_bound = range.upper().IsValid();
3155 cursor_options->forward =
3156 (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
3157 direction == indexed_db::CURSOR_NEXT);
3158 cursor_options->unique = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
3159 direction == indexed_db::CURSOR_PREV_NO_DUPLICATE);
3161 if (!lower_bound) {
3162 cursor_options->low_key =
3163 ObjectStoreDataKey::Encode(database_id, object_store_id, MinIDBKey());
3164 cursor_options->low_open = true; // Not included.
3165 } else {
3166 cursor_options->low_key =
3167 ObjectStoreDataKey::Encode(database_id, object_store_id, range.lower());
3168 cursor_options->low_open = range.lowerOpen();
3171 leveldb::Status s;
3173 if (!upper_bound) {
3174 cursor_options->high_key =
3175 ObjectStoreDataKey::Encode(database_id, object_store_id, MaxIDBKey());
3177 if (cursor_options->forward) {
3178 cursor_options->high_open = true; // Not included.
3179 } else {
3180 // We need a key that exists.
3181 // TODO(cmumford): Handle this error (crbug.com/363397)
3182 if (!FindGreatestKeyLessThanOrEqual(transaction,
3183 cursor_options->high_key,
3184 &cursor_options->high_key,
3186 return false;
3187 cursor_options->high_open = false;
3189 } else {
3190 cursor_options->high_key =
3191 ObjectStoreDataKey::Encode(database_id, object_store_id, range.upper());
3192 cursor_options->high_open = range.upperOpen();
3194 if (!cursor_options->forward) {
3195 // For reverse cursors, we need a key that exists.
3196 std::string found_high_key;
3197 // TODO(cmumford): Handle this error (crbug.com/363397)
3198 if (!FindGreatestKeyLessThanOrEqual(
3199 transaction, cursor_options->high_key, &found_high_key, s))
3200 return false;
3202 // If the target key should not be included, but we end up with a smaller
3203 // key, we should include that.
3204 if (cursor_options->high_open &&
3205 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3206 cursor_options->high_open = false;
3208 cursor_options->high_key = found_high_key;
3212 return true;
3215 bool IndexCursorOptions(
3216 LevelDBTransaction* transaction,
3217 int64 database_id,
3218 int64 object_store_id,
3219 int64 index_id,
3220 const IndexedDBKeyRange& range,
3221 indexed_db::CursorDirection direction,
3222 IndexedDBBackingStore::Cursor::CursorOptions* cursor_options) {
3223 DCHECK(transaction);
3224 if (!KeyPrefix::ValidIds(database_id, object_store_id, index_id))
3225 return false;
3227 cursor_options->database_id = database_id;
3228 cursor_options->object_store_id = object_store_id;
3229 cursor_options->index_id = index_id;
3231 bool lower_bound = range.lower().IsValid();
3232 bool upper_bound = range.upper().IsValid();
3233 cursor_options->forward =
3234 (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
3235 direction == indexed_db::CURSOR_NEXT);
3236 cursor_options->unique = (direction == indexed_db::CURSOR_NEXT_NO_DUPLICATE ||
3237 direction == indexed_db::CURSOR_PREV_NO_DUPLICATE);
3239 if (!lower_bound) {
3240 cursor_options->low_key =
3241 IndexDataKey::EncodeMinKey(database_id, object_store_id, index_id);
3242 cursor_options->low_open = false; // Included.
3243 } else {
3244 cursor_options->low_key = IndexDataKey::Encode(
3245 database_id, object_store_id, index_id, range.lower());
3246 cursor_options->low_open = range.lowerOpen();
3249 leveldb::Status s;
3251 if (!upper_bound) {
3252 cursor_options->high_key =
3253 IndexDataKey::EncodeMaxKey(database_id, object_store_id, index_id);
3254 cursor_options->high_open = false; // Included.
3256 if (!cursor_options->forward) { // We need a key that exists.
3257 if (!FindGreatestKeyLessThanOrEqual(transaction,
3258 cursor_options->high_key,
3259 &cursor_options->high_key,
3261 return false;
3262 cursor_options->high_open = false;
3264 } else {
3265 cursor_options->high_key = IndexDataKey::Encode(
3266 database_id, object_store_id, index_id, range.upper());
3267 cursor_options->high_open = range.upperOpen();
3269 std::string found_high_key;
3270 // Seek to the *last* key in the set of non-unique keys
3271 // TODO(cmumford): Handle this error (crbug.com/363397)
3272 if (!FindGreatestKeyLessThanOrEqual(
3273 transaction, cursor_options->high_key, &found_high_key, s))
3274 return false;
3276 // If the target key should not be included, but we end up with a smaller
3277 // key, we should include that.
3278 if (cursor_options->high_open &&
3279 CompareIndexKeys(found_high_key, cursor_options->high_key) < 0)
3280 cursor_options->high_open = false;
3282 cursor_options->high_key = found_high_key;
3285 return true;
3288 scoped_ptr<IndexedDBBackingStore::Cursor>
3289 IndexedDBBackingStore::OpenObjectStoreCursor(
3290 IndexedDBBackingStore::Transaction* transaction,
3291 int64 database_id,
3292 int64 object_store_id,
3293 const IndexedDBKeyRange& range,
3294 indexed_db::CursorDirection direction,
3295 leveldb::Status* s) {
3296 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreCursor");
3297 *s = leveldb::Status::OK();
3298 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3299 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3300 if (!ObjectStoreCursorOptions(leveldb_transaction,
3301 database_id,
3302 object_store_id,
3303 range,
3304 direction,
3305 &cursor_options))
3306 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3307 scoped_ptr<ObjectStoreCursorImpl> cursor(
3308 new ObjectStoreCursorImpl(leveldb_transaction, cursor_options));
3309 if (!cursor->FirstSeek(s))
3310 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3312 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3315 scoped_ptr<IndexedDBBackingStore::Cursor>
3316 IndexedDBBackingStore::OpenObjectStoreKeyCursor(
3317 IndexedDBBackingStore::Transaction* transaction,
3318 int64 database_id,
3319 int64 object_store_id,
3320 const IndexedDBKeyRange& range,
3321 indexed_db::CursorDirection direction,
3322 leveldb::Status* s) {
3323 IDB_TRACE("IndexedDBBackingStore::OpenObjectStoreKeyCursor");
3324 *s = leveldb::Status::OK();
3325 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3326 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3327 if (!ObjectStoreCursorOptions(leveldb_transaction,
3328 database_id,
3329 object_store_id,
3330 range,
3331 direction,
3332 &cursor_options))
3333 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3334 scoped_ptr<ObjectStoreKeyCursorImpl> cursor(
3335 new ObjectStoreKeyCursorImpl(leveldb_transaction, cursor_options));
3336 if (!cursor->FirstSeek(s))
3337 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3339 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3342 scoped_ptr<IndexedDBBackingStore::Cursor>
3343 IndexedDBBackingStore::OpenIndexKeyCursor(
3344 IndexedDBBackingStore::Transaction* transaction,
3345 int64 database_id,
3346 int64 object_store_id,
3347 int64 index_id,
3348 const IndexedDBKeyRange& range,
3349 indexed_db::CursorDirection direction,
3350 leveldb::Status* s) {
3351 IDB_TRACE("IndexedDBBackingStore::OpenIndexKeyCursor");
3352 *s = leveldb::Status::OK();
3353 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3354 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3355 if (!IndexCursorOptions(leveldb_transaction,
3356 database_id,
3357 object_store_id,
3358 index_id,
3359 range,
3360 direction,
3361 &cursor_options))
3362 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3363 scoped_ptr<IndexKeyCursorImpl> cursor(
3364 new IndexKeyCursorImpl(leveldb_transaction, cursor_options));
3365 if (!cursor->FirstSeek(s))
3366 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3368 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3371 scoped_ptr<IndexedDBBackingStore::Cursor>
3372 IndexedDBBackingStore::OpenIndexCursor(
3373 IndexedDBBackingStore::Transaction* transaction,
3374 int64 database_id,
3375 int64 object_store_id,
3376 int64 index_id,
3377 const IndexedDBKeyRange& range,
3378 indexed_db::CursorDirection direction,
3379 leveldb::Status* s) {
3380 IDB_TRACE("IndexedDBBackingStore::OpenIndexCursor");
3381 LevelDBTransaction* leveldb_transaction = transaction->transaction();
3382 IndexedDBBackingStore::Cursor::CursorOptions cursor_options;
3383 if (!IndexCursorOptions(leveldb_transaction,
3384 database_id,
3385 object_store_id,
3386 index_id,
3387 range,
3388 direction,
3389 &cursor_options))
3390 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3391 scoped_ptr<IndexCursorImpl> cursor(
3392 new IndexCursorImpl(leveldb_transaction, cursor_options));
3393 if (!cursor->FirstSeek(s))
3394 return scoped_ptr<IndexedDBBackingStore::Cursor>();
3396 return cursor.PassAs<IndexedDBBackingStore::Cursor>();
3399 IndexedDBBackingStore::Transaction::Transaction(
3400 IndexedDBBackingStore* backing_store)
3401 : backing_store_(backing_store), database_id_(-1) {}
3403 IndexedDBBackingStore::Transaction::~Transaction() {
3404 STLDeleteContainerPairSecondPointers(
3405 blob_change_map_.begin(), blob_change_map_.end());
3408 void IndexedDBBackingStore::Transaction::Begin() {
3409 IDB_TRACE("IndexedDBBackingStore::Transaction::Begin");
3410 DCHECK(!transaction_.get());
3411 transaction_ = new LevelDBTransaction(backing_store_->db_.get());
3414 leveldb::Status IndexedDBBackingStore::Transaction::Commit() {
3415 IDB_TRACE("IndexedDBBackingStore::Transaction::Commit");
3416 DCHECK(transaction_.get());
3417 leveldb::Status s = transaction_->Commit();
3418 transaction_ = NULL;
3419 if (!s.ok())
3420 INTERNAL_WRITE_ERROR_UNTESTED(TRANSACTION_COMMIT_METHOD);
3421 return s;
3425 class IndexedDBBackingStore::Transaction::BlobWriteCallbackWrapper
3426 : public IndexedDBBackingStore::BlobWriteCallback {
3427 public:
3428 BlobWriteCallbackWrapper(IndexedDBBackingStore::Transaction* transaction,
3429 scoped_refptr<BlobWriteCallback> callback)
3430 : transaction_(transaction), callback_(callback) {}
3431 virtual void Run(bool succeeded) OVERRIDE {
3432 callback_->Run(succeeded);
3433 transaction_->chained_blob_writer_ = NULL;
3436 private:
3437 virtual ~BlobWriteCallbackWrapper() {}
3438 friend class base::RefCounted<IndexedDBBackingStore::BlobWriteCallback>;
3440 IndexedDBBackingStore::Transaction* transaction_;
3441 scoped_refptr<BlobWriteCallback> callback_;
3444 void IndexedDBBackingStore::Transaction::WriteNewBlobs(
3445 BlobEntryKeyValuePairVec& new_blob_entries,
3446 WriteDescriptorVec& new_files_to_write,
3447 scoped_refptr<BlobWriteCallback> callback) {
3448 DCHECK_GT(new_files_to_write.size(), 0UL);
3449 DCHECK_GT(database_id_, 0);
3450 BlobEntryKeyValuePairVec::iterator blob_entry_iter;
3451 for (blob_entry_iter = new_blob_entries.begin();
3452 blob_entry_iter != new_blob_entries.end();
3453 ++blob_entry_iter) {
3454 // Add the new blob-table entry for each blob to the main transaction, or
3455 // remove any entry that may exist if there's no new one.
3456 if (!blob_entry_iter->second.size())
3457 transaction_->Remove(blob_entry_iter->first.Encode());
3458 else
3459 transaction_->Put(blob_entry_iter->first.Encode(),
3460 &blob_entry_iter->second);
3462 // Creating the writer will start it going asynchronously.
3463 chained_blob_writer_ =
3464 new ChainedBlobWriterImpl(database_id_,
3465 backing_store_,
3466 new_files_to_write,
3467 new BlobWriteCallbackWrapper(this, callback));
3470 void IndexedDBBackingStore::Transaction::Rollback() {
3471 IDB_TRACE("IndexedDBBackingStore::Transaction::Rollback");
3472 DCHECK(transaction_.get());
3473 if (chained_blob_writer_) {
3474 chained_blob_writer_->Abort();
3475 chained_blob_writer_ = NULL;
3477 transaction_->Rollback();
3478 transaction_ = NULL;
3481 IndexedDBBackingStore::Transaction::BlobChangeRecord::BlobChangeRecord(
3482 const std::string& key, int64 object_store_id)
3483 : key_(key), object_store_id_(object_store_id) {
3486 IndexedDBBackingStore::Transaction::BlobChangeRecord::~BlobChangeRecord() {
3489 void IndexedDBBackingStore::Transaction::BlobChangeRecord::SetBlobInfo(
3490 std::vector<IndexedDBBlobInfo>* blob_info) {
3491 blob_info_.clear();
3492 if (blob_info)
3493 blob_info_.swap(*blob_info);
3496 void IndexedDBBackingStore::Transaction::BlobChangeRecord::SetHandles(
3497 ScopedVector<webkit_blob::BlobDataHandle>* handles) {
3498 handles_.clear();
3499 if (handles)
3500 handles_.swap(*handles);
3503 // This is storing an info, even if empty, even if the previous key had no blob
3504 // info that we know of. It duplicates a bunch of information stored in the
3505 // leveldb transaction, but only w.r.t. the user keys altered--we don't keep the
3506 // changes to exists or index keys here.
3507 void IndexedDBBackingStore::Transaction::PutBlobInfo(
3508 int64 database_id,
3509 int64 object_store_id,
3510 const std::string& key,
3511 std::vector<IndexedDBBlobInfo>* blob_info,
3512 ScopedVector<webkit_blob::BlobDataHandle>* handles) {
3513 DCHECK_GT(key.size(), 0UL);
3514 if (database_id_ < 0)
3515 database_id_ = database_id;
3516 DCHECK_EQ(database_id_, database_id);
3518 BlobChangeMap::iterator it = blob_change_map_.find(key);
3519 BlobChangeRecord* record = NULL;
3520 if (it == blob_change_map_.end()) {
3521 record = new BlobChangeRecord(key, object_store_id);
3522 blob_change_map_[key] = record;
3523 } else {
3524 record = it->second;
3526 DCHECK_EQ(record->object_store_id(), object_store_id);
3527 record->SetBlobInfo(blob_info);
3528 record->SetHandles(handles);
3529 DCHECK(!handles || !handles->size());
3532 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
3533 const GURL& url,
3534 int64_t key)
3535 : is_file_(false), url_(url), key_(key) {}
3537 IndexedDBBackingStore::Transaction::WriteDescriptor::WriteDescriptor(
3538 const FilePath& file_path,
3539 int64_t key)
3540 : is_file_(true), file_path_(file_path), key_(key) {}
3542 } // namespace content