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/strings/string_number_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "content/browser/indexed_db/indexed_db_connection.h"
16 #include "content/browser/indexed_db/indexed_db_cursor.h"
17 #include "content/browser/indexed_db/indexed_db_factory.h"
18 #include "content/browser/indexed_db/indexed_db_index_writer.h"
19 #include "content/browser/indexed_db/indexed_db_tracing.h"
20 #include "content/browser/indexed_db/indexed_db_transaction.h"
21 #include "content/common/indexed_db/indexed_db_key_path.h"
22 #include "content/common/indexed_db/indexed_db_key_range.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
26 using base::Int64ToString16;
27 using WebKit::WebIDBKeyTypeNumber;
31 // PendingOpenCall has a scoped_refptr<IndexedDBDatabaseCallbacks> because it
32 // isn't a connection yet.
33 class IndexedDBDatabase::PendingOpenCall {
35 PendingOpenCall(scoped_refptr<IndexedDBCallbacks> callbacks,
36 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
39 : callbacks_(callbacks),
40 database_callbacks_(database_callbacks),
42 transaction_id_(transaction_id) {}
43 scoped_refptr<IndexedDBCallbacks> Callbacks() { return callbacks_; }
44 scoped_refptr<IndexedDBDatabaseCallbacks> DatabaseCallbacks() {
45 return database_callbacks_;
47 int64 Version() { return version_; }
48 int64 TransactionId() const { return transaction_id_; }
51 scoped_refptr<IndexedDBCallbacks> callbacks_;
52 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks_;
54 const int64 transaction_id_;
57 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
58 // in-progress connection.
59 class IndexedDBDatabase::PendingUpgradeCall {
61 PendingUpgradeCall(scoped_refptr<IndexedDBCallbacks> callbacks,
62 scoped_ptr<IndexedDBConnection> connection,
65 : callbacks_(callbacks),
66 connection_(connection.Pass()),
68 transaction_id_(transaction_id) {}
69 scoped_refptr<IndexedDBCallbacks> Callbacks() { return callbacks_; }
70 scoped_ptr<IndexedDBConnection> Connection() { return connection_.Pass(); }
71 int64 Version() { return version_; }
72 int64 TransactionId() const { return transaction_id_; }
75 scoped_refptr<IndexedDBCallbacks> callbacks_;
76 scoped_ptr<IndexedDBConnection> connection_;
78 const int64 transaction_id_;
81 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
82 // owned elsewhere, but we need to cancel the success call if that connection
83 // closes before it is sent.
84 class IndexedDBDatabase::PendingSuccessCall {
86 PendingSuccessCall(scoped_refptr<IndexedDBCallbacks> callbacks,
87 IndexedDBConnection* connection,
90 : callbacks_(callbacks),
91 connection_(connection),
93 transaction_id_(transaction_id) {}
94 scoped_refptr<IndexedDBCallbacks> Callbacks() { return callbacks_; }
95 IndexedDBConnection* Connection() { return connection_; }
96 int64 Version() { return version_; }
97 int64 TransactionId() const { return transaction_id_; }
100 scoped_refptr<IndexedDBCallbacks> callbacks_;
101 IndexedDBConnection* connection_;
103 const int64 transaction_id_;
106 class IndexedDBDatabase::PendingDeleteCall {
108 explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks)
109 : callbacks_(callbacks) {}
110 scoped_refptr<IndexedDBCallbacks> Callbacks() { return callbacks_; }
113 scoped_refptr<IndexedDBCallbacks> callbacks_;
116 scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
117 const string16& name,
118 IndexedDBBackingStore* backing_store,
119 IndexedDBFactory* factory,
120 const Identifier& unique_identifier) {
121 scoped_refptr<IndexedDBDatabase> database =
122 new IndexedDBDatabase(name, backing_store, factory, unique_identifier);
123 if (!database->OpenInternal())
129 const base::string16::value_type kNoStringVersion[] = {0};
131 template <typename T, typename U>
132 bool Contains(const T& container, const U& item) {
133 return container.find(item) != container.end();
137 IndexedDBDatabase::IndexedDBDatabase(const string16& name,
138 IndexedDBBackingStore* backing_store,
139 IndexedDBFactory* factory,
140 const Identifier& unique_identifier)
141 : backing_store_(backing_store),
145 IndexedDBDatabaseMetadata::NO_INT_VERSION,
147 identifier_(unique_identifier),
149 running_version_change_transaction_(NULL) {
150 DCHECK(!metadata_.name.empty());
153 void IndexedDBDatabase::AddObjectStore(
154 const IndexedDBObjectStoreMetadata& object_store,
155 int64 new_max_object_store_id) {
156 DCHECK(metadata_.object_stores.find(object_store.id) ==
157 metadata_.object_stores.end());
158 if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
159 DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
160 metadata_.max_object_store_id = new_max_object_store_id;
162 metadata_.object_stores[object_store.id] = object_store;
165 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) {
166 DCHECK(metadata_.object_stores.find(object_store_id) !=
167 metadata_.object_stores.end());
168 metadata_.object_stores.erase(object_store_id);
171 void IndexedDBDatabase::AddIndex(int64 object_store_id,
172 const IndexedDBIndexMetadata& index,
173 int64 new_max_index_id) {
174 DCHECK(metadata_.object_stores.find(object_store_id) !=
175 metadata_.object_stores.end());
176 IndexedDBObjectStoreMetadata object_store =
177 metadata_.object_stores[object_store_id];
179 DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
180 object_store.indexes[index.id] = index;
181 if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
182 DCHECK_LT(object_store.max_index_id, new_max_index_id);
183 object_store.max_index_id = new_max_index_id;
185 metadata_.object_stores[object_store_id] = object_store;
188 void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) {
189 DCHECK(metadata_.object_stores.find(object_store_id) !=
190 metadata_.object_stores.end());
191 IndexedDBObjectStoreMetadata object_store =
192 metadata_.object_stores[object_store_id];
194 DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
195 object_store.indexes.erase(index_id);
196 metadata_.object_stores[object_store_id] = object_store;
199 bool IndexedDBDatabase::OpenInternal() {
200 bool success = false;
201 bool ok = backing_store_->GetIDBDatabaseMetaData(
202 metadata_.name, &metadata_, &success);
203 DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
204 << " id = " << metadata_.id;
208 return backing_store_->GetObjectStores(metadata_.id,
209 &metadata_.object_stores);
211 return backing_store_->CreateIDBDatabaseMetaData(
212 metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id);
215 IndexedDBDatabase::~IndexedDBDatabase() {
216 DCHECK(transactions_.empty());
217 DCHECK(pending_open_calls_.empty());
218 DCHECK(pending_delete_calls_.empty());
221 IndexedDBTransaction* IndexedDBDatabase::GetTransaction(
222 int64 transaction_id) const {
223 TransactionMap::const_iterator trans_iterator =
224 transactions_.find(transaction_id);
225 if (trans_iterator == transactions_.end())
227 return trans_iterator->second;
230 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id) const {
231 if (!Contains(metadata_.object_stores, object_store_id)) {
232 DLOG(ERROR) << "Invalid object_store_id";
238 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id,
239 int64 index_id) const {
240 if (!ValidateObjectStoreId(object_store_id))
242 const IndexedDBObjectStoreMetadata& object_store_metadata =
243 metadata_.object_stores.find(object_store_id)->second;
244 if (!Contains(object_store_metadata.indexes, index_id)) {
245 DLOG(ERROR) << "Invalid index_id";
251 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
252 int64 object_store_id,
253 int64 index_id) const {
254 if (!ValidateObjectStoreId(object_store_id))
256 const IndexedDBObjectStoreMetadata& object_store_metadata =
257 metadata_.object_stores.find(object_store_id)->second;
258 if (index_id != IndexedDBIndexMetadata::kInvalidId &&
259 !Contains(object_store_metadata.indexes, index_id)) {
260 DLOG(ERROR) << "Invalid index_id";
266 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
267 int64 object_store_id,
268 int64 index_id) const {
269 if (!ValidateObjectStoreId(object_store_id))
271 const IndexedDBObjectStoreMetadata& object_store_metadata =
272 metadata_.object_stores.find(object_store_id)->second;
273 if (Contains(object_store_metadata.indexes, index_id)) {
274 DLOG(ERROR) << "Invalid index_id";
280 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
281 int64 object_store_id,
282 const string16& name,
283 const IndexedDBKeyPath& key_path,
284 bool auto_increment) {
285 IDB_TRACE("IndexedDBDatabase::CreateObjectStore");
286 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
289 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
291 if (Contains(metadata_.object_stores, object_store_id)) {
292 DLOG(ERROR) << "Invalid object_store_id";
296 IndexedDBObjectStoreMetadata object_store_metadata(
301 IndexedDBDatabase::kMinimumIndexId);
303 transaction->ScheduleTask(
304 base::Bind(&IndexedDBDatabase::CreateObjectStoreOperation,
306 object_store_metadata),
307 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
311 AddObjectStore(object_store_metadata, object_store_id);
314 void IndexedDBDatabase::CreateObjectStoreOperation(
315 const IndexedDBObjectStoreMetadata& object_store_metadata,
316 IndexedDBTransaction* transaction) {
317 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreOperation");
318 if (!backing_store_->CreateObjectStore(
319 transaction->BackingStoreTransaction(),
320 transaction->database()->id(),
321 object_store_metadata.id,
322 object_store_metadata.name,
323 object_store_metadata.key_path,
324 object_store_metadata.auto_increment)) {
325 transaction->Abort(IndexedDBDatabaseError(
326 WebKit::WebIDBDatabaseExceptionUnknownError,
327 ASCIIToUTF16("Internal error creating object store '") +
328 object_store_metadata.name + ASCIIToUTF16("'.")));
333 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
334 int64 object_store_id) {
335 IDB_TRACE("IndexedDBDatabase::DeleteObjectStore");
336 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
339 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
341 if (!ValidateObjectStoreId(object_store_id))
344 const IndexedDBObjectStoreMetadata& object_store_metadata =
345 metadata_.object_stores[object_store_id];
347 transaction->ScheduleTask(
348 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation,
350 object_store_metadata),
351 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
353 object_store_metadata));
354 RemoveObjectStore(object_store_id);
357 void IndexedDBDatabase::CreateIndex(int64 transaction_id,
358 int64 object_store_id,
360 const string16& name,
361 const IndexedDBKeyPath& key_path,
364 IDB_TRACE("IndexedDBDatabase::CreateIndex");
365 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
368 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
370 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id))
372 const IndexedDBIndexMetadata index_metadata(
373 name, index_id, key_path, unique, multi_entry);
375 transaction->ScheduleTask(
376 base::Bind(&IndexedDBDatabase::CreateIndexOperation,
380 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation,
385 AddIndex(object_store_id, index_metadata, index_id);
388 void IndexedDBDatabase::CreateIndexOperation(
389 int64 object_store_id,
390 const IndexedDBIndexMetadata& index_metadata,
391 IndexedDBTransaction* transaction) {
392 IDB_TRACE("IndexedDBDatabase::CreateIndexOperation");
393 if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
394 transaction->database()->id(),
398 index_metadata.key_path,
399 index_metadata.unique,
400 index_metadata.multi_entry)) {
401 string16 error_string = ASCIIToUTF16("Internal error creating index '") +
402 index_metadata.name + ASCIIToUTF16("'.");
403 transaction->Abort(IndexedDBDatabaseError(
404 WebKit::WebIDBDatabaseExceptionUnknownError, error_string));
409 void IndexedDBDatabase::CreateIndexAbortOperation(
410 int64 object_store_id,
412 IndexedDBTransaction* transaction) {
413 IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation");
414 DCHECK(!transaction);
415 RemoveIndex(object_store_id, index_id);
418 void IndexedDBDatabase::DeleteIndex(int64 transaction_id,
419 int64 object_store_id,
421 IDB_TRACE("IndexedDBDatabase::DeleteIndex");
422 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
425 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
427 if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id))
429 const IndexedDBIndexMetadata& index_metadata =
430 metadata_.object_stores[object_store_id].indexes[index_id];
432 transaction->ScheduleTask(
433 base::Bind(&IndexedDBDatabase::DeleteIndexOperation,
437 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation,
442 RemoveIndex(object_store_id, index_id);
445 void IndexedDBDatabase::DeleteIndexOperation(
446 int64 object_store_id,
447 const IndexedDBIndexMetadata& index_metadata,
448 IndexedDBTransaction* transaction) {
449 IDB_TRACE("IndexedDBDatabase::DeleteIndexOperation");
450 bool ok = backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
451 transaction->database()->id(),
455 string16 error_string = ASCIIToUTF16("Internal error deleting index '") +
456 index_metadata.name + ASCIIToUTF16("'.");
457 transaction->Abort(IndexedDBDatabaseError(
458 WebKit::WebIDBDatabaseExceptionUnknownError, error_string));
462 void IndexedDBDatabase::DeleteIndexAbortOperation(
463 int64 object_store_id,
464 const IndexedDBIndexMetadata& index_metadata,
465 IndexedDBTransaction* transaction) {
466 IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation");
467 DCHECK(!transaction);
468 AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId);
471 void IndexedDBDatabase::Commit(int64 transaction_id) {
472 // The frontend suggests that we commit, but we may have previously initiated
473 // an abort, and so have disposed of the transaction. on_abort has already
474 // been dispatched to the frontend, so it will find out about that
476 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
478 transaction->Commit();
481 void IndexedDBDatabase::Abort(int64 transaction_id) {
482 // If the transaction is unknown, then it has already been aborted by the
483 // backend before this call so it is safe to ignore it.
484 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
486 transaction->Abort();
489 void IndexedDBDatabase::Abort(int64 transaction_id,
490 const IndexedDBDatabaseError& error) {
491 // If the transaction is unknown, then it has already been aborted by the
492 // backend before this call so it is safe to ignore it.
493 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
495 transaction->Abort(error);
498 void IndexedDBDatabase::Get(int64 transaction_id,
499 int64 object_store_id,
501 scoped_ptr<IndexedDBKeyRange> key_range,
503 scoped_refptr<IndexedDBCallbacks> callbacks) {
504 IDB_TRACE("IndexedDBDatabase::Get");
505 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
509 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
512 transaction->ScheduleTask(base::Bind(
513 &IndexedDBDatabase::GetOperation,
518 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
522 void IndexedDBDatabase::GetOperation(
523 int64 object_store_id,
525 scoped_ptr<IndexedDBKeyRange> key_range,
526 indexed_db::CursorType cursor_type,
527 scoped_refptr<IndexedDBCallbacks> callbacks,
528 IndexedDBTransaction* transaction) {
529 IDB_TRACE("IndexedDBDatabase::GetOperation");
531 DCHECK(metadata_.object_stores.find(object_store_id) !=
532 metadata_.object_stores.end());
533 const IndexedDBObjectStoreMetadata& object_store_metadata =
534 metadata_.object_stores[object_store_id];
536 const IndexedDBKey* key;
538 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
539 if (key_range->IsOnlyKey()) {
540 key = &key_range->lower();
542 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
543 DCHECK_NE(cursor_type, indexed_db::CURSOR_KEY_ONLY);
544 // ObjectStore Retrieval Operation
545 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
546 transaction->BackingStoreTransaction(),
550 indexed_db::CURSOR_NEXT);
551 } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
552 // Index Value Retrieval Operation
553 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
554 transaction->BackingStoreTransaction(),
559 indexed_db::CURSOR_NEXT);
561 // Index Referenced Value Retrieval Operation
562 backing_store_cursor = backing_store_->OpenIndexCursor(
563 transaction->BackingStoreTransaction(),
568 indexed_db::CURSOR_NEXT);
571 if (!backing_store_cursor) {
572 callbacks->OnSuccess();
576 key = &backing_store_cursor->key();
579 scoped_ptr<IndexedDBKey> primary_key;
581 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
582 // Object Store Retrieval Operation
584 ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
591 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
592 "Internal error in GetRecord."));
597 callbacks->OnSuccess();
601 if (object_store_metadata.auto_increment &&
602 !object_store_metadata.key_path.IsNull()) {
603 callbacks->OnSuccess(&value, *key, object_store_metadata.key_path);
607 callbacks->OnSuccess(&value);
611 // From here we are dealing only with indexes.
612 ok = backing_store_->GetPrimaryKeyViaIndex(
613 transaction->BackingStoreTransaction(),
621 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
622 "Internal error in GetPrimaryKeyViaIndex."));
626 callbacks->OnSuccess();
629 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
630 // Index Value Retrieval Operation
631 callbacks->OnSuccess(*primary_key);
635 // Index Referenced Value Retrieval Operation
637 ok = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
644 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
645 "Internal error in GetRecord."));
650 callbacks->OnSuccess();
653 if (object_store_metadata.auto_increment &&
654 !object_store_metadata.key_path.IsNull()) {
655 callbacks->OnSuccess(&value, *primary_key, object_store_metadata.key_path);
658 callbacks->OnSuccess(&value);
661 static scoped_ptr<IndexedDBKey> GenerateKey(
662 scoped_refptr<IndexedDBBackingStore> backing_store,
663 scoped_refptr<IndexedDBTransaction> transaction,
665 int64 object_store_id) {
666 const int64 max_generator_value =
667 9007199254740992LL; // Maximum integer storable as ECMAScript number.
668 int64 current_number;
669 bool ok = backing_store->GetKeyGeneratorCurrentNumber(
670 transaction->BackingStoreTransaction(),
675 LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber";
676 return make_scoped_ptr(new IndexedDBKey());
678 if (current_number < 0 || current_number > max_generator_value)
679 return make_scoped_ptr(new IndexedDBKey());
681 return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber));
684 static bool UpdateKeyGenerator(
685 scoped_refptr<IndexedDBBackingStore> backing_store,
686 scoped_refptr<IndexedDBTransaction> transaction,
688 int64 object_store_id,
689 const IndexedDBKey& key,
690 bool check_current) {
691 DCHECK_EQ(WebIDBKeyTypeNumber, key.type());
692 return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
693 transaction->BackingStoreTransaction(),
696 static_cast<int64>(floor(key.number())) + 1,
700 struct IndexedDBDatabase::PutOperationParams {
701 PutOperationParams() {}
702 int64 object_store_id;
704 scoped_ptr<IndexedDBKey> key;
705 IndexedDBDatabase::PutMode put_mode;
706 scoped_refptr<IndexedDBCallbacks> callbacks;
707 std::vector<int64> index_ids;
708 std::vector<IndexKeys> index_keys;
710 DISALLOW_COPY_AND_ASSIGN(PutOperationParams);
713 void IndexedDBDatabase::Put(int64 transaction_id,
714 int64 object_store_id,
716 scoped_ptr<IndexedDBKey> key,
718 scoped_refptr<IndexedDBCallbacks> callbacks,
719 const std::vector<int64>& index_ids,
720 const std::vector<IndexKeys>& index_keys) {
721 IDB_TRACE("IndexedDBDatabase::Put");
722 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
725 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
727 if (!ValidateObjectStoreId(object_store_id))
731 scoped_ptr<PutOperationParams> params(new PutOperationParams());
732 params->object_store_id = object_store_id;
733 params->value.swap(*value);
734 params->key = key.Pass();
735 params->put_mode = put_mode;
736 params->callbacks = callbacks;
737 params->index_ids = index_ids;
738 params->index_keys = index_keys;
739 transaction->ScheduleTask(base::Bind(
740 &IndexedDBDatabase::PutOperation, this, base::Passed(¶ms)));
743 void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
744 IndexedDBTransaction* transaction) {
745 IDB_TRACE("IndexedDBDatabase::PutOperation");
746 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
747 DCHECK_EQ(params->index_ids.size(), params->index_keys.size());
748 bool key_was_generated = false;
750 DCHECK(metadata_.object_stores.find(params->object_store_id) !=
751 metadata_.object_stores.end());
752 const IndexedDBObjectStoreMetadata& object_store =
753 metadata_.object_stores[params->object_store_id];
754 DCHECK(object_store.auto_increment || params->key->IsValid());
756 scoped_ptr<IndexedDBKey> key;
757 if (params->put_mode != IndexedDBDatabase::CURSOR_UPDATE &&
758 object_store.auto_increment && !params->key->IsValid()) {
759 scoped_ptr<IndexedDBKey> auto_inc_key =
760 GenerateKey(backing_store_, transaction, id(), params->object_store_id);
761 key_was_generated = true;
762 if (!auto_inc_key->IsValid()) {
763 params->callbacks->OnError(
764 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionConstraintError,
765 "Maximum key generator value reached."));
768 key = auto_inc_key.Pass();
770 key = params->key.Pass();
773 DCHECK(key->IsValid());
775 IndexedDBBackingStore::RecordIdentifier record_identifier;
776 if (params->put_mode == IndexedDBDatabase::ADD_ONLY) {
778 bool ok = backing_store_->KeyExistsInObjectStore(
779 transaction->BackingStoreTransaction(),
781 params->object_store_id,
786 params->callbacks->OnError(
787 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
788 "Internal error checking key existence."));
792 params->callbacks->OnError(
793 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionConstraintError,
794 "Key already exists in the object store."));
799 ScopedVector<IndexWriter> index_writers;
800 string16 error_message;
801 bool obeys_constraints = false;
802 bool backing_store_success = MakeIndexWriters(transaction,
803 backing_store_.get(),
813 if (!backing_store_success) {
814 params->callbacks->OnError(IndexedDBDatabaseError(
815 WebKit::WebIDBDatabaseExceptionUnknownError,
816 "Internal error: backing store error updating index keys."));
819 if (!obeys_constraints) {
820 params->callbacks->OnError(IndexedDBDatabaseError(
821 WebKit::WebIDBDatabaseExceptionConstraintError, error_message));
825 // Before this point, don't do any mutation. After this point, rollback the
826 // transaction in case of error.
827 backing_store_success =
828 backing_store_->PutRecord(transaction->BackingStoreTransaction(),
830 params->object_store_id,
834 if (!backing_store_success) {
835 params->callbacks->OnError(IndexedDBDatabaseError(
836 WebKit::WebIDBDatabaseExceptionUnknownError,
837 "Internal error: backing store error performing put/add."));
841 for (size_t i = 0; i < index_writers.size(); ++i) {
842 IndexWriter* index_writer = index_writers[i];
843 index_writer->WriteIndexKeys(record_identifier,
844 backing_store_.get(),
845 transaction->BackingStoreTransaction(),
847 params->object_store_id);
850 if (object_store.auto_increment &&
851 params->put_mode != IndexedDBDatabase::CURSOR_UPDATE &&
852 key->type() == WebIDBKeyTypeNumber) {
853 bool ok = UpdateKeyGenerator(backing_store_,
856 params->object_store_id,
860 params->callbacks->OnError(
861 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
862 "Internal error updating key generator."));
867 params->callbacks->OnSuccess(*key);
870 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
871 int64 object_store_id,
872 scoped_ptr<IndexedDBKey> primary_key,
873 const std::vector<int64>& index_ids,
874 const std::vector<IndexKeys>& index_keys) {
875 IDB_TRACE("IndexedDBDatabase::SetIndexKeys");
876 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
879 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
881 // TODO(alecflett): This method could be asynchronous, but we need to
882 // evaluate if it's worth the extra complexity.
883 IndexedDBBackingStore::RecordIdentifier record_identifier;
885 bool ok = backing_store_->KeyExistsInObjectStore(
886 transaction->BackingStoreTransaction(),
894 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
895 "Internal error setting index keys."));
899 transaction->Abort(IndexedDBDatabaseError(
900 WebKit::WebIDBDatabaseExceptionUnknownError,
901 "Internal error setting index keys for object store."));
905 ScopedVector<IndexWriter> index_writers;
906 string16 error_message;
907 bool obeys_constraints = false;
908 DCHECK(metadata_.object_stores.find(object_store_id) !=
909 metadata_.object_stores.end());
910 const IndexedDBObjectStoreMetadata& object_store_metadata =
911 metadata_.object_stores[object_store_id];
912 bool backing_store_success = MakeIndexWriters(transaction,
915 object_store_metadata,
923 if (!backing_store_success) {
924 transaction->Abort(IndexedDBDatabaseError(
925 WebKit::WebIDBDatabaseExceptionUnknownError,
926 "Internal error: backing store error updating index keys."));
929 if (!obeys_constraints) {
930 transaction->Abort(IndexedDBDatabaseError(
931 WebKit::WebIDBDatabaseExceptionConstraintError, error_message));
935 for (size_t i = 0; i < index_writers.size(); ++i) {
936 IndexWriter* index_writer = index_writers[i];
937 index_writer->WriteIndexKeys(record_identifier,
939 transaction->BackingStoreTransaction(),
945 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id,
947 const std::vector<int64>& index_ids) {
948 IDB_TRACE("IndexedDBDatabase::SetIndexesReady");
949 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
952 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
954 transaction->ScheduleTask(
955 IndexedDBDatabase::PREEMPTIVE_TASK,
956 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation,
961 void IndexedDBDatabase::SetIndexesReadyOperation(
963 IndexedDBTransaction* transaction) {
964 IDB_TRACE("IndexedDBDatabase::SetIndexesReadyOperation");
965 for (size_t i = 0; i < index_count; ++i)
966 transaction->DidCompletePreemptiveEvent();
969 struct IndexedDBDatabase::OpenCursorOperationParams {
970 OpenCursorOperationParams() {}
971 int64 object_store_id;
973 scoped_ptr<IndexedDBKeyRange> key_range;
974 indexed_db::CursorDirection direction;
975 indexed_db::CursorType cursor_type;
976 IndexedDBDatabase::TaskType task_type;
977 scoped_refptr<IndexedDBCallbacks> callbacks;
979 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams);
982 void IndexedDBDatabase::OpenCursor(
983 int64 transaction_id,
984 int64 object_store_id,
986 scoped_ptr<IndexedDBKeyRange> key_range,
987 indexed_db::CursorDirection direction,
990 scoped_refptr<IndexedDBCallbacks> callbacks) {
991 IDB_TRACE("IndexedDBDatabase::OpenCursor");
992 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
996 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
999 scoped_ptr<OpenCursorOperationParams> params(new OpenCursorOperationParams());
1000 params->object_store_id = object_store_id;
1001 params->index_id = index_id;
1002 params->key_range = key_range.Pass();
1003 params->direction = direction;
1004 params->cursor_type =
1005 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE;
1006 params->task_type = task_type;
1007 params->callbacks = callbacks;
1008 transaction->ScheduleTask(base::Bind(
1009 &IndexedDBDatabase::OpenCursorOperation, this, base::Passed(¶ms)));
1012 void IndexedDBDatabase::OpenCursorOperation(
1013 scoped_ptr<OpenCursorOperationParams> params,
1014 IndexedDBTransaction* transaction) {
1015 IDB_TRACE("IndexedDBDatabase::OpenCursorOperation");
1017 // The frontend has begun indexing, so this pauses the transaction
1018 // until the indexing is complete. This can't happen any earlier
1019 // because we don't want to switch to early mode in case multiple
1020 // indexes are being created in a row, with Put()'s in between.
1021 if (params->task_type == IndexedDBDatabase::PREEMPTIVE_TASK)
1022 transaction->AddPreemptiveEvent();
1024 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1025 if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
1026 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1027 DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK);
1028 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1029 transaction->BackingStoreTransaction(),
1031 params->object_store_id,
1035 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
1036 transaction->BackingStoreTransaction(),
1038 params->object_store_id,
1043 DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK);
1044 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1045 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1046 transaction->BackingStoreTransaction(),
1048 params->object_store_id,
1053 backing_store_cursor = backing_store_->OpenIndexCursor(
1054 transaction->BackingStoreTransaction(),
1056 params->object_store_id,
1063 if (!backing_store_cursor) {
1064 params->callbacks->OnSuccess(static_cast<std::string*>(NULL));
1068 scoped_refptr<IndexedDBCursor> cursor =
1069 new IndexedDBCursor(backing_store_cursor.Pass(),
1070 params->cursor_type,
1073 params->callbacks->OnSuccess(
1074 cursor, cursor->key(), cursor->primary_key(), cursor->Value());
1077 void IndexedDBDatabase::Count(int64 transaction_id,
1078 int64 object_store_id,
1080 scoped_ptr<IndexedDBKeyRange> key_range,
1081 scoped_refptr<IndexedDBCallbacks> callbacks) {
1082 IDB_TRACE("IndexedDBDatabase::Count");
1083 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1087 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1090 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation,
1094 base::Passed(&key_range),
1098 void IndexedDBDatabase::CountOperation(
1099 int64 object_store_id,
1101 scoped_ptr<IndexedDBKeyRange> key_range,
1102 scoped_refptr<IndexedDBCallbacks> callbacks,
1103 IndexedDBTransaction* transaction) {
1104 IDB_TRACE("IndexedDBDatabase::CountOperation");
1106 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1108 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
1109 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1110 transaction->BackingStoreTransaction(),
1114 indexed_db::CURSOR_NEXT);
1116 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1117 transaction->BackingStoreTransaction(),
1122 indexed_db::CURSOR_NEXT);
1124 if (!backing_store_cursor) {
1125 callbacks->OnSuccess(count);
1131 } while (backing_store_cursor->Continue());
1133 callbacks->OnSuccess(count);
1136 void IndexedDBDatabase::DeleteRange(
1137 int64 transaction_id,
1138 int64 object_store_id,
1139 scoped_ptr<IndexedDBKeyRange> key_range,
1140 scoped_refptr<IndexedDBCallbacks> callbacks) {
1141 IDB_TRACE("IndexedDBDatabase::DeleteRange");
1142 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1145 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
1147 if (!ValidateObjectStoreId(object_store_id))
1150 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation,
1153 base::Passed(&key_range),
1157 void IndexedDBDatabase::DeleteRangeOperation(
1158 int64 object_store_id,
1159 scoped_ptr<IndexedDBKeyRange> key_range,
1160 scoped_refptr<IndexedDBCallbacks> callbacks,
1161 IndexedDBTransaction* transaction) {
1162 IDB_TRACE("IndexedDBDatabase::DeleteRangeOperation");
1163 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor =
1164 backing_store_->OpenObjectStoreCursor(
1165 transaction->BackingStoreTransaction(),
1169 indexed_db::CURSOR_NEXT);
1170 if (backing_store_cursor) {
1172 if (!backing_store_->DeleteRecord(
1173 transaction->BackingStoreTransaction(),
1176 backing_store_cursor->record_identifier())) {
1178 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1179 "Internal error deleting data in range"));
1182 } while (backing_store_cursor->Continue());
1185 callbacks->OnSuccess();
1188 void IndexedDBDatabase::Clear(int64 transaction_id,
1189 int64 object_store_id,
1190 scoped_refptr<IndexedDBCallbacks> callbacks) {
1191 IDB_TRACE("IndexedDBDatabase::Clear");
1192 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1195 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
1197 if (!ValidateObjectStoreId(object_store_id))
1200 transaction->ScheduleTask(base::Bind(
1201 &IndexedDBDatabase::ClearOperation, this, object_store_id, callbacks));
1204 void IndexedDBDatabase::ClearOperation(
1205 int64 object_store_id,
1206 scoped_refptr<IndexedDBCallbacks> callbacks,
1207 IndexedDBTransaction* transaction) {
1208 IDB_TRACE("IndexedDBDatabase::ObjectStoreClearOperation");
1209 if (!backing_store_->ClearObjectStore(
1210 transaction->BackingStoreTransaction(), id(), object_store_id)) {
1212 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1213 "Internal error clearing object store"));
1216 callbacks->OnSuccess();
1219 void IndexedDBDatabase::DeleteObjectStoreOperation(
1220 const IndexedDBObjectStoreMetadata& object_store_metadata,
1221 IndexedDBTransaction* transaction) {
1222 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreOperation");
1224 backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
1225 transaction->database()->id(),
1226 object_store_metadata.id);
1228 string16 error_string =
1229 ASCIIToUTF16("Internal error deleting object store '") +
1230 object_store_metadata.name + ASCIIToUTF16("'.");
1231 transaction->Abort(IndexedDBDatabaseError(
1232 WebKit::WebIDBDatabaseExceptionUnknownError, error_string));
1236 void IndexedDBDatabase::VersionChangeOperation(
1238 scoped_refptr<IndexedDBCallbacks> callbacks,
1239 scoped_ptr<IndexedDBConnection> connection,
1240 WebKit::WebIDBCallbacks::DataLoss data_loss,
1241 std::string data_loss_message,
1242 IndexedDBTransaction* transaction) {
1243 IDB_TRACE("IndexedDBDatabase::VersionChangeOperation");
1244 int64 old_version = metadata_.int_version;
1245 DCHECK_GT(version, old_version);
1246 metadata_.int_version = version;
1247 if (!backing_store_->UpdateIDBDatabaseIntVersion(
1248 transaction->BackingStoreTransaction(),
1250 metadata_.int_version)) {
1251 IndexedDBDatabaseError error(
1252 WebKit::WebIDBDatabaseExceptionUnknownError,
1254 "Internal error writing data to stable storage when "
1255 "updating version."));
1256 callbacks->OnError(error);
1257 transaction->Abort(error);
1260 DCHECK(!pending_second_half_open_);
1261 pending_second_half_open_.reset(new PendingSuccessCall(
1262 callbacks, connection.get(), transaction->id(), version));
1263 callbacks->OnUpgradeNeeded(
1264 old_version, connection.Pass(), metadata(), data_loss, data_loss_message);
1267 void IndexedDBDatabase::TransactionStarted(IndexedDBTransaction* transaction) {
1269 if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1270 DCHECK(!running_version_change_transaction_);
1271 running_version_change_transaction_ = transaction;
1275 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction) {
1277 DCHECK(transactions_.find(transaction->id()) != transactions_.end());
1278 DCHECK_EQ(transactions_[transaction->id()], transaction);
1279 transactions_.erase(transaction->id());
1280 if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1281 DCHECK_EQ(transaction, running_version_change_transaction_);
1282 running_version_change_transaction_ = NULL;
1286 void IndexedDBDatabase::TransactionFinishedAndAbortFired(
1287 IndexedDBTransaction* transaction) {
1288 if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1289 if (pending_second_half_open_) {
1290 pending_second_half_open_->Callbacks()->OnError(
1291 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionAbortError,
1292 "Version change transaction was aborted in "
1293 "upgradeneeded event handler."));
1294 pending_second_half_open_.reset();
1296 ProcessPendingCalls();
1300 void IndexedDBDatabase::TransactionFinishedAndCompleteFired(
1301 IndexedDBTransaction* transaction) {
1302 if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1303 DCHECK(pending_second_half_open_);
1304 if (pending_second_half_open_) {
1305 DCHECK_EQ(pending_second_half_open_->Version(), metadata_.int_version);
1306 DCHECK(metadata_.id != kInvalidId);
1308 // Connection was already minted for OnUpgradeNeeded callback.
1309 scoped_ptr<IndexedDBConnection> connection;
1311 pending_second_half_open_->Callbacks()->OnSuccess(connection.Pass(),
1313 pending_second_half_open_.reset();
1315 ProcessPendingCalls();
1319 void IndexedDBDatabase::TransactionCommitFailed() {
1320 factory_->HandleBackingStoreFailure(backing_store_->origin_url());
1323 size_t IndexedDBDatabase::ConnectionCount() const {
1324 // This does not include pending open calls, as those should not block version
1325 // changes and deletes.
1326 return connections_.size();
1329 size_t IndexedDBDatabase::PendingOpenCount() const {
1330 return pending_open_calls_.size();
1333 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1334 return pending_run_version_change_transaction_call_ ? 1 : 0;
1337 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1338 return pending_second_half_open_ ? 1 : 0;
1341 size_t IndexedDBDatabase::PendingDeleteCount() const {
1342 return pending_delete_calls_.size();
1345 void IndexedDBDatabase::ProcessPendingCalls() {
1346 if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
1347 DCHECK(pending_run_version_change_transaction_call_->Version() >
1348 metadata_.int_version);
1349 scoped_ptr<PendingUpgradeCall> pending_call =
1350 pending_run_version_change_transaction_call_.Pass();
1351 RunVersionChangeTransactionFinal(pending_call->Callbacks(),
1352 pending_call->Connection(),
1353 pending_call->TransactionId(),
1354 pending_call->Version());
1355 DCHECK_EQ(static_cast<size_t>(1), ConnectionCount());
1356 // Fall through would be a no-op, since transaction must complete
1358 DCHECK(IsDeleteDatabaseBlocked());
1359 DCHECK(IsOpenConnectionBlocked());
1363 if (!IsDeleteDatabaseBlocked()) {
1364 PendingDeleteCallList pending_delete_calls;
1365 pending_delete_calls_.swap(pending_delete_calls);
1366 while (!pending_delete_calls.empty()) {
1367 // Only the first delete call will delete the database, but each must fire
1369 scoped_ptr<PendingDeleteCall> pending_delete_call(
1370 pending_delete_calls.front());
1371 pending_delete_calls.pop_front();
1372 DeleteDatabaseFinal(pending_delete_call->Callbacks());
1374 // delete_database_final should never re-queue calls.
1375 DCHECK(pending_delete_calls_.empty());
1376 // Fall through when complete, as pending opens may be unblocked.
1379 if (!IsOpenConnectionBlocked()) {
1380 PendingOpenCallList pending_open_calls;
1381 pending_open_calls_.swap(pending_open_calls);
1382 while (!pending_open_calls.empty()) {
1383 scoped_ptr<PendingOpenCall> pending_open_call(pending_open_calls.front());
1384 pending_open_calls.pop_front();
1385 OpenConnection(pending_open_call->Callbacks(),
1386 pending_open_call->DatabaseCallbacks(),
1387 pending_open_call->TransactionId(),
1388 pending_open_call->Version());
1393 void IndexedDBDatabase::CreateTransaction(
1394 int64 transaction_id,
1395 IndexedDBConnection* connection,
1396 const std::vector<int64>& object_store_ids,
1399 DCHECK(connections_.count(connection));
1400 DCHECK(transactions_.find(transaction_id) == transactions_.end());
1401 if (transactions_.find(transaction_id) != transactions_.end())
1404 scoped_refptr<IndexedDBTransaction> transaction = new IndexedDBTransaction(
1406 connection->callbacks(),
1407 std::set<int64>(object_store_ids.begin(), object_store_ids.end()),
1408 static_cast<indexed_db::TransactionMode>(mode),
1410 transactions_[transaction_id] = transaction;
1413 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1414 return !pending_delete_calls_.empty() ||
1415 running_version_change_transaction_ ||
1416 pending_run_version_change_transaction_call_;
1419 void IndexedDBDatabase::OpenConnection(
1420 scoped_refptr<IndexedDBCallbacks> callbacks,
1421 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
1422 int64 transaction_id,
1424 const WebKit::WebIDBCallbacks::DataLoss kDataLoss =
1425 WebKit::WebIDBCallbacks::DataLossNone;
1427 callbacks, database_callbacks, transaction_id, version, kDataLoss, "");
1430 void IndexedDBDatabase::OpenConnection(
1431 scoped_refptr<IndexedDBCallbacks> callbacks,
1432 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
1433 int64 transaction_id,
1435 WebKit::WebIDBCallbacks::DataLoss data_loss,
1436 std::string data_loss_message) {
1437 DCHECK(backing_store_);
1439 // TODO(jsbell): Should have a priority queue so that higher version
1440 // requests are processed first. http://crbug.com/225850
1441 if (IsOpenConnectionBlocked()) {
1442 // The backing store only detects data loss when it is first opened. The
1443 // presence of existing connections means we didn't even check for data loss
1444 // so there'd better not be any.
1445 DCHECK_NE(WebKit::WebIDBCallbacks::DataLossTotal, data_loss);
1446 pending_open_calls_.push_back(new PendingOpenCall(
1447 callbacks, database_callbacks, transaction_id, version));
1451 if (metadata_.id == kInvalidId) {
1452 // The database was deleted then immediately re-opened; OpenInternal()
1453 // recreates it in the backing store.
1454 if (OpenInternal()) {
1455 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
1456 metadata_.int_version);
1459 if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION)
1460 message = ASCIIToUTF16(
1461 "Internal error opening database with no version specified.");
1464 ASCIIToUTF16("Internal error opening database with version ") +
1465 Int64ToString16(version);
1466 callbacks->OnError(IndexedDBDatabaseError(
1467 WebKit::WebIDBDatabaseExceptionUnknownError, message));
1472 // We infer that the database didn't exist from its lack of either type of
1474 bool is_new_database =
1475 metadata_.version == kNoStringVersion &&
1476 metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
1478 scoped_ptr<IndexedDBConnection> connection(
1479 new IndexedDBConnection(this, database_callbacks));
1481 if (version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
1482 // For unit tests only - skip upgrade steps. Calling from script with
1483 // DEFAULT_INT_VERSION throws exception.
1484 // TODO(jsbell): DCHECK that not in unit tests.
1485 DCHECK(is_new_database);
1486 connections_.insert(connection.get());
1487 callbacks->OnSuccess(connection.Pass(), this->metadata());
1491 if (version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1492 if (!is_new_database) {
1493 connections_.insert(connection.get());
1494 callbacks->OnSuccess(connection.Pass(), this->metadata());
1497 // Spec says: If no version is specified and no database exists, set
1498 // database version to 1.
1502 if (version > metadata_.int_version) {
1503 connections_.insert(connection.get());
1504 RunVersionChangeTransaction(callbacks,
1512 if (version < metadata_.int_version) {
1513 callbacks->OnError(IndexedDBDatabaseError(
1514 WebKit::WebIDBDatabaseExceptionVersionError,
1515 ASCIIToUTF16("The requested version (") + Int64ToString16(version) +
1516 ASCIIToUTF16(") is less than the existing version (") +
1517 Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
1520 DCHECK_EQ(version, metadata_.int_version);
1521 connections_.insert(connection.get());
1522 callbacks->OnSuccess(connection.Pass(), this->metadata());
1525 void IndexedDBDatabase::RunVersionChangeTransaction(
1526 scoped_refptr<IndexedDBCallbacks> callbacks,
1527 scoped_ptr<IndexedDBConnection> connection,
1528 int64 transaction_id,
1529 int64 requested_version,
1530 WebKit::WebIDBCallbacks::DataLoss data_loss,
1531 std::string data_loss_message) {
1534 DCHECK(connections_.count(connection.get()));
1535 if (ConnectionCount() > 1) {
1536 DCHECK_NE(WebKit::WebIDBCallbacks::DataLossTotal, data_loss);
1537 // Front end ensures the event is not fired at connections that have
1538 // close_pending set.
1539 for (ConnectionSet::const_iterator it = connections_.begin();
1540 it != connections_.end();
1542 if (*it != connection.get()) {
1543 (*it)->callbacks()->OnVersionChange(metadata_.int_version,
1547 // TODO(jsbell): Remove the call to OnBlocked and instead wait
1548 // until the frontend tells us that all the "versionchange" events
1549 // have been delivered. http://crbug.com/100123
1550 callbacks->OnBlocked(metadata_.int_version);
1552 DCHECK(!pending_run_version_change_transaction_call_);
1553 pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall(
1554 callbacks, connection.Pass(), transaction_id, requested_version));
1557 RunVersionChangeTransactionFinal(callbacks,
1565 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1566 scoped_refptr<IndexedDBCallbacks> callbacks,
1567 scoped_ptr<IndexedDBConnection> connection,
1568 int64 transaction_id,
1569 int64 requested_version) {
1570 const WebKit::WebIDBCallbacks::DataLoss kDataLoss =
1571 WebKit::WebIDBCallbacks::DataLossNone;
1572 RunVersionChangeTransactionFinal(callbacks,
1580 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1581 scoped_refptr<IndexedDBCallbacks> callbacks,
1582 scoped_ptr<IndexedDBConnection> connection,
1583 int64 transaction_id,
1584 int64 requested_version,
1585 WebKit::WebIDBCallbacks::DataLoss data_loss,
1586 std::string data_loss_message) {
1588 std::vector<int64> object_store_ids;
1589 CreateTransaction(transaction_id,
1592 indexed_db::TRANSACTION_VERSION_CHANGE);
1593 scoped_refptr<IndexedDBTransaction> transaction =
1594 transactions_[transaction_id];
1596 transaction->ScheduleTask(
1597 base::Bind(&IndexedDBDatabase::VersionChangeOperation,
1601 base::Passed(&connection),
1604 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation,
1607 metadata_.int_version));
1609 DCHECK(!pending_second_half_open_);
1612 void IndexedDBDatabase::DeleteDatabase(
1613 scoped_refptr<IndexedDBCallbacks> callbacks) {
1615 if (IsDeleteDatabaseBlocked()) {
1616 for (ConnectionSet::const_iterator it = connections_.begin();
1617 it != connections_.end();
1619 // Front end ensures the event is not fired at connections that have
1620 // close_pending set.
1621 (*it)->callbacks()->OnVersionChange(
1622 metadata_.int_version, IndexedDBDatabaseMetadata::NO_INT_VERSION);
1624 // TODO(jsbell): Only fire OnBlocked if there are open
1625 // connections after the VersionChangeEvents are received, not
1626 // just set up to fire. http://crbug.com/100123
1627 callbacks->OnBlocked(metadata_.int_version);
1628 pending_delete_calls_.push_back(new PendingDeleteCall(callbacks));
1631 DeleteDatabaseFinal(callbacks);
1634 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1635 return !!ConnectionCount();
1638 void IndexedDBDatabase::DeleteDatabaseFinal(
1639 scoped_refptr<IndexedDBCallbacks> callbacks) {
1640 DCHECK(!IsDeleteDatabaseBlocked());
1641 DCHECK(backing_store_);
1642 if (!backing_store_->DeleteDatabase(metadata_.name)) {
1644 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1645 "Internal error deleting database."));
1648 metadata_.version = kNoStringVersion;
1649 metadata_.id = kInvalidId;
1650 metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1651 metadata_.object_stores.clear();
1652 callbacks->OnSuccess();
1655 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
1656 DCHECK(connections_.count(connection));
1657 DCHECK(connection->IsConnected());
1658 DCHECK(connection->database() == this);
1660 // Abort outstanding transactions from the closing connection. This
1661 // can not happen if the close is requested by the connection itself
1662 // as the front-end defers the close until all transactions are
1663 // complete, but can occur on process termination or forced close.
1665 TransactionMap transactions(transactions_);
1666 for (TransactionMap::const_iterator it = transactions.begin(),
1667 end = transactions.end();
1670 if (it->second->connection() == connection->callbacks())
1672 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
1673 "Connection is closing."));
1677 connections_.erase(connection);
1678 if (pending_second_half_open_ &&
1679 pending_second_half_open_->Connection() == connection) {
1680 pending_second_half_open_->Callbacks()->OnError(
1681 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionAbortError,
1682 "The connection was closed."));
1683 pending_second_half_open_.reset();
1686 ProcessPendingCalls();
1688 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1689 if (!ConnectionCount() && !pending_open_calls_.size() &&
1690 !pending_delete_calls_.size()) {
1691 DCHECK(transactions_.empty());
1693 const GURL origin_url = backing_store_->origin_url();
1694 backing_store_ = NULL;
1696 // factory_ should only be null in unit tests.
1697 // TODO(jsbell): DCHECK(factory_ || !in_unit_tests) - somehow.
1699 factory_->ReleaseDatabase(identifier_, origin_url, forced);
1705 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1706 int64 object_store_id,
1707 IndexedDBTransaction* transaction) {
1708 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
1709 DCHECK(!transaction);
1710 RemoveObjectStore(object_store_id);
1713 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1714 const IndexedDBObjectStoreMetadata& object_store_metadata,
1715 IndexedDBTransaction* transaction) {
1716 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation");
1717 DCHECK(!transaction);
1718 AddObjectStore(object_store_metadata,
1719 IndexedDBObjectStoreMetadata::kInvalidId);
1722 void IndexedDBDatabase::VersionChangeAbortOperation(
1723 const string16& previous_version,
1724 int64 previous_int_version,
1725 IndexedDBTransaction* transaction) {
1726 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
1727 DCHECK(!transaction);
1728 metadata_.version = previous_version;
1729 metadata_.int_version = previous_int_version;
1732 } // namespace content