Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_database.cc
blob1d9ccbbc5f273bfda9efabeb901453c50a71a0da
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"
7 #include <math.h>
8 #include <set>
10 #include "base/auto_reset.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/memory/scoped_vector.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "content/browser/indexed_db/indexed_db_blob_info.h"
18 #include "content/browser/indexed_db/indexed_db_connection.h"
19 #include "content/browser/indexed_db/indexed_db_context_impl.h"
20 #include "content/browser/indexed_db/indexed_db_cursor.h"
21 #include "content/browser/indexed_db/indexed_db_factory.h"
22 #include "content/browser/indexed_db/indexed_db_index_writer.h"
23 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
24 #include "content/browser/indexed_db/indexed_db_tracing.h"
25 #include "content/browser/indexed_db/indexed_db_transaction.h"
26 #include "content/browser/indexed_db/indexed_db_value.h"
27 #include "content/common/indexed_db/indexed_db_key_path.h"
28 #include "content/common/indexed_db/indexed_db_key_range.h"
29 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
30 #include "webkit/browser/blob/blob_data_handle.h"
32 using base::ASCIIToUTF16;
33 using base::Int64ToString16;
34 using blink::WebIDBKeyTypeNumber;
36 namespace content {
38 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
39 // in-progress connection.
40 class IndexedDBDatabase::PendingUpgradeCall {
41 public:
42 PendingUpgradeCall(scoped_refptr<IndexedDBCallbacks> callbacks,
43 scoped_ptr<IndexedDBConnection> connection,
44 int64 transaction_id,
45 int64 version)
46 : callbacks_(callbacks),
47 connection_(connection.Pass()),
48 version_(version),
49 transaction_id_(transaction_id) {}
50 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
51 // Takes ownership of the connection object.
52 scoped_ptr<IndexedDBConnection> ReleaseConnection() WARN_UNUSED_RESULT {
53 return connection_.Pass();
55 int64 version() const { return version_; }
56 int64 transaction_id() const { return transaction_id_; }
58 private:
59 scoped_refptr<IndexedDBCallbacks> callbacks_;
60 scoped_ptr<IndexedDBConnection> connection_;
61 int64 version_;
62 const int64 transaction_id_;
65 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
66 // owned elsewhere, but we need to cancel the success call if that connection
67 // closes before it is sent.
68 class IndexedDBDatabase::PendingSuccessCall {
69 public:
70 PendingSuccessCall(scoped_refptr<IndexedDBCallbacks> callbacks,
71 IndexedDBConnection* connection,
72 int64 version)
73 : callbacks_(callbacks), connection_(connection), version_(version) {}
74 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
75 IndexedDBConnection* connection() const { return connection_; }
76 int64 version() const { return version_; }
78 private:
79 scoped_refptr<IndexedDBCallbacks> callbacks_;
80 IndexedDBConnection* connection_;
81 int64 version_;
84 class IndexedDBDatabase::PendingDeleteCall {
85 public:
86 explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks)
87 : callbacks_(callbacks) {}
88 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
90 private:
91 scoped_refptr<IndexedDBCallbacks> callbacks_;
94 scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
95 const base::string16& name,
96 IndexedDBBackingStore* backing_store,
97 IndexedDBFactory* factory,
98 const Identifier& unique_identifier,
99 leveldb::Status* s) {
100 scoped_refptr<IndexedDBDatabase> database =
101 new IndexedDBDatabase(name, backing_store, factory, unique_identifier);
102 *s = database->OpenInternal();
103 if (s->ok())
104 return database;
105 else
106 return NULL;
109 namespace {
110 const base::string16::value_type kNoStringVersion[] = {0};
113 IndexedDBDatabase::IndexedDBDatabase(const base::string16& name,
114 IndexedDBBackingStore* backing_store,
115 IndexedDBFactory* factory,
116 const Identifier& unique_identifier)
117 : backing_store_(backing_store),
118 metadata_(name,
119 kInvalidId,
120 kNoStringVersion,
121 IndexedDBDatabaseMetadata::NO_INT_VERSION,
122 kInvalidId),
123 identifier_(unique_identifier),
124 factory_(factory) {
127 void IndexedDBDatabase::AddObjectStore(
128 const IndexedDBObjectStoreMetadata& object_store,
129 int64 new_max_object_store_id) {
130 DCHECK(metadata_.object_stores.find(object_store.id) ==
131 metadata_.object_stores.end());
132 if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) {
133 DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id);
134 metadata_.max_object_store_id = new_max_object_store_id;
136 metadata_.object_stores[object_store.id] = object_store;
139 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) {
140 DCHECK(metadata_.object_stores.find(object_store_id) !=
141 metadata_.object_stores.end());
142 metadata_.object_stores.erase(object_store_id);
145 void IndexedDBDatabase::AddIndex(int64 object_store_id,
146 const IndexedDBIndexMetadata& index,
147 int64 new_max_index_id) {
148 DCHECK(metadata_.object_stores.find(object_store_id) !=
149 metadata_.object_stores.end());
150 IndexedDBObjectStoreMetadata object_store =
151 metadata_.object_stores[object_store_id];
153 DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end());
154 object_store.indexes[index.id] = index;
155 if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) {
156 DCHECK_LT(object_store.max_index_id, new_max_index_id);
157 object_store.max_index_id = new_max_index_id;
159 metadata_.object_stores[object_store_id] = object_store;
162 void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) {
163 DCHECK(metadata_.object_stores.find(object_store_id) !=
164 metadata_.object_stores.end());
165 IndexedDBObjectStoreMetadata object_store =
166 metadata_.object_stores[object_store_id];
168 DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end());
169 object_store.indexes.erase(index_id);
170 metadata_.object_stores[object_store_id] = object_store;
173 leveldb::Status IndexedDBDatabase::OpenInternal() {
174 bool success = false;
175 leveldb::Status s = backing_store_->GetIDBDatabaseMetaData(
176 metadata_.name, &metadata_, &success);
177 DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success
178 << " id = " << metadata_.id;
179 if (!s.ok())
180 return s;
181 if (success)
182 return backing_store_->GetObjectStores(metadata_.id,
183 &metadata_.object_stores);
185 return backing_store_->CreateIDBDatabaseMetaData(
186 metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id);
189 IndexedDBDatabase::~IndexedDBDatabase() {
190 DCHECK(transactions_.empty());
191 DCHECK(pending_open_calls_.empty());
192 DCHECK(pending_delete_calls_.empty());
195 scoped_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection(
196 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
197 int child_process_id) {
198 scoped_ptr<IndexedDBConnection> connection(
199 new IndexedDBConnection(this, database_callbacks));
200 connections_.insert(connection.get());
201 backing_store_->GrantChildProcessPermissions(child_process_id);
202 return connection.Pass();
205 IndexedDBTransaction* IndexedDBDatabase::GetTransaction(
206 int64 transaction_id) const {
207 TransactionMap::const_iterator trans_iterator =
208 transactions_.find(transaction_id);
209 if (trans_iterator == transactions_.end())
210 return NULL;
211 return trans_iterator->second;
214 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id) const {
215 if (!ContainsKey(metadata_.object_stores, object_store_id)) {
216 DLOG(ERROR) << "Invalid object_store_id";
217 return false;
219 return true;
222 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id,
223 int64 index_id) const {
224 if (!ValidateObjectStoreId(object_store_id))
225 return false;
226 const IndexedDBObjectStoreMetadata& object_store_metadata =
227 metadata_.object_stores.find(object_store_id)->second;
228 if (!ContainsKey(object_store_metadata.indexes, index_id)) {
229 DLOG(ERROR) << "Invalid index_id";
230 return false;
232 return true;
235 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
236 int64 object_store_id,
237 int64 index_id) const {
238 if (!ValidateObjectStoreId(object_store_id))
239 return false;
240 const IndexedDBObjectStoreMetadata& object_store_metadata =
241 metadata_.object_stores.find(object_store_id)->second;
242 if (index_id != IndexedDBIndexMetadata::kInvalidId &&
243 !ContainsKey(object_store_metadata.indexes, index_id)) {
244 DLOG(ERROR) << "Invalid index_id";
245 return false;
247 return true;
250 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
251 int64 object_store_id,
252 int64 index_id) const {
253 if (!ValidateObjectStoreId(object_store_id))
254 return false;
255 const IndexedDBObjectStoreMetadata& object_store_metadata =
256 metadata_.object_stores.find(object_store_id)->second;
257 if (ContainsKey(object_store_metadata.indexes, index_id)) {
258 DLOG(ERROR) << "Invalid index_id";
259 return false;
261 return true;
264 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id,
265 int64 object_store_id,
266 const base::string16& name,
267 const IndexedDBKeyPath& key_path,
268 bool auto_increment) {
269 IDB_TRACE("IndexedDBDatabase::CreateObjectStore");
270 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
271 if (!transaction)
272 return;
273 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
275 if (ContainsKey(metadata_.object_stores, object_store_id)) {
276 DLOG(ERROR) << "Invalid object_store_id";
277 return;
280 IndexedDBObjectStoreMetadata object_store_metadata(
281 name,
282 object_store_id,
283 key_path,
284 auto_increment,
285 IndexedDBDatabase::kMinimumIndexId);
287 transaction->ScheduleTask(
288 base::Bind(&IndexedDBDatabase::CreateObjectStoreOperation,
289 this,
290 object_store_metadata),
291 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation,
292 this,
293 object_store_id));
295 AddObjectStore(object_store_metadata, object_store_id);
298 void IndexedDBDatabase::CreateObjectStoreOperation(
299 const IndexedDBObjectStoreMetadata& object_store_metadata,
300 IndexedDBTransaction* transaction) {
301 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreOperation");
302 leveldb::Status s =
303 backing_store_->CreateObjectStore(transaction->BackingStoreTransaction(),
304 transaction->database()->id(),
305 object_store_metadata.id,
306 object_store_metadata.name,
307 object_store_metadata.key_path,
308 object_store_metadata.auto_increment);
309 if (!s.ok()) {
310 IndexedDBDatabaseError error(
311 blink::WebIDBDatabaseExceptionUnknownError,
312 ASCIIToUTF16("Internal error creating object store '") +
313 object_store_metadata.name + ASCIIToUTF16("'."));
314 transaction->Abort(error);
315 if (s.IsCorruption())
316 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
317 error);
318 return;
322 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id,
323 int64 object_store_id) {
324 IDB_TRACE("IndexedDBDatabase::DeleteObjectStore");
325 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
326 if (!transaction)
327 return;
328 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
330 if (!ValidateObjectStoreId(object_store_id))
331 return;
333 const IndexedDBObjectStoreMetadata& object_store_metadata =
334 metadata_.object_stores[object_store_id];
336 transaction->ScheduleTask(
337 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation,
338 this,
339 object_store_metadata),
340 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
341 this,
342 object_store_metadata));
343 RemoveObjectStore(object_store_id);
346 void IndexedDBDatabase::CreateIndex(int64 transaction_id,
347 int64 object_store_id,
348 int64 index_id,
349 const base::string16& name,
350 const IndexedDBKeyPath& key_path,
351 bool unique,
352 bool multi_entry) {
353 IDB_TRACE("IndexedDBDatabase::CreateIndex");
354 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
355 if (!transaction)
356 return;
357 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
359 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id))
360 return;
361 const IndexedDBIndexMetadata index_metadata(
362 name, index_id, key_path, unique, multi_entry);
364 transaction->ScheduleTask(
365 base::Bind(&IndexedDBDatabase::CreateIndexOperation,
366 this,
367 object_store_id,
368 index_metadata),
369 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation,
370 this,
371 object_store_id,
372 index_id));
374 AddIndex(object_store_id, index_metadata, index_id);
377 void IndexedDBDatabase::CreateIndexOperation(
378 int64 object_store_id,
379 const IndexedDBIndexMetadata& index_metadata,
380 IndexedDBTransaction* transaction) {
381 IDB_TRACE("IndexedDBDatabase::CreateIndexOperation");
382 if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(),
383 transaction->database()->id(),
384 object_store_id,
385 index_metadata.id,
386 index_metadata.name,
387 index_metadata.key_path,
388 index_metadata.unique,
389 index_metadata.multi_entry).ok()) {
390 base::string16 error_string =
391 ASCIIToUTF16("Internal error creating index '") +
392 index_metadata.name + ASCIIToUTF16("'.");
393 transaction->Abort(IndexedDBDatabaseError(
394 blink::WebIDBDatabaseExceptionUnknownError, error_string));
395 return;
399 void IndexedDBDatabase::CreateIndexAbortOperation(
400 int64 object_store_id,
401 int64 index_id,
402 IndexedDBTransaction* transaction) {
403 IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation");
404 DCHECK(!transaction);
405 RemoveIndex(object_store_id, index_id);
408 void IndexedDBDatabase::DeleteIndex(int64 transaction_id,
409 int64 object_store_id,
410 int64 index_id) {
411 IDB_TRACE("IndexedDBDatabase::DeleteIndex");
412 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
413 if (!transaction)
414 return;
415 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
417 if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id))
418 return;
419 const IndexedDBIndexMetadata& index_metadata =
420 metadata_.object_stores[object_store_id].indexes[index_id];
422 transaction->ScheduleTask(
423 base::Bind(&IndexedDBDatabase::DeleteIndexOperation,
424 this,
425 object_store_id,
426 index_metadata),
427 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation,
428 this,
429 object_store_id,
430 index_metadata));
432 RemoveIndex(object_store_id, index_id);
435 void IndexedDBDatabase::DeleteIndexOperation(
436 int64 object_store_id,
437 const IndexedDBIndexMetadata& index_metadata,
438 IndexedDBTransaction* transaction) {
439 IDB_TRACE("IndexedDBDatabase::DeleteIndexOperation");
440 leveldb::Status s =
441 backing_store_->DeleteIndex(transaction->BackingStoreTransaction(),
442 transaction->database()->id(),
443 object_store_id,
444 index_metadata.id);
445 if (!s.ok()) {
446 base::string16 error_string =
447 ASCIIToUTF16("Internal error deleting index '") +
448 index_metadata.name + ASCIIToUTF16("'.");
449 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
450 error_string);
451 transaction->Abort(error);
452 if (s.IsCorruption())
453 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
454 error);
458 void IndexedDBDatabase::DeleteIndexAbortOperation(
459 int64 object_store_id,
460 const IndexedDBIndexMetadata& index_metadata,
461 IndexedDBTransaction* transaction) {
462 IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation");
463 DCHECK(!transaction);
464 AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId);
467 void IndexedDBDatabase::Commit(int64 transaction_id) {
468 // The frontend suggests that we commit, but we may have previously initiated
469 // an abort, and so have disposed of the transaction. on_abort has already
470 // been dispatched to the frontend, so it will find out about that
471 // asynchronously.
472 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
473 if (transaction)
474 transaction->Commit();
477 void IndexedDBDatabase::Abort(int64 transaction_id) {
478 // If the transaction is unknown, then it has already been aborted by the
479 // backend before this call so it is safe to ignore it.
480 IDB_TRACE("IndexedDBDatabase::Abort");
481 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
482 if (transaction)
483 transaction->Abort();
486 void IndexedDBDatabase::Abort(int64 transaction_id,
487 const IndexedDBDatabaseError& error) {
488 IDB_TRACE("IndexedDBDatabase::Abort");
489 // If the transaction is unknown, then it has already been aborted by the
490 // backend before this call so it is safe to ignore it.
491 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
492 if (transaction)
493 transaction->Abort(error);
496 void IndexedDBDatabase::Get(int64 transaction_id,
497 int64 object_store_id,
498 int64 index_id,
499 scoped_ptr<IndexedDBKeyRange> key_range,
500 bool key_only,
501 scoped_refptr<IndexedDBCallbacks> callbacks) {
502 IDB_TRACE("IndexedDBDatabase::Get");
503 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
504 if (!transaction)
505 return;
507 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
508 return;
510 transaction->ScheduleTask(base::Bind(
511 &IndexedDBDatabase::GetOperation,
512 this,
513 object_store_id,
514 index_id,
515 Passed(&key_range),
516 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE,
517 callbacks));
520 void IndexedDBDatabase::GetOperation(
521 int64 object_store_id,
522 int64 index_id,
523 scoped_ptr<IndexedDBKeyRange> key_range,
524 indexed_db::CursorType cursor_type,
525 scoped_refptr<IndexedDBCallbacks> callbacks,
526 IndexedDBTransaction* transaction) {
527 IDB_TRACE("IndexedDBDatabase::GetOperation");
529 DCHECK(metadata_.object_stores.find(object_store_id) !=
530 metadata_.object_stores.end());
531 const IndexedDBObjectStoreMetadata& object_store_metadata =
532 metadata_.object_stores[object_store_id];
534 const IndexedDBKey* key;
536 leveldb::Status s;
537 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
538 if (key_range->IsOnlyKey()) {
539 key = &key_range->lower();
540 } else {
541 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
542 DCHECK_NE(cursor_type, indexed_db::CURSOR_KEY_ONLY);
543 // ObjectStore Retrieval Operation
544 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
545 transaction->BackingStoreTransaction(),
546 id(),
547 object_store_id,
548 *key_range,
549 indexed_db::CURSOR_NEXT,
550 &s);
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(),
555 id(),
556 object_store_id,
557 index_id,
558 *key_range,
559 indexed_db::CURSOR_NEXT,
560 &s);
561 } else {
562 // Index Referenced Value Retrieval Operation
563 backing_store_cursor = backing_store_->OpenIndexCursor(
564 transaction->BackingStoreTransaction(),
565 id(),
566 object_store_id,
567 index_id,
568 *key_range,
569 indexed_db::CURSOR_NEXT,
570 &s);
573 if (!s.ok()) {
574 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
575 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
576 "Internal error deleting data in range");
577 if (s.IsCorruption()) {
578 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
579 error);
583 if (!backing_store_cursor) {
584 callbacks->OnSuccess();
585 return;
588 key = &backing_store_cursor->key();
591 scoped_ptr<IndexedDBKey> primary_key;
592 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
593 // Object Store Retrieval Operation
594 IndexedDBValue value;
595 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
596 id(),
597 object_store_id,
598 *key,
599 &value);
600 if (!s.ok()) {
601 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
602 "Internal error in GetRecord.");
603 callbacks->OnError(error);
605 if (s.IsCorruption())
606 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
607 error);
608 return;
611 if (value.empty()) {
612 callbacks->OnSuccess();
613 return;
616 if (object_store_metadata.auto_increment &&
617 !object_store_metadata.key_path.IsNull()) {
618 callbacks->OnSuccess(&value, *key, object_store_metadata.key_path);
619 return;
622 callbacks->OnSuccess(&value);
623 return;
626 // From here we are dealing only with indexes.
627 s = backing_store_->GetPrimaryKeyViaIndex(
628 transaction->BackingStoreTransaction(),
629 id(),
630 object_store_id,
631 index_id,
632 *key,
633 &primary_key);
634 if (!s.ok()) {
635 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
636 "Internal error in GetPrimaryKeyViaIndex.");
637 callbacks->OnError(error);
638 if (s.IsCorruption())
639 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
640 error);
641 return;
643 if (!primary_key) {
644 callbacks->OnSuccess();
645 return;
647 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) {
648 // Index Value Retrieval Operation
649 callbacks->OnSuccess(*primary_key);
650 return;
653 // Index Referenced Value Retrieval Operation
654 IndexedDBValue value;
655 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(),
656 id(),
657 object_store_id,
658 *primary_key,
659 &value);
660 if (!s.ok()) {
661 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
662 "Internal error in GetRecord.");
663 callbacks->OnError(error);
664 if (s.IsCorruption())
665 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
666 error);
667 return;
670 if (value.empty()) {
671 callbacks->OnSuccess();
672 return;
674 if (object_store_metadata.auto_increment &&
675 !object_store_metadata.key_path.IsNull()) {
676 callbacks->OnSuccess(&value, *primary_key, object_store_metadata.key_path);
677 return;
679 callbacks->OnSuccess(&value);
682 static scoped_ptr<IndexedDBKey> GenerateKey(
683 IndexedDBBackingStore* backing_store,
684 IndexedDBTransaction* transaction,
685 int64 database_id,
686 int64 object_store_id) {
687 const int64 max_generator_value =
688 9007199254740992LL; // Maximum integer storable as ECMAScript number.
689 int64 current_number;
690 leveldb::Status s = backing_store->GetKeyGeneratorCurrentNumber(
691 transaction->BackingStoreTransaction(),
692 database_id,
693 object_store_id,
694 &current_number);
695 if (!s.ok()) {
696 LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber";
697 return make_scoped_ptr(new IndexedDBKey());
699 if (current_number < 0 || current_number > max_generator_value)
700 return make_scoped_ptr(new IndexedDBKey());
702 return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber));
705 static leveldb::Status UpdateKeyGenerator(IndexedDBBackingStore* backing_store,
706 IndexedDBTransaction* transaction,
707 int64 database_id,
708 int64 object_store_id,
709 const IndexedDBKey& key,
710 bool check_current) {
711 DCHECK_EQ(WebIDBKeyTypeNumber, key.type());
712 return backing_store->MaybeUpdateKeyGeneratorCurrentNumber(
713 transaction->BackingStoreTransaction(),
714 database_id,
715 object_store_id,
716 static_cast<int64>(floor(key.number())) + 1,
717 check_current);
720 struct IndexedDBDatabase::PutOperationParams {
721 PutOperationParams() {}
722 int64 object_store_id;
723 IndexedDBValue value;
724 ScopedVector<webkit_blob::BlobDataHandle> handles;
725 scoped_ptr<IndexedDBKey> key;
726 IndexedDBDatabase::PutMode put_mode;
727 scoped_refptr<IndexedDBCallbacks> callbacks;
728 std::vector<IndexKeys> index_keys;
730 private:
731 DISALLOW_COPY_AND_ASSIGN(PutOperationParams);
734 void IndexedDBDatabase::Put(int64 transaction_id,
735 int64 object_store_id,
736 IndexedDBValue* value,
737 ScopedVector<webkit_blob::BlobDataHandle>* handles,
738 scoped_ptr<IndexedDBKey> key,
739 PutMode put_mode,
740 scoped_refptr<IndexedDBCallbacks> callbacks,
741 const std::vector<IndexKeys>& index_keys) {
742 IDB_TRACE("IndexedDBDatabase::Put");
743 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
744 if (!transaction)
745 return;
746 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
748 if (!ValidateObjectStoreId(object_store_id))
749 return;
751 DCHECK(key);
752 DCHECK(value);
753 scoped_ptr<PutOperationParams> params(new PutOperationParams());
754 params->object_store_id = object_store_id;
755 params->value.swap(*value);
756 params->handles.swap(*handles);
757 params->key = key.Pass();
758 params->put_mode = put_mode;
759 params->callbacks = callbacks;
760 params->index_keys = index_keys;
761 transaction->ScheduleTask(base::Bind(
762 &IndexedDBDatabase::PutOperation, this, base::Passed(&params)));
765 void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params,
766 IndexedDBTransaction* transaction) {
767 IDB_TRACE("IndexedDBDatabase::PutOperation");
768 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
769 bool key_was_generated = false;
771 DCHECK(metadata_.object_stores.find(params->object_store_id) !=
772 metadata_.object_stores.end());
773 const IndexedDBObjectStoreMetadata& object_store =
774 metadata_.object_stores[params->object_store_id];
775 DCHECK(object_store.auto_increment || params->key->IsValid());
777 scoped_ptr<IndexedDBKey> key;
778 if (params->put_mode != IndexedDBDatabase::CURSOR_UPDATE &&
779 object_store.auto_increment && !params->key->IsValid()) {
780 scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey(
781 backing_store_.get(), transaction, id(), params->object_store_id);
782 key_was_generated = true;
783 if (!auto_inc_key->IsValid()) {
784 params->callbacks->OnError(
785 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
786 "Maximum key generator value reached."));
787 return;
789 key = auto_inc_key.Pass();
790 } else {
791 key = params->key.Pass();
794 DCHECK(key->IsValid());
796 IndexedDBBackingStore::RecordIdentifier record_identifier;
797 if (params->put_mode == IndexedDBDatabase::ADD_ONLY) {
798 bool found = false;
799 leveldb::Status s = backing_store_->KeyExistsInObjectStore(
800 transaction->BackingStoreTransaction(),
801 id(),
802 params->object_store_id,
803 *key,
804 &record_identifier,
805 &found);
806 if (!s.ok()) {
807 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
808 "Internal error checking key existence.");
809 params->callbacks->OnError(error);
810 if (s.IsCorruption())
811 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
812 error);
813 return;
815 if (found) {
816 params->callbacks->OnError(
817 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError,
818 "Key already exists in the object store."));
819 return;
823 ScopedVector<IndexWriter> index_writers;
824 base::string16 error_message;
825 bool obeys_constraints = false;
826 bool backing_store_success = MakeIndexWriters(transaction,
827 backing_store_.get(),
828 id(),
829 object_store,
830 *key,
831 key_was_generated,
832 params->index_keys,
833 &index_writers,
834 &error_message,
835 &obeys_constraints);
836 if (!backing_store_success) {
837 params->callbacks->OnError(IndexedDBDatabaseError(
838 blink::WebIDBDatabaseExceptionUnknownError,
839 "Internal error: backing store error updating index keys."));
840 return;
842 if (!obeys_constraints) {
843 params->callbacks->OnError(IndexedDBDatabaseError(
844 blink::WebIDBDatabaseExceptionConstraintError, error_message));
845 return;
848 // Before this point, don't do any mutation. After this point, rollback the
849 // transaction in case of error.
850 leveldb::Status s =
851 backing_store_->PutRecord(transaction->BackingStoreTransaction(),
852 id(),
853 params->object_store_id,
854 *key,
855 params->value,
856 &params->handles,
857 &record_identifier);
858 if (!s.ok()) {
859 IndexedDBDatabaseError error(
860 blink::WebIDBDatabaseExceptionUnknownError,
861 "Internal error: backing store error performing put/add.");
862 params->callbacks->OnError(error);
863 if (s.IsCorruption())
864 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
865 error);
866 return;
869 for (size_t i = 0; i < index_writers.size(); ++i) {
870 IndexWriter* index_writer = index_writers[i];
871 index_writer->WriteIndexKeys(record_identifier,
872 backing_store_.get(),
873 transaction->BackingStoreTransaction(),
874 id(),
875 params->object_store_id);
878 if (object_store.auto_increment &&
879 params->put_mode != IndexedDBDatabase::CURSOR_UPDATE &&
880 key->type() == WebIDBKeyTypeNumber) {
881 leveldb::Status s = UpdateKeyGenerator(backing_store_.get(),
882 transaction,
883 id(),
884 params->object_store_id,
885 *key,
886 !key_was_generated);
887 if (!s.ok()) {
888 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
889 "Internal error updating key generator.");
890 params->callbacks->OnError(error);
891 if (s.IsCorruption())
892 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
893 error);
894 return;
898 params->callbacks->OnSuccess(*key);
901 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id,
902 int64 object_store_id,
903 scoped_ptr<IndexedDBKey> primary_key,
904 const std::vector<IndexKeys>& index_keys) {
905 IDB_TRACE("IndexedDBDatabase::SetIndexKeys");
906 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
907 if (!transaction)
908 return;
909 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
911 // TODO(alecflett): This method could be asynchronous, but we need to
912 // evaluate if it's worth the extra complexity.
913 IndexedDBBackingStore::RecordIdentifier record_identifier;
914 bool found = false;
915 leveldb::Status s = backing_store_->KeyExistsInObjectStore(
916 transaction->BackingStoreTransaction(),
917 metadata_.id,
918 object_store_id,
919 *primary_key,
920 &record_identifier,
921 &found);
922 if (!s.ok()) {
923 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
924 "Internal error setting index keys.");
925 transaction->Abort(error);
926 if (s.IsCorruption())
927 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
928 error);
929 return;
931 if (!found) {
932 transaction->Abort(IndexedDBDatabaseError(
933 blink::WebIDBDatabaseExceptionUnknownError,
934 "Internal error setting index keys for object store."));
935 return;
938 ScopedVector<IndexWriter> index_writers;
939 base::string16 error_message;
940 bool obeys_constraints = false;
941 DCHECK(metadata_.object_stores.find(object_store_id) !=
942 metadata_.object_stores.end());
943 const IndexedDBObjectStoreMetadata& object_store_metadata =
944 metadata_.object_stores[object_store_id];
945 bool backing_store_success = MakeIndexWriters(transaction,
946 backing_store_,
947 id(),
948 object_store_metadata,
949 *primary_key,
950 false,
951 index_keys,
952 &index_writers,
953 &error_message,
954 &obeys_constraints);
955 if (!backing_store_success) {
956 transaction->Abort(IndexedDBDatabaseError(
957 blink::WebIDBDatabaseExceptionUnknownError,
958 "Internal error: backing store error updating index keys."));
959 return;
961 if (!obeys_constraints) {
962 transaction->Abort(IndexedDBDatabaseError(
963 blink::WebIDBDatabaseExceptionConstraintError, error_message));
964 return;
967 for (size_t i = 0; i < index_writers.size(); ++i) {
968 IndexWriter* index_writer = index_writers[i];
969 index_writer->WriteIndexKeys(record_identifier,
970 backing_store_,
971 transaction->BackingStoreTransaction(),
972 id(),
973 object_store_id);
977 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id,
978 int64,
979 const std::vector<int64>& index_ids) {
980 IDB_TRACE("IndexedDBDatabase::SetIndexesReady");
981 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
982 if (!transaction)
983 return;
984 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE);
986 transaction->ScheduleTask(
987 IndexedDBDatabase::PREEMPTIVE_TASK,
988 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation,
989 this,
990 index_ids.size()));
993 void IndexedDBDatabase::SetIndexesReadyOperation(
994 size_t index_count,
995 IndexedDBTransaction* transaction) {
996 IDB_TRACE("IndexedDBDatabase::SetIndexesReadyOperation");
997 for (size_t i = 0; i < index_count; ++i)
998 transaction->DidCompletePreemptiveEvent();
1001 struct IndexedDBDatabase::OpenCursorOperationParams {
1002 OpenCursorOperationParams() {}
1003 int64 object_store_id;
1004 int64 index_id;
1005 scoped_ptr<IndexedDBKeyRange> key_range;
1006 indexed_db::CursorDirection direction;
1007 indexed_db::CursorType cursor_type;
1008 IndexedDBDatabase::TaskType task_type;
1009 scoped_refptr<IndexedDBCallbacks> callbacks;
1011 private:
1012 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams);
1015 void IndexedDBDatabase::OpenCursor(
1016 int64 transaction_id,
1017 int64 object_store_id,
1018 int64 index_id,
1019 scoped_ptr<IndexedDBKeyRange> key_range,
1020 indexed_db::CursorDirection direction,
1021 bool key_only,
1022 TaskType task_type,
1023 scoped_refptr<IndexedDBCallbacks> callbacks) {
1024 IDB_TRACE("IndexedDBDatabase::OpenCursor");
1025 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1026 if (!transaction)
1027 return;
1029 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1030 return;
1032 scoped_ptr<OpenCursorOperationParams> params(new OpenCursorOperationParams());
1033 params->object_store_id = object_store_id;
1034 params->index_id = index_id;
1035 params->key_range = key_range.Pass();
1036 params->direction = direction;
1037 params->cursor_type =
1038 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE;
1039 params->task_type = task_type;
1040 params->callbacks = callbacks;
1041 transaction->ScheduleTask(base::Bind(
1042 &IndexedDBDatabase::OpenCursorOperation, this, base::Passed(&params)));
1045 void IndexedDBDatabase::OpenCursorOperation(
1046 scoped_ptr<OpenCursorOperationParams> params,
1047 IndexedDBTransaction* transaction) {
1048 IDB_TRACE("IndexedDBDatabase::OpenCursorOperation");
1050 // The frontend has begun indexing, so this pauses the transaction
1051 // until the indexing is complete. This can't happen any earlier
1052 // because we don't want to switch to early mode in case multiple
1053 // indexes are being created in a row, with Put()'s in between.
1054 if (params->task_type == IndexedDBDatabase::PREEMPTIVE_TASK)
1055 transaction->AddPreemptiveEvent();
1057 leveldb::Status s;
1058 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1059 if (params->index_id == IndexedDBIndexMetadata::kInvalidId) {
1060 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1061 DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK);
1062 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1063 transaction->BackingStoreTransaction(),
1064 id(),
1065 params->object_store_id,
1066 *params->key_range,
1067 params->direction,
1068 &s);
1069 } else {
1070 backing_store_cursor = backing_store_->OpenObjectStoreCursor(
1071 transaction->BackingStoreTransaction(),
1072 id(),
1073 params->object_store_id,
1074 *params->key_range,
1075 params->direction,
1076 &s);
1078 } else {
1079 DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK);
1080 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) {
1081 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1082 transaction->BackingStoreTransaction(),
1083 id(),
1084 params->object_store_id,
1085 params->index_id,
1086 *params->key_range,
1087 params->direction,
1088 &s);
1089 } else {
1090 backing_store_cursor = backing_store_->OpenIndexCursor(
1091 transaction->BackingStoreTransaction(),
1092 id(),
1093 params->object_store_id,
1094 params->index_id,
1095 *params->key_range,
1096 params->direction,
1097 &s);
1101 if (!s.ok()) {
1102 DLOG(ERROR) << "Unable to open cursor operation: " << s.ToString();
1103 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1104 "Internal error opening cursor operation");
1105 if (s.IsCorruption()) {
1106 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1107 error);
1111 if (!backing_store_cursor) {
1112 // Why is Success being called?
1113 params->callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL));
1114 return;
1117 scoped_refptr<IndexedDBCursor> cursor =
1118 new IndexedDBCursor(backing_store_cursor.Pass(),
1119 params->cursor_type,
1120 params->task_type,
1121 transaction);
1122 params->callbacks->OnSuccess(
1123 cursor, cursor->key(), cursor->primary_key(), cursor->Value());
1126 void IndexedDBDatabase::Count(int64 transaction_id,
1127 int64 object_store_id,
1128 int64 index_id,
1129 scoped_ptr<IndexedDBKeyRange> key_range,
1130 scoped_refptr<IndexedDBCallbacks> callbacks) {
1131 IDB_TRACE("IndexedDBDatabase::Count");
1132 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1133 if (!transaction)
1134 return;
1136 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id))
1137 return;
1139 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation,
1140 this,
1141 object_store_id,
1142 index_id,
1143 base::Passed(&key_range),
1144 callbacks));
1147 void IndexedDBDatabase::CountOperation(
1148 int64 object_store_id,
1149 int64 index_id,
1150 scoped_ptr<IndexedDBKeyRange> key_range,
1151 scoped_refptr<IndexedDBCallbacks> callbacks,
1152 IndexedDBTransaction* transaction) {
1153 IDB_TRACE("IndexedDBDatabase::CountOperation");
1154 uint32 count = 0;
1155 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor;
1157 leveldb::Status s;
1158 if (index_id == IndexedDBIndexMetadata::kInvalidId) {
1159 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor(
1160 transaction->BackingStoreTransaction(),
1161 id(),
1162 object_store_id,
1163 *key_range,
1164 indexed_db::CURSOR_NEXT,
1165 &s);
1166 } else {
1167 backing_store_cursor = backing_store_->OpenIndexKeyCursor(
1168 transaction->BackingStoreTransaction(),
1169 id(),
1170 object_store_id,
1171 index_id,
1172 *key_range,
1173 indexed_db::CURSOR_NEXT,
1174 &s);
1176 if (!s.ok()) {
1177 DLOG(ERROR) << "Unable perform count operation: " << s.ToString();
1178 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1179 "Internal error performing count operation");
1180 if (s.IsCorruption()) {
1181 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1182 error);
1185 if (!backing_store_cursor) {
1186 callbacks->OnSuccess(count);
1187 return;
1190 do {
1191 ++count;
1192 } while (backing_store_cursor->Continue(&s));
1194 // TODO(cmumford): Check for database corruption.
1196 callbacks->OnSuccess(count);
1199 void IndexedDBDatabase::DeleteRange(
1200 int64 transaction_id,
1201 int64 object_store_id,
1202 scoped_ptr<IndexedDBKeyRange> key_range,
1203 scoped_refptr<IndexedDBCallbacks> callbacks) {
1204 IDB_TRACE("IndexedDBDatabase::DeleteRange");
1205 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1206 if (!transaction)
1207 return;
1208 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
1210 if (!ValidateObjectStoreId(object_store_id))
1211 return;
1213 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation,
1214 this,
1215 object_store_id,
1216 base::Passed(&key_range),
1217 callbacks));
1220 void IndexedDBDatabase::DeleteRangeOperation(
1221 int64 object_store_id,
1222 scoped_ptr<IndexedDBKeyRange> key_range,
1223 scoped_refptr<IndexedDBCallbacks> callbacks,
1224 IndexedDBTransaction* transaction) {
1225 IDB_TRACE("IndexedDBDatabase::DeleteRangeOperation");
1226 leveldb::Status s;
1227 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor =
1228 backing_store_->OpenObjectStoreCursor(
1229 transaction->BackingStoreTransaction(),
1230 id(),
1231 object_store_id,
1232 *key_range,
1233 indexed_db::CURSOR_NEXT,
1234 &s);
1235 if (backing_store_cursor && s.ok()) {
1236 do {
1237 if (!backing_store_->DeleteRecord(
1238 transaction->BackingStoreTransaction(),
1239 id(),
1240 object_store_id,
1241 backing_store_cursor->record_identifier())
1242 .ok()) {
1243 callbacks->OnError(
1244 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1245 "Internal error deleting data in range"));
1246 return;
1248 } while (backing_store_cursor->Continue(&s));
1251 if (!s.ok()) {
1252 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1253 ASCIIToUTF16("Internal error deleting range"));
1254 transaction->Abort(error);
1255 if (s.IsCorruption()) {
1256 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1257 error);
1259 return;
1262 callbacks->OnSuccess();
1265 void IndexedDBDatabase::Clear(int64 transaction_id,
1266 int64 object_store_id,
1267 scoped_refptr<IndexedDBCallbacks> callbacks) {
1268 IDB_TRACE("IndexedDBDatabase::Clear");
1269 IndexedDBTransaction* transaction = GetTransaction(transaction_id);
1270 if (!transaction)
1271 return;
1272 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY);
1274 if (!ValidateObjectStoreId(object_store_id))
1275 return;
1277 transaction->ScheduleTask(base::Bind(
1278 &IndexedDBDatabase::ClearOperation, this, object_store_id, callbacks));
1281 void IndexedDBDatabase::ClearOperation(
1282 int64 object_store_id,
1283 scoped_refptr<IndexedDBCallbacks> callbacks,
1284 IndexedDBTransaction* transaction) {
1285 IDB_TRACE("IndexedDBDatabase::ObjectStoreClearOperation");
1286 leveldb::Status s = backing_store_->ClearObjectStore(
1287 transaction->BackingStoreTransaction(), id(), object_store_id);
1288 if (!s.ok()) {
1289 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1290 "Internal error clearing object store");
1291 callbacks->OnError(error);
1292 if (s.IsCorruption()) {
1293 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1294 error);
1296 return;
1298 callbacks->OnSuccess();
1301 void IndexedDBDatabase::DeleteObjectStoreOperation(
1302 const IndexedDBObjectStoreMetadata& object_store_metadata,
1303 IndexedDBTransaction* transaction) {
1304 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreOperation");
1305 leveldb::Status s =
1306 backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(),
1307 transaction->database()->id(),
1308 object_store_metadata.id);
1309 if (!s.ok()) {
1310 base::string16 error_string =
1311 ASCIIToUTF16("Internal error deleting object store '") +
1312 object_store_metadata.name + ASCIIToUTF16("'.");
1313 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1314 error_string);
1315 transaction->Abort(error);
1316 if (s.IsCorruption())
1317 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(),
1318 error);
1322 void IndexedDBDatabase::VersionChangeOperation(
1323 int64 version,
1324 scoped_refptr<IndexedDBCallbacks> callbacks,
1325 scoped_ptr<IndexedDBConnection> connection,
1326 IndexedDBTransaction* transaction) {
1327 IDB_TRACE("IndexedDBDatabase::VersionChangeOperation");
1328 int64 old_version = metadata_.int_version;
1329 DCHECK_GT(version, old_version);
1330 metadata_.int_version = version;
1331 if (!backing_store_->UpdateIDBDatabaseIntVersion(
1332 transaction->BackingStoreTransaction(),
1333 id(),
1334 metadata_.int_version)) {
1335 IndexedDBDatabaseError error(
1336 blink::WebIDBDatabaseExceptionUnknownError,
1337 ASCIIToUTF16(
1338 "Internal error writing data to stable storage when "
1339 "updating version."));
1340 callbacks->OnError(error);
1341 transaction->Abort(error);
1342 return;
1344 DCHECK(!pending_second_half_open_);
1345 pending_second_half_open_.reset(
1346 new PendingSuccessCall(callbacks, connection.get(), version));
1347 callbacks->OnUpgradeNeeded(old_version, connection.Pass(), metadata());
1350 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
1351 bool committed) {
1352 DCHECK(transactions_.find(transaction->id()) != transactions_.end());
1353 DCHECK_EQ(transactions_[transaction->id()], transaction);
1354 transactions_.erase(transaction->id());
1356 if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) {
1357 if (pending_second_half_open_) {
1358 if (committed) {
1359 DCHECK_EQ(pending_second_half_open_->version(), metadata_.int_version);
1360 DCHECK(metadata_.id != kInvalidId);
1362 // Connection was already minted for OnUpgradeNeeded callback.
1363 scoped_ptr<IndexedDBConnection> connection;
1364 pending_second_half_open_->callbacks()->OnSuccess(connection.Pass(),
1365 this->metadata());
1366 } else {
1367 pending_second_half_open_->callbacks()->OnError(
1368 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1369 "Version change transaction was aborted in "
1370 "upgradeneeded event handler."));
1372 pending_second_half_open_.reset();
1375 // Connection queue is now unblocked.
1376 ProcessPendingCalls();
1380 void IndexedDBDatabase::TransactionCommitFailed() {
1381 // Factory may be null in unit tests.
1382 if (!factory_)
1383 return;
1384 factory_->HandleBackingStoreFailure(backing_store_->origin_url());
1387 size_t IndexedDBDatabase::ConnectionCount() const {
1388 // This does not include pending open calls, as those should not block version
1389 // changes and deletes.
1390 return connections_.size();
1393 size_t IndexedDBDatabase::PendingOpenCount() const {
1394 return pending_open_calls_.size();
1397 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1398 return pending_run_version_change_transaction_call_ ? 1 : 0;
1401 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1402 return pending_second_half_open_ ? 1 : 0;
1405 size_t IndexedDBDatabase::PendingDeleteCount() const {
1406 return pending_delete_calls_.size();
1409 void IndexedDBDatabase::ProcessPendingCalls() {
1410 if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
1411 DCHECK(pending_run_version_change_transaction_call_->version() >
1412 metadata_.int_version);
1413 scoped_ptr<PendingUpgradeCall> pending_call =
1414 pending_run_version_change_transaction_call_.Pass();
1415 RunVersionChangeTransactionFinal(pending_call->callbacks(),
1416 pending_call->ReleaseConnection(),
1417 pending_call->transaction_id(),
1418 pending_call->version());
1419 DCHECK_EQ(1u, ConnectionCount());
1420 // Fall through would be a no-op, since transaction must complete
1421 // asynchronously.
1422 DCHECK(IsDeleteDatabaseBlocked());
1423 DCHECK(IsOpenConnectionBlocked());
1424 return;
1427 if (!IsDeleteDatabaseBlocked()) {
1428 PendingDeleteCallList pending_delete_calls;
1429 pending_delete_calls_.swap(pending_delete_calls);
1430 while (!pending_delete_calls.empty()) {
1431 // Only the first delete call will delete the database, but each must fire
1432 // callbacks.
1433 scoped_ptr<PendingDeleteCall> pending_delete_call(
1434 pending_delete_calls.front());
1435 pending_delete_calls.pop_front();
1436 DeleteDatabaseFinal(pending_delete_call->callbacks());
1438 // delete_database_final should never re-queue calls.
1439 DCHECK(pending_delete_calls_.empty());
1440 // Fall through when complete, as pending opens may be unblocked.
1443 if (!IsOpenConnectionBlocked()) {
1444 PendingOpenCallList pending_open_calls;
1445 pending_open_calls_.swap(pending_open_calls);
1446 while (!pending_open_calls.empty()) {
1447 OpenConnection(pending_open_calls.front());
1448 pending_open_calls.pop_front();
1453 void IndexedDBDatabase::CreateTransaction(
1454 int64 transaction_id,
1455 IndexedDBConnection* connection,
1456 const std::vector<int64>& object_store_ids,
1457 uint16 mode) {
1459 IDB_TRACE("IndexedDBDatabase::CreateTransaction");
1460 DCHECK(connections_.count(connection));
1461 DCHECK(transactions_.find(transaction_id) == transactions_.end());
1462 if (transactions_.find(transaction_id) != transactions_.end())
1463 return;
1465 // The transaction will add itself to this database's coordinator, which
1466 // manages the lifetime of the object.
1467 TransactionCreated(new IndexedDBTransaction(
1468 transaction_id,
1469 connection->callbacks(),
1470 std::set<int64>(object_store_ids.begin(), object_store_ids.end()),
1471 static_cast<indexed_db::TransactionMode>(mode),
1472 this,
1473 new IndexedDBBackingStore::Transaction(backing_store_)));
1476 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) {
1477 transactions_[transaction->id()] = transaction;
1480 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1481 return !pending_delete_calls_.empty() ||
1482 transaction_coordinator_.IsRunningVersionChangeTransaction() ||
1483 pending_run_version_change_transaction_call_;
1486 void IndexedDBDatabase::OpenConnection(
1487 const IndexedDBPendingConnection& connection) {
1488 DCHECK(backing_store_);
1490 // TODO(jsbell): Should have a priority queue so that higher version
1491 // requests are processed first. http://crbug.com/225850
1492 if (IsOpenConnectionBlocked()) {
1493 // The backing store only detects data loss when it is first opened. The
1494 // presence of existing connections means we didn't even check for data loss
1495 // so there'd better not be any.
1496 DCHECK_NE(blink::WebIDBDataLossTotal, connection.callbacks->data_loss());
1497 pending_open_calls_.push_back(connection);
1498 return;
1501 if (metadata_.id == kInvalidId) {
1502 // The database was deleted then immediately re-opened; OpenInternal()
1503 // recreates it in the backing store.
1504 if (OpenInternal().ok()) {
1505 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION,
1506 metadata_.int_version);
1507 } else {
1508 base::string16 message;
1509 if (connection.version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1510 message = ASCIIToUTF16(
1511 "Internal error opening database with no version specified.");
1512 } else {
1513 message =
1514 ASCIIToUTF16("Internal error opening database with version ") +
1515 Int64ToString16(connection.version);
1517 connection.callbacks->OnError(IndexedDBDatabaseError(
1518 blink::WebIDBDatabaseExceptionUnknownError, message));
1519 return;
1523 // We infer that the database didn't exist from its lack of either type of
1524 // version.
1525 bool is_new_database =
1526 metadata_.version == kNoStringVersion &&
1527 metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION;
1529 if (connection.version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) {
1530 // For unit tests only - skip upgrade steps. Calling from script with
1531 // DEFAULT_INT_VERSION throws exception.
1532 // TODO(jsbell): DCHECK that not in unit tests.
1533 DCHECK(is_new_database);
1534 connection.callbacks->OnSuccess(
1535 CreateConnection(connection.database_callbacks,
1536 connection.child_process_id),
1537 this->metadata());
1538 return;
1541 // We may need to change the version.
1542 int64 local_version = connection.version;
1543 if (local_version == IndexedDBDatabaseMetadata::NO_INT_VERSION) {
1544 if (!is_new_database) {
1545 connection.callbacks->OnSuccess(
1546 CreateConnection(connection.database_callbacks,
1547 connection.child_process_id),
1548 this->metadata());
1549 return;
1551 // Spec says: If no version is specified and no database exists, set
1552 // database version to 1.
1553 local_version = 1;
1556 if (local_version > metadata_.int_version) {
1557 RunVersionChangeTransaction(connection.callbacks,
1558 CreateConnection(connection.database_callbacks,
1559 connection.child_process_id),
1560 connection.transaction_id,
1561 local_version);
1562 return;
1564 if (local_version < metadata_.int_version) {
1565 connection.callbacks->OnError(IndexedDBDatabaseError(
1566 blink::WebIDBDatabaseExceptionVersionError,
1567 ASCIIToUTF16("The requested version (") +
1568 Int64ToString16(local_version) +
1569 ASCIIToUTF16(") is less than the existing version (") +
1570 Int64ToString16(metadata_.int_version) + ASCIIToUTF16(").")));
1571 return;
1573 DCHECK_EQ(local_version, metadata_.int_version);
1574 connection.callbacks->OnSuccess(
1575 CreateConnection(connection.database_callbacks,
1576 connection.child_process_id),
1577 this->metadata());
1580 void IndexedDBDatabase::RunVersionChangeTransaction(
1581 scoped_refptr<IndexedDBCallbacks> callbacks,
1582 scoped_ptr<IndexedDBConnection> connection,
1583 int64 transaction_id,
1584 int64 requested_version) {
1586 DCHECK(callbacks);
1587 DCHECK(connections_.count(connection.get()));
1588 if (ConnectionCount() > 1) {
1589 DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss());
1590 // Front end ensures the event is not fired at connections that have
1591 // close_pending set.
1592 for (ConnectionSet::const_iterator it = connections_.begin();
1593 it != connections_.end();
1594 ++it) {
1595 if (*it != connection.get()) {
1596 (*it)->callbacks()->OnVersionChange(metadata_.int_version,
1597 requested_version);
1600 // TODO(jsbell): Remove the call to OnBlocked and instead wait
1601 // until the frontend tells us that all the "versionchange" events
1602 // have been delivered. http://crbug.com/100123
1603 callbacks->OnBlocked(metadata_.int_version);
1605 DCHECK(!pending_run_version_change_transaction_call_);
1606 pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall(
1607 callbacks, connection.Pass(), transaction_id, requested_version));
1608 return;
1610 RunVersionChangeTransactionFinal(
1611 callbacks, connection.Pass(), transaction_id, requested_version);
1614 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1615 scoped_refptr<IndexedDBCallbacks> callbacks,
1616 scoped_ptr<IndexedDBConnection> connection,
1617 int64 transaction_id,
1618 int64 requested_version) {
1620 std::vector<int64> object_store_ids;
1621 CreateTransaction(transaction_id,
1622 connection.get(),
1623 object_store_ids,
1624 indexed_db::TRANSACTION_VERSION_CHANGE);
1626 transactions_[transaction_id]
1627 ->ScheduleTask(base::Bind(&IndexedDBDatabase::VersionChangeOperation,
1628 this,
1629 requested_version,
1630 callbacks,
1631 base::Passed(&connection)),
1632 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation,
1633 this,
1634 metadata_.version,
1635 metadata_.int_version));
1637 DCHECK(!pending_second_half_open_);
1640 void IndexedDBDatabase::DeleteDatabase(
1641 scoped_refptr<IndexedDBCallbacks> callbacks) {
1643 if (IsDeleteDatabaseBlocked()) {
1644 for (ConnectionSet::const_iterator it = connections_.begin();
1645 it != connections_.end();
1646 ++it) {
1647 // Front end ensures the event is not fired at connections that have
1648 // close_pending set.
1649 (*it)->callbacks()->OnVersionChange(
1650 metadata_.int_version, IndexedDBDatabaseMetadata::NO_INT_VERSION);
1652 // TODO(jsbell): Only fire OnBlocked if there are open
1653 // connections after the VersionChangeEvents are received, not
1654 // just set up to fire. http://crbug.com/100123
1655 callbacks->OnBlocked(metadata_.int_version);
1656 pending_delete_calls_.push_back(new PendingDeleteCall(callbacks));
1657 return;
1659 DeleteDatabaseFinal(callbacks);
1662 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1663 return !!ConnectionCount();
1666 void IndexedDBDatabase::DeleteDatabaseFinal(
1667 scoped_refptr<IndexedDBCallbacks> callbacks) {
1668 DCHECK(!IsDeleteDatabaseBlocked());
1669 DCHECK(backing_store_);
1670 if (!backing_store_->DeleteDatabase(metadata_.name).ok()) {
1671 callbacks->OnError(
1672 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1673 "Internal error deleting database."));
1674 return;
1676 int64 old_version = metadata_.int_version;
1677 metadata_.version = kNoStringVersion;
1678 metadata_.id = kInvalidId;
1679 metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION;
1680 metadata_.object_stores.clear();
1681 callbacks->OnSuccess(old_version);
1682 if (factory_)
1683 factory_->DatabaseDeleted(identifier_);
1686 void IndexedDBDatabase::ForceClose() {
1687 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1688 scoped_refptr<IndexedDBDatabase> protect(this);
1689 ConnectionSet::const_iterator it = connections_.begin();
1690 while (it != connections_.end()) {
1691 IndexedDBConnection* connection = *it++;
1692 connection->ForceClose();
1694 DCHECK(connections_.empty());
1697 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
1698 DCHECK(connections_.count(connection));
1699 DCHECK(connection->IsConnected());
1700 DCHECK(connection->database() == this);
1702 IDB_TRACE("IndexedDBDatabase::Close");
1703 // Abort outstanding transactions from the closing connection. This
1704 // can not happen if the close is requested by the connection itself
1705 // as the front-end defers the close until all transactions are
1706 // complete, but can occur on process termination or forced close.
1708 TransactionMap transactions(transactions_);
1709 for (TransactionMap::const_iterator it = transactions.begin(),
1710 end = transactions.end();
1711 it != end;
1712 ++it) {
1713 if (it->second->connection() == connection->callbacks())
1714 it->second->Abort(
1715 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1716 "Connection is closing."));
1720 connections_.erase(connection);
1721 if (pending_second_half_open_ &&
1722 pending_second_half_open_->connection() == connection) {
1723 pending_second_half_open_->callbacks()->OnError(
1724 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1725 "The connection was closed."));
1726 pending_second_half_open_.reset();
1729 ProcessPendingCalls();
1731 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1732 if (!ConnectionCount() && !pending_open_calls_.size() &&
1733 !pending_delete_calls_.size()) {
1734 DCHECK(transactions_.empty());
1736 const GURL origin_url = backing_store_->origin_url();
1737 backing_store_ = NULL;
1739 // factory_ should only be null in unit tests.
1740 // TODO(jsbell): DCHECK(factory_ || !in_unit_tests) - somehow.
1741 if (factory_) {
1742 factory_->ReleaseDatabase(identifier_, forced);
1743 factory_ = NULL;
1748 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1749 int64 object_store_id,
1750 IndexedDBTransaction* transaction) {
1751 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
1752 DCHECK(!transaction);
1753 RemoveObjectStore(object_store_id);
1756 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1757 const IndexedDBObjectStoreMetadata& object_store_metadata,
1758 IndexedDBTransaction* transaction) {
1759 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation");
1760 DCHECK(!transaction);
1761 AddObjectStore(object_store_metadata,
1762 IndexedDBObjectStoreMetadata::kInvalidId);
1765 void IndexedDBDatabase::VersionChangeAbortOperation(
1766 const base::string16& previous_version,
1767 int64 previous_int_version,
1768 IndexedDBTransaction* transaction) {
1769 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
1770 DCHECK(!transaction);
1771 metadata_.version = previous_version;
1772 metadata_.int_version = previous_int_version;
1775 } // namespace content