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_database.h"
10 #include "base/auto_reset.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/stl_util.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "content/browser/indexed_db/indexed_db_connection.h"
17 #include "content/browser/indexed_db/indexed_db_cursor.h"
18 #include "content/browser/indexed_db/indexed_db_factory.h"
19 #include "content/browser/indexed_db/indexed_db_index_writer.h"
20 #include "content/browser/indexed_db/indexed_db_tracing.h"
21 #include "content/browser/indexed_db/indexed_db_transaction.h"
22 #include "content/common/indexed_db/indexed_db_key_path.h"
23 #include "content/common/indexed_db/indexed_db_key_range.h"
24 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
26 using base::ASCIIToUTF16;
27 using base::Int64ToString16;
28 using blink::WebIDBKeyTypeNumber;
32 // PendingOpenCall has a scoped_refptr<IndexedDBDatabaseCallbacks> because it
33 // isn't a connection yet.
34 class IndexedDBDatabase::PendingOpenCall {
36 PendingOpenCall(scoped_refptr<IndexedDBCallbacks> callbacks,
37 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
40 : callbacks_(callbacks),
41 database_callbacks_(database_callbacks),
43 transaction_id_(transaction_id) {}
44 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
45 scoped_refptr<IndexedDBDatabaseCallbacks> const database_callbacks() {
46 return database_callbacks_;
48 int64 version() const { return version_; }
49 int64 transaction_id() const { return transaction_id_; }
52 scoped_refptr<IndexedDBCallbacks> callbacks_;
53 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks_;
55 const int64 transaction_id_;
58 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
59 // in-progress connection.
60 class IndexedDBDatabase::PendingUpgradeCall {
62 PendingUpgradeCall(scoped_refptr<IndexedDBCallbacks> callbacks,
63 scoped_ptr<IndexedDBConnection> connection,
66 : callbacks_(callbacks),
67 connection_(connection.Pass()),
69 transaction_id_(transaction_id) {}
70 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
71 // Takes ownership of the connection object.
72 scoped_ptr<IndexedDBConnection> ReleaseConnection() WARN_UNUSED_RESULT {
73 return connection_.Pass();
75 int64 version() const { return version_; }
76 int64 transaction_id() const { return transaction_id_; }
79 scoped_refptr<IndexedDBCallbacks> callbacks_;
80 scoped_ptr<IndexedDBConnection> connection_;
82 const int64 transaction_id_;
85 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
86 // owned elsewhere, but we need to cancel the success call if that connection
87 // closes before it is sent.
88 class IndexedDBDatabase::PendingSuccessCall {
90 PendingSuccessCall(scoped_refptr<IndexedDBCallbacks> callbacks,
91 IndexedDBConnection* connection,
93 : callbacks_(callbacks), connection_(connection), version_(version) {}
94 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
95 IndexedDBConnection* connection() const { return connection_; }
96 int64 version() const { return version_; }
99 scoped_refptr<IndexedDBCallbacks> callbacks_;
100 IndexedDBConnection* connection_;
104 class IndexedDBDatabase::PendingDeleteCall {
106 explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks)
107 : callbacks_(callbacks) {}
108 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
111 scoped_refptr<IndexedDBCallbacks> callbacks_;
114 scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
115 const base::string16& name,
116 IndexedDBBackingStore* backing_store,
117 IndexedDBFactory* factory,
118 const Identifier& unique_identifier) {
119 scoped_refptr<IndexedDBDatabase> database =
120 new IndexedDBDatabase(name, backing_store, factory, unique_identifier);
121 if (!database->OpenInternal())
127 const base::string16::value_type kNoStringVersion[] = {0};
130 IndexedDBDatabase::IndexedDBDatabase(const base::string16& name,
131 IndexedDBBackingStore* backing_store,
132 IndexedDBFactory* factory,
133 const Identifier& unique_identifier)
134 : backing_store_(backing_store),
138 IndexedDBDatabaseMetadata::NO_INT_VERSION,
140 identifier_(unique_identifier),
142 DCHECK(!metadata_.name.empty());
145 void IndexedDBDatabase::AddObjectStore(
146 const IndexedDBObjectStoreMetadata& object_store,
147 int64 new_max_object_store_id) {
148 DCHECK(metadata_.object_stores.find(object_store.id) ==
149 metadata_.object_stores.end());
150 if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
151 DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
152 metadata_.max_object_store_id = new_max_object_store_id;
154 metadata_.object_stores[object_store.id] = object_store;
157 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) {
158 DCHECK(metadata_.object_stores.find(object_store_id) !=
159 metadata_.object_stores.end());
160 metadata_.object_stores.erase(object_store_id);
163 void IndexedDBDatabase::AddIndex(int64 object_store_id,
164 const IndexedDBIndexMetadata& index,
165 int64 new_max_index_id) {
166 DCHECK(metadata_.object_stores.find(object_store_id) !=
167 metadata_.object_stores.end());
168 IndexedDBObjectStoreMetadata object_store =
169 metadata_.object_stores[object_store_id];
171 DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
172 object_store.indexes[index.id] = index;
173 if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
174 DCHECK_LT(object_store.max_index_id, new_max_index_id);
175 object_store.max_index_id = new_max_index_id;
177 metadata_.object_stores[object_store_id] = object_store;
180 void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) {
181 DCHECK(metadata_.object_stores.find(object_store_id) !=
182 metadata_.object_stores.end());
183 IndexedDBObjectStoreMetadata object_store =
184 metadata_.object_stores[object_store_id];
186 DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
187 object_store.indexes.erase(index_id);
188 metadata_.object_stores[object_store_id] = object_store;
191 bool IndexedDBDatabase::OpenInternal() {
192 bool success = false;
193 bool ok = backing_store_->GetIDBDatabaseMetaData(
194 metadata_.name, &metadata_, &success);
195 DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
196 << " id = " << metadata_.id;
200 return backing_store_->GetObjectStores(metadata_.id,
201 &metadata_.object_stores);
203 return backing_store_->CreateIDBDatabaseMetaData(
204 metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id);
207 IndexedDBDatabase::~IndexedDBDatabase() {
208 DCHECK(transactions_.empty());
209 DCHECK(pending_open_calls_.empty());
210 DCHECK(pending_delete_calls_.empty());
213 IndexedDBTransaction* IndexedDBDatabase::GetTransaction(
214 int64 transaction_id) const {
215 TransactionMap::const_iterator trans_iterator =
216 transactions_.find(transaction_id);
217 if (trans_iterator == transactions_.end())
219 return trans_iterator->second;
222 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id) const {
223 if (!ContainsKey(metadata_.object_stores, object_store_id)) {
224 DLOG(ERROR) << "Invalid object_store_id";
230 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id,
231 int64 index_id) const {
232 if (!ValidateObjectStoreId(object_store_id))
234 const IndexedDBObjectStoreMetadata& object_store_metadata =
235 metadata_.object_stores.find(object_store_id)->second;
236 if (!ContainsKey(object_store_metadata.indexes, index_id)) {
237 DLOG(ERROR) << "Invalid index_id";
243 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
244 int64 object_store_id,
245 int64 index_id) const {
246 if (!ValidateObjectStoreId(object_store_id))
248 const IndexedDBObjectStoreMetadata& object_store_metadata =
249 metadata_.object_stores.find(object_store_id)->second;
250 if (index_id != IndexedDBIndexMetadata::kInvalidId &&
251 !ContainsKey(object_store_metadata.indexes, index_id)) {
252 DLOG(ERROR) << "Invalid index_id";
258 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
259 int64 object_store_id,
260 int64 index_id) const {
261 if (!ValidateObjectStoreId(object_store_id))
263 const IndexedDBObjectStoreMetadata& object_store_metadata =
264 metadata_.object_stores.find(object_store_id)->second;
265 if (ContainsKey(object_store_metadata.indexes, index_id)) {
266 DLOG(ERROR) << "Invalid index_id";
272 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
273 int64 object_store_id,
274 const base::string16& name,
275 const IndexedDBKeyPath& key_path,
276 bool auto_increment) {
277 IDB_TRACE("IndexedDBDatabase::CreateObjectStore");
278 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
281 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
283 if (ContainsKey(metadata_.object_stores, object_store_id)) {
284 DLOG(ERROR) << "Invalid object_store_id";
288 IndexedDBObjectStoreMetadata object_store_metadata(
293 IndexedDBDatabase::kMinimumIndexId);
295 transaction->ScheduleTask(
296 base::Bind(&IndexedDBDatabase::CreateObjectStoreOperation,
298 object_store_metadata),
299 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
303 AddObjectStore(object_store_metadata, object_store_id);
306 void IndexedDBDatabase::CreateObjectStoreOperation(
307 const IndexedDBObjectStoreMetadata& object_store_metadata,
308 IndexedDBTransaction* transaction) {
309 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreOperation");
310 if (!backing_store_->CreateObjectStore(
311 transaction->BackingStoreTransaction(),
312 transaction->database()->id(),
313 object_store_metadata.id,
314 object_store_metadata.name,
315 object_store_metadata.key_path,
316 object_store_metadata.auto_increment)) {
317 transaction->Abort(IndexedDBDatabaseError(
318 blink::WebIDBDatabaseExceptionUnknownError,
319 ASCIIToUTF16("Internal error creating object store '") +
320 object_store_metadata.name + ASCIIToUTF16("'.")));
325 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
326 int64 object_store_id) {
327 IDB_TRACE("IndexedDBDatabase::DeleteObjectStore");
328 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
331 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
333 if (!ValidateObjectStoreId(object_store_id))
336 const IndexedDBObjectStoreMetadata& object_store_metadata =
337 metadata_.object_stores[object_store_id];
339 transaction->ScheduleTask(
340 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation,
342 object_store_metadata),
343 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
345 object_store_metadata));
346 RemoveObjectStore(object_store_id);
349 void IndexedDBDatabase::CreateIndex(int64 transaction_id,
350 int64 object_store_id,
352 const base::string16& name,
353 const IndexedDBKeyPath& key_path,
356 IDB_TRACE("IndexedDBDatabase::CreateIndex");
357 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
360 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
362 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id))
364 const IndexedDBIndexMetadata index_metadata(
365 name, index_id, key_path, unique, multi_entry);
367 transaction->ScheduleTask(
368 base::Bind(&IndexedDBDatabase::CreateIndexOperation,
372 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation,
377 AddIndex(object_store_id, index_metadata, index_id);
380 void IndexedDBDatabase::CreateIndexOperation(
381 int64 object_store_id,
382 const IndexedDBIndexMetadata& index_metadata,
383 IndexedDBTransaction* transaction) {
384 IDB_TRACE("IndexedDBDatabase::CreateIndexOperation");
385 if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
386 transaction->database()->id(),
390 index_metadata.key_path,
391 index_metadata.unique,
392 index_metadata.multi_entry)) {
393 base::string16 error_string =
394 ASCIIToUTF16("Internal error creating index '") +
395 index_metadata.name + ASCIIToUTF16("'.");
396 transaction->Abort(IndexedDBDatabaseError(
397 blink::WebIDBDatabaseExceptionUnknownError, error_string));
402 void IndexedDBDatabase::CreateIndexAbortOperation(
403 int64 object_store_id,
405 IndexedDBTransaction* transaction) {
406 IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation");
407 DCHECK(!transaction);
408 RemoveIndex(object_store_id, index_id);
411 void IndexedDBDatabase::DeleteIndex(int64 transaction_id,
412 int64 object_store_id,
414 IDB_TRACE("IndexedDBDatabase::DeleteIndex");
415 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
418 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
420 if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id))
422 const IndexedDBIndexMetadata& index_metadata =
423 metadata_.object_stores[object_store_id].indexes[index_id];
425 transaction->ScheduleTask(
426 base::Bind(&IndexedDBDatabase::DeleteIndexOperation,
430 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation,
435 RemoveIndex(object_store_id, index_id);
438 void IndexedDBDatabase::DeleteIndexOperation(
439 int64 object_store_id,
440 const IndexedDBIndexMetadata& index_metadata,
441 IndexedDBTransaction* transaction) {
442 IDB_TRACE("IndexedDBDatabase::DeleteIndexOperation");
443 bool ok = backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
444 transaction->database()->id(),
448 base::string16 error_string =
449 ASCIIToUTF16("Internal error deleting index '") +
450 index_metadata.name + ASCIIToUTF16("'.");
451 transaction->Abort(IndexedDBDatabaseError(
452 blink::WebIDBDatabaseExceptionUnknownError, error_string));
456 void IndexedDBDatabase::DeleteIndexAbortOperation(
457 int64 object_store_id,
458 const IndexedDBIndexMetadata& index_metadata,
459 IndexedDBTransaction* transaction) {
460 IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation");
461 DCHECK(!transaction);
462 AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId);
465 void IndexedDBDatabase::Commit(int64 transaction_id) {
466 // The frontend suggests that we commit, but we may have previously initiated
467 // an abort, and so have disposed of the transaction. on_abort has already
468 // been dispatched to the frontend, so it will find out about that
470 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
472 transaction->Commit();
475 void IndexedDBDatabase::Abort(int64 transaction_id) {
476 // If the transaction is unknown, then it has already been aborted by the
477 // backend before this call so it is safe to ignore it.
478 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
480 transaction->Abort();
483 void IndexedDBDatabase::Abort(int64 transaction_id,
484 const IndexedDBDatabaseError& error) {
485 // If the transaction is unknown, then it has already been aborted by the
486 // backend before this call so it is safe to ignore it.
487 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
489 transaction->Abort(error);
492 void IndexedDBDatabase::Get(int64 transaction_id,
493 int64 object_store_id,
495 scoped_ptr<IndexedDBKeyRange> key_range,
497 scoped_refptr<IndexedDBCallbacks> callbacks) {
498 IDB_TRACE("IndexedDBDatabase::Get");
499 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
503 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
506 transaction->ScheduleTask(base::Bind(
507 &IndexedDBDatabase::GetOperation,
512 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
516 void IndexedDBDatabase::GetOperation(
517 int64 object_store_id,
519 scoped_ptr<IndexedDBKeyRange> key_range,
520 indexed_db::CursorType cursor_type,
521 scoped_refptr<IndexedDBCallbacks> callbacks,
522 IndexedDBTransaction* transaction) {
523 IDB_TRACE("IndexedDBDatabase::GetOperation");
525 DCHECK(metadata_.object_stores.find(object_store_id) !=
526 metadata_.object_stores.end());
527 const IndexedDBObjectStoreMetadata& object_store_metadata =
528 metadata_.object_stores[object_store_id];
530 const IndexedDBKey* key;
532 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
533 if (key_range->IsOnlyKey()) {
534 key = &key_range->lower();
536 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
537 DCHECK_NE(cursor_type, indexed_db::CURSOR_KEY_ONLY);
538 // ObjectStore Retrieval Operation
539 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
540 transaction->BackingStoreTransaction(),
544 indexed_db::CURSOR_NEXT);
545 } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
546 // Index Value Retrieval Operation
547 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
548 transaction->BackingStoreTransaction(),
553 indexed_db::CURSOR_NEXT);
555 // Index Referenced Value Retrieval Operation
556 backing_store_cursor = backing_store_->OpenIndexCursor(
557 transaction->BackingStoreTransaction(),
562 indexed_db::CURSOR_NEXT);
565 if (!backing_store_cursor) {
566 callbacks->OnSuccess();
570 key = &backing_store_cursor->key();
573 scoped_ptr<IndexedDBKey> primary_key;
575 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
576 // Object Store Retrieval Operation
578 ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
585 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
586 "Internal error in GetRecord."));
591 callbacks->OnSuccess();
595 if (object_store_metadata.auto_increment &&
596 !object_store_metadata.key_path.IsNull()) {
597 callbacks->OnSuccess(&value, *key, object_store_metadata.key_path);
601 callbacks->OnSuccess(&value);
605 // From here we are dealing only with indexes.
606 ok = backing_store_->GetPrimaryKeyViaIndex(
607 transaction->BackingStoreTransaction(),
615 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
616 "Internal error in GetPrimaryKeyViaIndex."));
620 callbacks->OnSuccess();
623 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
624 // Index Value Retrieval Operation
625 callbacks->OnSuccess(*primary_key);
629 // Index Referenced Value Retrieval Operation
631 ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
638 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
639 "Internal error in GetRecord."));
644 callbacks->OnSuccess();
647 if (object_store_metadata.auto_increment &&
648 !object_store_metadata.key_path.IsNull()) {
649 callbacks->OnSuccess(&value, *primary_key, object_store_metadata.key_path);
652 callbacks->OnSuccess(&value);
655 static scoped_ptr<IndexedDBKey> GenerateKey(
656 IndexedDBBackingStore* backing_store,
657 IndexedDBTransaction* transaction,
659 int64 object_store_id) {
660 const int64 max_generator_value =
661 9007199254740992LL; // Maximum integer storable as ECMAScript number.
662 int64 current_number;
663 bool ok = backing_store->GetKeyGeneratorCurrentNumber(
664 transaction->BackingStoreTransaction(),
669 LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber";
670 return make_scoped_ptr(new IndexedDBKey());
672 if (current_number < 0 || current_number > max_generator_value)
673 return make_scoped_ptr(new IndexedDBKey());
675 return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber));
678 static bool UpdateKeyGenerator(IndexedDBBackingStore* backing_store,
679 IndexedDBTransaction* transaction,
681 int64 object_store_id,
682 const IndexedDBKey& key,
683 bool check_current) {
684 DCHECK_EQ(WebIDBKeyTypeNumber, key.type());
685 return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
686 transaction->BackingStoreTransaction(),
689 static_cast<int64>(floor(key.number())) + 1,
693 struct IndexedDBDatabase::PutOperationParams {
694 PutOperationParams() {}
695 int64 object_store_id;
697 scoped_ptr<IndexedDBKey> key;
698 IndexedDBDatabase::PutMode put_mode;
699 scoped_refptr<IndexedDBCallbacks> callbacks;
700 std::vector<IndexKeys> index_keys;
703 DISALLOW_COPY_AND_ASSIGN(PutOperationParams);
706 void IndexedDBDatabase::Put(int64 transaction_id,
707 int64 object_store_id,
709 scoped_ptr<IndexedDBKey> key,
711 scoped_refptr<IndexedDBCallbacks> callbacks,
712 const std::vector<IndexKeys>& index_keys) {
713 IDB_TRACE("IndexedDBDatabase::Put");
714 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
717 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
719 if (!ValidateObjectStoreId(object_store_id))
723 scoped_ptr<PutOperationParams> params(new PutOperationParams());
724 params->object_store_id = object_store_id;
725 params->value.swap(*value);
726 params->key = key.Pass();
727 params->put_mode = put_mode;
728 params->callbacks = callbacks;
729 params->index_keys = index_keys;
730 transaction->ScheduleTask(base::Bind(
731 &IndexedDBDatabase::PutOperation, this, base::Passed(¶ms)));
734 void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
735 IndexedDBTransaction* transaction) {
736 IDB_TRACE("IndexedDBDatabase::PutOperation");
737 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
738 bool key_was_generated = false;
740 DCHECK(metadata_.object_stores.find(params->object_store_id) !=
741 metadata_.object_stores.end());
742 const IndexedDBObjectStoreMetadata& object_store =
743 metadata_.object_stores[params->object_store_id];
744 DCHECK(object_store.auto_increment || params->key->IsValid());
746 scoped_ptr<IndexedDBKey> key;
747 if (params->put_mode != IndexedDBDatabase::CURSOR_UPDATE &&
748 object_store.auto_increment && !params->key->IsValid()) {
749 scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey(
750 backing_store_.get(), transaction, id(), params->object_store_id);
751 key_was_generated = true;
752 if (!auto_inc_key->IsValid()) {
753 params->callbacks->OnError(
754 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
755 "Maximum key generator value reached."));
758 key = auto_inc_key.Pass();
760 key = params->key.Pass();
763 DCHECK(key->IsValid());
765 IndexedDBBackingStore::RecordIdentifier record_identifier;
766 if (params->put_mode == IndexedDBDatabase::ADD_ONLY) {
768 bool ok = backing_store_->KeyExistsInObjectStore(
769 transaction->BackingStoreTransaction(),
771 params->object_store_id,
776 params->callbacks->OnError(
777 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
778 "Internal error checking key existence."));
782 params->callbacks->OnError(
783 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
784 "Key already exists in the object store."));
789 ScopedVector<IndexWriter> index_writers;
790 base::string16 error_message;
791 bool obeys_constraints = false;
792 bool backing_store_success = MakeIndexWriters(transaction,
793 backing_store_.get(),
802 if (!backing_store_success) {
803 params->callbacks->OnError(IndexedDBDatabaseError(
804 blink::WebIDBDatabaseExceptionUnknownError,
805 "Internal error: backing store error updating index keys."));
808 if (!obeys_constraints) {
809 params->callbacks->OnError(IndexedDBDatabaseError(
810 blink::WebIDBDatabaseExceptionConstraintError, error_message));
814 // Before this point, don't do any mutation. After this point, rollback the
815 // transaction in case of error.
816 backing_store_success =
817 backing_store_->PutRecord(transaction->BackingStoreTransaction(),
819 params->object_store_id,
823 if (!backing_store_success) {
824 params->callbacks->OnError(IndexedDBDatabaseError(
825 blink::WebIDBDatabaseExceptionUnknownError,
826 "Internal error: backing store error performing put/add."));
830 for (size_t i = 0; i < index_writers.size(); ++i) {
831 IndexWriter* index_writer = index_writers[i];
832 index_writer->WriteIndexKeys(record_identifier,
833 backing_store_.get(),
834 transaction->BackingStoreTransaction(),
836 params->object_store_id);
839 if (object_store.auto_increment &&
840 params->put_mode != IndexedDBDatabase::CURSOR_UPDATE &&
841 key->type() == WebIDBKeyTypeNumber) {
842 bool ok = UpdateKeyGenerator(backing_store_.get(),
845 params->object_store_id,
849 params->callbacks->OnError(
850 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
851 "Internal error updating key generator."));
856 params->callbacks->OnSuccess(*key);
859 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
860 int64 object_store_id,
861 scoped_ptr<IndexedDBKey> primary_key,
862 const std::vector<IndexKeys>& index_keys) {
863 IDB_TRACE("IndexedDBDatabase::SetIndexKeys");
864 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
867 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
869 // TODO(alecflett): This method could be asynchronous, but we need to
870 // evaluate if it's worth the extra complexity.
871 IndexedDBBackingStore::RecordIdentifier record_identifier;
873 bool ok = backing_store_->KeyExistsInObjectStore(
874 transaction->BackingStoreTransaction(),
882 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
883 "Internal error setting index keys."));
887 transaction->Abort(IndexedDBDatabaseError(
888 blink::WebIDBDatabaseExceptionUnknownError,
889 "Internal error setting index keys for object store."));
893 ScopedVector<IndexWriter> index_writers;
894 base::string16 error_message;
895 bool obeys_constraints = false;
896 DCHECK(metadata_.object_stores.find(object_store_id) !=
897 metadata_.object_stores.end());
898 const IndexedDBObjectStoreMetadata& object_store_metadata =
899 metadata_.object_stores[object_store_id];
900 bool backing_store_success = MakeIndexWriters(transaction,
903 object_store_metadata,
910 if (!backing_store_success) {
911 transaction->Abort(IndexedDBDatabaseError(
912 blink::WebIDBDatabaseExceptionUnknownError,
913 "Internal error: backing store error updating index keys."));
916 if (!obeys_constraints) {
917 transaction->Abort(IndexedDBDatabaseError(
918 blink::WebIDBDatabaseExceptionConstraintError, error_message));
922 for (size_t i = 0; i < index_writers.size(); ++i) {
923 IndexWriter* index_writer = index_writers[i];
924 index_writer->WriteIndexKeys(record_identifier,
926 transaction->BackingStoreTransaction(),
932 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id,
934 const std::vector<int64>& index_ids) {
935 IDB_TRACE("IndexedDBDatabase::SetIndexesReady");
936 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
939 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
941 transaction->ScheduleTask(
942 IndexedDBDatabase::PREEMPTIVE_TASK,
943 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation,
948 void IndexedDBDatabase::SetIndexesReadyOperation(
950 IndexedDBTransaction* transaction) {
951 IDB_TRACE("IndexedDBDatabase::SetIndexesReadyOperation");
952 for (size_t i = 0; i < index_count; ++i)
953 transaction->DidCompletePreemptiveEvent();
956 struct IndexedDBDatabase::OpenCursorOperationParams {
957 OpenCursorOperationParams() {}
958 int64 object_store_id;
960 scoped_ptr<IndexedDBKeyRange> key_range;
961 indexed_db::CursorDirection direction;
962 indexed_db::CursorType cursor_type;
963 IndexedDBDatabase::TaskType task_type;
964 scoped_refptr<IndexedDBCallbacks> callbacks;
967 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams);
970 void IndexedDBDatabase::OpenCursor(
971 int64 transaction_id,
972 int64 object_store_id,
974 scoped_ptr<IndexedDBKeyRange> key_range,
975 indexed_db::CursorDirection direction,
978 scoped_refptr<IndexedDBCallbacks> callbacks) {
979 IDB_TRACE("IndexedDBDatabase::OpenCursor");
980 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
984 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
987 scoped_ptr<OpenCursorOperationParams> params(new OpenCursorOperationParams());
988 params->object_store_id = object_store_id;
989 params->index_id = index_id;
990 params->key_range = key_range.Pass();
991 params->direction = direction;
992 params->cursor_type =
993 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE;
994 params->task_type = task_type;
995 params->callbacks = callbacks;
996 transaction->ScheduleTask(base::Bind(
997 &IndexedDBDatabase::OpenCursorOperation, this, base::Passed(¶ms)));
1000 void IndexedDBDatabase::OpenCursorOperation(
1001 scoped_ptr<OpenCursorOperationParams> params,
1002 IndexedDBTransaction* transaction) {
1003 IDB_TRACE("IndexedDBDatabase::OpenCursorOperation");
1005 // The frontend has begun indexing, so this pauses the transaction
1006 // until the indexing is complete. This can't happen any earlier
1007 // because we don't want to switch to early mode in case multiple
1008 // indexes are being created in a row, with Put()'s in between.
1009 if (params->task_type == IndexedDBDatabase::PREEMPTIVE_TASK)
1010 transaction->AddPreemptiveEvent();
1012 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1013 if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
1014 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1015 DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK);
1016 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1017 transaction->BackingStoreTransaction(),
1019 params->object_store_id,
1023 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
1024 transaction->BackingStoreTransaction(),
1026 params->object_store_id,
1031 DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK);
1032 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1033 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1034 transaction->BackingStoreTransaction(),
1036 params->object_store_id,
1041 backing_store_cursor = backing_store_->OpenIndexCursor(
1042 transaction->BackingStoreTransaction(),
1044 params->object_store_id,
1051 if (!backing_store_cursor) {
1052 params->callbacks->OnSuccess(static_cast<std::string*>(NULL));
1056 scoped_refptr<IndexedDBCursor> cursor =
1057 new IndexedDBCursor(backing_store_cursor.Pass(),
1058 params->cursor_type,
1061 params->callbacks->OnSuccess(
1062 cursor, cursor->key(), cursor->primary_key(), cursor->Value());
1065 void IndexedDBDatabase::Count(int64 transaction_id,
1066 int64 object_store_id,
1068 scoped_ptr<IndexedDBKeyRange> key_range,
1069 scoped_refptr<IndexedDBCallbacks> callbacks) {
1070 IDB_TRACE("IndexedDBDatabase::Count");
1071 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1075 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1078 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation,
1082 base::Passed(&key_range),
1086 void IndexedDBDatabase::CountOperation(
1087 int64 object_store_id,
1089 scoped_ptr<IndexedDBKeyRange> key_range,
1090 scoped_refptr<IndexedDBCallbacks> callbacks,
1091 IndexedDBTransaction* transaction) {
1092 IDB_TRACE("IndexedDBDatabase::CountOperation");
1094 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1096 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
1097 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1098 transaction->BackingStoreTransaction(),
1102 indexed_db::CURSOR_NEXT);
1104 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1105 transaction->BackingStoreTransaction(),
1110 indexed_db::CURSOR_NEXT);
1112 if (!backing_store_cursor) {
1113 callbacks->OnSuccess(count);
1119 } while (backing_store_cursor->Continue());
1121 callbacks->OnSuccess(count);
1124 void IndexedDBDatabase::DeleteRange(
1125 int64 transaction_id,
1126 int64 object_store_id,
1127 scoped_ptr<IndexedDBKeyRange> key_range,
1128 scoped_refptr<IndexedDBCallbacks> callbacks) {
1129 IDB_TRACE("IndexedDBDatabase::DeleteRange");
1130 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1133 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
1135 if (!ValidateObjectStoreId(object_store_id))
1138 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation,
1141 base::Passed(&key_range),
1145 void IndexedDBDatabase::DeleteRangeOperation(
1146 int64 object_store_id,
1147 scoped_ptr<IndexedDBKeyRange> key_range,
1148 scoped_refptr<IndexedDBCallbacks> callbacks,
1149 IndexedDBTransaction* transaction) {
1150 IDB_TRACE("IndexedDBDatabase::DeleteRangeOperation");
1151 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor =
1152 backing_store_->OpenObjectStoreCursor(
1153 transaction->BackingStoreTransaction(),
1157 indexed_db::CURSOR_NEXT);
1158 if (backing_store_cursor) {
1160 if (!backing_store_->DeleteRecord(
1161 transaction->BackingStoreTransaction(),
1164 backing_store_cursor->record_identifier())) {
1166 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1167 "Internal error deleting data in range"));
1170 } while (backing_store_cursor->Continue());
1173 callbacks->OnSuccess();
1176 void IndexedDBDatabase::Clear(int64 transaction_id,
1177 int64 object_store_id,
1178 scoped_refptr<IndexedDBCallbacks> callbacks) {
1179 IDB_TRACE("IndexedDBDatabase::Clear");
1180 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1183 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
1185 if (!ValidateObjectStoreId(object_store_id))
1188 transaction->ScheduleTask(base::Bind(
1189 &IndexedDBDatabase::ClearOperation, this, object_store_id, callbacks));
1192 void IndexedDBDatabase::ClearOperation(
1193 int64 object_store_id,
1194 scoped_refptr<IndexedDBCallbacks> callbacks,
1195 IndexedDBTransaction* transaction) {
1196 IDB_TRACE("IndexedDBDatabase::ObjectStoreClearOperation");
1197 if (!backing_store_->ClearObjectStore(
1198 transaction->BackingStoreTransaction(), id(), object_store_id)) {
1200 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1201 "Internal error clearing object store"));
1204 callbacks->OnSuccess();
1207 void IndexedDBDatabase::DeleteObjectStoreOperation(
1208 const IndexedDBObjectStoreMetadata& object_store_metadata,
1209 IndexedDBTransaction* transaction) {
1210 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreOperation");
1212 backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
1213 transaction->database()->id(),
1214 object_store_metadata.id);
1216 base::string16 error_string =
1217 ASCIIToUTF16("Internal error deleting object store '") +
1218 object_store_metadata.name + ASCIIToUTF16("'.");
1219 transaction->Abort(IndexedDBDatabaseError(
1220 blink::WebIDBDatabaseExceptionUnknownError, error_string));
1224 void IndexedDBDatabase::VersionChangeOperation(
1226 scoped_refptr<IndexedDBCallbacks> callbacks,
1227 scoped_ptr<IndexedDBConnection> connection,
1228 IndexedDBTransaction* transaction) {
1229 IDB_TRACE("IndexedDBDatabase::VersionChangeOperation");
1230 int64 old_version = metadata_.int_version;
1231 DCHECK_GT(version, old_version);
1232 metadata_.int_version = version;
1233 if (!backing_store_->UpdateIDBDatabaseIntVersion(
1234 transaction->BackingStoreTransaction(),
1236 metadata_.int_version)) {
1237 IndexedDBDatabaseError error(
1238 blink::WebIDBDatabaseExceptionUnknownError,
1240 "Internal error writing data to stable storage when "
1241 "updating version."));
1242 callbacks->OnError(error);
1243 transaction->Abort(error);
1246 DCHECK(!pending_second_half_open_);
1247 pending_second_half_open_.reset(
1248 new PendingSuccessCall(callbacks, connection.get(), version));
1249 callbacks->OnUpgradeNeeded(old_version, connection.Pass(), metadata());
1252 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
1254 DCHECK(transactions_.find(transaction->id()) != transactions_.end());
1255 DCHECK_EQ(transactions_[transaction->id()], transaction);
1256 transactions_.erase(transaction->id());
1258 if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1259 if (pending_second_half_open_) {
1261 DCHECK_EQ(pending_second_half_open_->version(), metadata_.int_version);
1262 DCHECK(metadata_.id != kInvalidId);
1264 // Connection was already minted for OnUpgradeNeeded callback.
1265 scoped_ptr<IndexedDBConnection> connection;
1266 pending_second_half_open_->callbacks()->OnSuccess(connection.Pass(),
1269 pending_second_half_open_->callbacks()->OnError(
1270 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1271 "Version change transaction was aborted in "
1272 "upgradeneeded event handler."));
1274 pending_second_half_open_.reset();
1277 // Connection queue is now unblocked.
1278 ProcessPendingCalls();
1282 void IndexedDBDatabase::TransactionCommitFailed() {
1283 // Factory may be null in unit tests.
1286 factory_->HandleBackingStoreFailure(backing_store_->origin_url());
1289 size_t IndexedDBDatabase::ConnectionCount() const {
1290 // This does not include pending open calls, as those should not block version
1291 // changes and deletes.
1292 return connections_.size();
1295 size_t IndexedDBDatabase::PendingOpenCount() const {
1296 return pending_open_calls_.size();
1299 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1300 return pending_run_version_change_transaction_call_ ? 1 : 0;
1303 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1304 return pending_second_half_open_ ? 1 : 0;
1307 size_t IndexedDBDatabase::PendingDeleteCount() const {
1308 return pending_delete_calls_.size();
1311 void IndexedDBDatabase::ProcessPendingCalls() {
1312 if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
1313 DCHECK(pending_run_version_change_transaction_call_->version() >
1314 metadata_.int_version);
1315 scoped_ptr<PendingUpgradeCall> pending_call =
1316 pending_run_version_change_transaction_call_.Pass();
1317 RunVersionChangeTransactionFinal(pending_call->callbacks(),
1318 pending_call->ReleaseConnection(),
1319 pending_call->transaction_id(),
1320 pending_call->version());
1321 DCHECK_EQ(1u, ConnectionCount());
1322 // Fall through would be a no-op, since transaction must complete
1324 DCHECK(IsDeleteDatabaseBlocked());
1325 DCHECK(IsOpenConnectionBlocked());
1329 if (!IsDeleteDatabaseBlocked()) {
1330 PendingDeleteCallList pending_delete_calls;
1331 pending_delete_calls_.swap(pending_delete_calls);
1332 while (!pending_delete_calls.empty()) {
1333 // Only the first delete call will delete the database, but each must fire
1335 scoped_ptr<PendingDeleteCall> pending_delete_call(
1336 pending_delete_calls.front());
1337 pending_delete_calls.pop_front();
1338 DeleteDatabaseFinal(pending_delete_call->callbacks());
1340 // delete_database_final should never re-queue calls.
1341 DCHECK(pending_delete_calls_.empty());
1342 // Fall through when complete, as pending opens may be unblocked.
1345 if (!IsOpenConnectionBlocked()) {
1346 PendingOpenCallList pending_open_calls;
1347 pending_open_calls_.swap(pending_open_calls);
1348 while (!pending_open_calls.empty()) {
1349 scoped_ptr<PendingOpenCall> pending_open_call(pending_open_calls.front());
1350 pending_open_calls.pop_front();
1351 OpenConnection(pending_open_call->callbacks(),
1352 pending_open_call->database_callbacks(),
1353 pending_open_call->transaction_id(),
1354 pending_open_call->version());
1359 void IndexedDBDatabase::CreateTransaction(
1360 int64 transaction_id,
1361 IndexedDBConnection* connection,
1362 const std::vector<int64>& object_store_ids,
1365 DCHECK(connections_.count(connection));
1366 DCHECK(transactions_.find(transaction_id) == transactions_.end());
1367 if (transactions_.find(transaction_id) != transactions_.end())
1370 // The transaction will add itself to this database's coordinator, which
1371 // manages the lifetime of the object.
1372 TransactionCreated(new IndexedDBTransaction(
1374 connection->callbacks(),
1375 std::set<int64>(object_store_ids.begin(), object_store_ids.end()),
1376 static_cast<indexed_db::TransactionMode>(mode),
1378 new IndexedDBBackingStore::Transaction(backing_store_)));
1381 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) {
1382 transactions_[transaction->id()] = transaction;
1385 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1386 return !pending_delete_calls_.empty() ||
1387 transaction_coordinator_.IsRunningVersionChangeTransaction() ||
1388 pending_run_version_change_transaction_call_;
1391 void IndexedDBDatabase::OpenConnection(
1392 scoped_refptr<IndexedDBCallbacks> callbacks,
1393 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
1394 int64 transaction_id,
1396 DCHECK(backing_store_);
1398 // TODO(jsbell): Should have a priority queue so that higher version
1399 // requests are processed first. http://crbug.com/225850
1400 if (IsOpenConnectionBlocked()) {
1401 // The backing store only detects data loss when it is first opened. The
1402 // presence of existing connections means we didn't even check for data loss
1403 // so there'd better not be any.
1404 DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss());
1405 pending_open_calls_.push_back(new PendingOpenCall(
1406 callbacks, database_callbacks, transaction_id, version));
1410 if (metadata_.id == kInvalidId) {
1411 // The database was deleted then immediately re-opened; OpenInternal()
1412 // recreates it in the backing store.
1413 if (OpenInternal()) {
1414 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
1415 metadata_.int_version);
1417 base::string16 message;
1418 if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1419 message = ASCIIToUTF16(
1420 "Internal error opening database with no version specified.");
1423 ASCIIToUTF16("Internal error opening database with version ") +
1424 Int64ToString16(version);
1426 callbacks->OnError(IndexedDBDatabaseError(
1427 blink::WebIDBDatabaseExceptionUnknownError, message));
1432 // We infer that the database didn't exist from its lack of either type of
1434 bool is_new_database =
1435 metadata_.version == kNoStringVersion &&
1436 metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
1438 scoped_ptr<IndexedDBConnection> connection(
1439 new IndexedDBConnection(this, database_callbacks));
1441 if (version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
1442 // For unit tests only - skip upgrade steps. Calling from script with
1443 // DEFAULT_INT_VERSION throws exception.
1444 // TODO(jsbell): DCHECK that not in unit tests.
1445 DCHECK(is_new_database);
1446 connections_.insert(connection.get());
1447 callbacks->OnSuccess(connection.Pass(), this->metadata());
1451 if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1452 if (!is_new_database) {
1453 connections_.insert(connection.get());
1454 callbacks->OnSuccess(connection.Pass(), this->metadata());
1457 // Spec says: If no version is specified and no database exists, set
1458 // database version to 1.
1462 if (version > metadata_.int_version) {
1463 connections_.insert(connection.get());
1464 RunVersionChangeTransaction(
1465 callbacks, connection.Pass(), transaction_id, version);
1468 if (version < metadata_.int_version) {
1469 callbacks->OnError(IndexedDBDatabaseError(
1470 blink::WebIDBDatabaseExceptionVersionError,
1471 ASCIIToUTF16("The requested version (") + Int64ToString16(version) +
1472 ASCIIToUTF16(") is less than the existing version (") +
1473 Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
1476 DCHECK_EQ(version, metadata_.int_version);
1477 connections_.insert(connection.get());
1478 callbacks->OnSuccess(connection.Pass(), this->metadata());
1481 void IndexedDBDatabase::RunVersionChangeTransaction(
1482 scoped_refptr<IndexedDBCallbacks> callbacks,
1483 scoped_ptr<IndexedDBConnection> connection,
1484 int64 transaction_id,
1485 int64 requested_version) {
1488 DCHECK(connections_.count(connection.get()));
1489 if (ConnectionCount() > 1) {
1490 DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss());
1491 // Front end ensures the event is not fired at connections that have
1492 // close_pending set.
1493 for (ConnectionSet::const_iterator it = connections_.begin();
1494 it != connections_.end();
1496 if (*it != connection.get()) {
1497 (*it)->callbacks()->OnVersionChange(metadata_.int_version,
1501 // TODO(jsbell): Remove the call to OnBlocked and instead wait
1502 // until the frontend tells us that all the "versionchange" events
1503 // have been delivered. http://crbug.com/100123
1504 callbacks->OnBlocked(metadata_.int_version);
1506 DCHECK(!pending_run_version_change_transaction_call_);
1507 pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall(
1508 callbacks, connection.Pass(), transaction_id, requested_version));
1511 RunVersionChangeTransactionFinal(
1512 callbacks, connection.Pass(), transaction_id, requested_version);
1515 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1516 scoped_refptr<IndexedDBCallbacks> callbacks,
1517 scoped_ptr<IndexedDBConnection> connection,
1518 int64 transaction_id,
1519 int64 requested_version) {
1521 std::vector<int64> object_store_ids;
1522 CreateTransaction(transaction_id,
1525 indexed_db::TRANSACTION_VERSION_CHANGE);
1527 transactions_[transaction_id]
1528 ->ScheduleTask(base::Bind(&IndexedDBDatabase::VersionChangeOperation,
1532 base::Passed(&connection)),
1533 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation,
1536 metadata_.int_version));
1538 DCHECK(!pending_second_half_open_);
1541 void IndexedDBDatabase::DeleteDatabase(
1542 scoped_refptr<IndexedDBCallbacks> callbacks) {
1544 if (IsDeleteDatabaseBlocked()) {
1545 for (ConnectionSet::const_iterator it = connections_.begin();
1546 it != connections_.end();
1548 // Front end ensures the event is not fired at connections that have
1549 // close_pending set.
1550 (*it)->callbacks()->OnVersionChange(
1551 metadata_.int_version, IndexedDBDatabaseMetadata::NO_INT_VERSION);
1553 // TODO(jsbell): Only fire OnBlocked if there are open
1554 // connections after the VersionChangeEvents are received, not
1555 // just set up to fire. http://crbug.com/100123
1556 callbacks->OnBlocked(metadata_.int_version);
1557 pending_delete_calls_.push_back(new PendingDeleteCall(callbacks));
1560 DeleteDatabaseFinal(callbacks);
1563 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1564 return !!ConnectionCount();
1567 void IndexedDBDatabase::DeleteDatabaseFinal(
1568 scoped_refptr<IndexedDBCallbacks> callbacks) {
1569 DCHECK(!IsDeleteDatabaseBlocked());
1570 DCHECK(backing_store_);
1571 if (!backing_store_->DeleteDatabase(metadata_.name)) {
1573 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1574 "Internal error deleting database."));
1577 metadata_.version = kNoStringVersion;
1578 metadata_.id = kInvalidId;
1579 metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1580 metadata_.object_stores.clear();
1581 callbacks->OnSuccess();
1583 factory_->DatabaseDeleted(identifier_);
1586 void IndexedDBDatabase::ForceClose() {
1587 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1588 scoped_refptr<IndexedDBDatabase> protect(this);
1589 ConnectionSet::const_iterator it = connections_.begin();
1590 while (it != connections_.end()) {
1591 IndexedDBConnection* connection = *it++;
1592 connection->ForceClose();
1594 DCHECK(connections_.empty());
1597 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
1598 DCHECK(connections_.count(connection));
1599 DCHECK(connection->IsConnected());
1600 DCHECK(connection->database() == this);
1602 // Abort outstanding transactions from the closing connection. This
1603 // can not happen if the close is requested by the connection itself
1604 // as the front-end defers the close until all transactions are
1605 // complete, but can occur on process termination or forced close.
1607 TransactionMap transactions(transactions_);
1608 for (TransactionMap::const_iterator it = transactions.begin(),
1609 end = transactions.end();
1612 if (it->second->connection() == connection->callbacks())
1614 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1615 "Connection is closing."));
1619 connections_.erase(connection);
1620 if (pending_second_half_open_ &&
1621 pending_second_half_open_->connection() == connection) {
1622 pending_second_half_open_->callbacks()->OnError(
1623 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1624 "The connection was closed."));
1625 pending_second_half_open_.reset();
1628 ProcessPendingCalls();
1630 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1631 if (!ConnectionCount() && !pending_open_calls_.size() &&
1632 !pending_delete_calls_.size()) {
1633 DCHECK(transactions_.empty());
1635 const GURL origin_url = backing_store_->origin_url();
1636 backing_store_ = NULL;
1638 // factory_ should only be null in unit tests.
1639 // TODO(jsbell): DCHECK(factory_ || !in_unit_tests) - somehow.
1641 factory_->ReleaseDatabase(identifier_, forced);
1647 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1648 int64 object_store_id,
1649 IndexedDBTransaction* transaction) {
1650 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
1651 DCHECK(!transaction);
1652 RemoveObjectStore(object_store_id);
1655 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1656 const IndexedDBObjectStoreMetadata& object_store_metadata,
1657 IndexedDBTransaction* transaction) {
1658 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation");
1659 DCHECK(!transaction);
1660 AddObjectStore(object_store_metadata,
1661 IndexedDBObjectStoreMetadata::kInvalidId);
1664 void IndexedDBDatabase::VersionChangeAbortOperation(
1665 const base::string16& previous_version,
1666 int64 previous_int_version,
1667 IndexedDBTransaction* transaction) {
1668 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
1669 DCHECK(!transaction);
1670 metadata_.version = previous_version;
1671 metadata_.int_version = previous_int_version;
1674 } // namespace content