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/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 "storage/browser/blob/blob_data_handle.h"
30 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
31 #include "third_party/leveldatabase/env_chromium.h"
33 using base::ASCIIToUTF16
;
34 using base::Int64ToString16
;
35 using blink::WebIDBKeyTypeNumber
;
39 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
40 // in-progress connection.
41 class IndexedDBDatabase::PendingUpgradeCall
{
43 PendingUpgradeCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
,
44 scoped_ptr
<IndexedDBConnection
> connection
,
47 : callbacks_(callbacks
),
48 connection_(connection
.Pass()),
50 transaction_id_(transaction_id
) {}
51 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
52 // Takes ownership of the connection object.
53 scoped_ptr
<IndexedDBConnection
> ReleaseConnection() WARN_UNUSED_RESULT
{
54 return connection_
.Pass();
56 int64
version() const { return version_
; }
57 int64
transaction_id() const { return transaction_id_
; }
60 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
61 scoped_ptr
<IndexedDBConnection
> connection_
;
63 const int64 transaction_id_
;
66 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
67 // owned elsewhere, but we need to cancel the success call if that connection
68 // closes before it is sent.
69 class IndexedDBDatabase::PendingSuccessCall
{
71 PendingSuccessCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
,
72 IndexedDBConnection
* connection
,
74 : callbacks_(callbacks
), connection_(connection
), version_(version
) {}
75 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
76 IndexedDBConnection
* connection() const { return connection_
; }
77 int64
version() const { return version_
; }
80 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
81 IndexedDBConnection
* connection_
;
85 class IndexedDBDatabase::PendingDeleteCall
{
87 explicit PendingDeleteCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
)
88 : callbacks_(callbacks
) {}
89 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
92 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
95 scoped_refptr
<IndexedDBDatabase
> IndexedDBDatabase::Create(
96 const base::string16
& name
,
97 IndexedDBBackingStore
* backing_store
,
98 IndexedDBFactory
* factory
,
99 const Identifier
& unique_identifier
,
100 leveldb::Status
* s
) {
101 scoped_refptr
<IndexedDBDatabase
> database
=
102 new IndexedDBDatabase(name
, backing_store
, factory
, unique_identifier
);
103 *s
= database
->OpenInternal();
111 const base::string16::value_type kNoStringVersion
[] = {0};
114 IndexedDBDatabase::IndexedDBDatabase(const base::string16
& name
,
115 IndexedDBBackingStore
* backing_store
,
116 IndexedDBFactory
* factory
,
117 const Identifier
& unique_identifier
)
118 : backing_store_(backing_store
),
122 IndexedDBDatabaseMetadata::NO_INT_VERSION
,
124 identifier_(unique_identifier
),
126 DCHECK(factory
!= NULL
);
129 void IndexedDBDatabase::AddObjectStore(
130 const IndexedDBObjectStoreMetadata
& object_store
,
131 int64 new_max_object_store_id
) {
132 DCHECK(metadata_
.object_stores
.find(object_store
.id
) ==
133 metadata_
.object_stores
.end());
134 if (new_max_object_store_id
!= IndexedDBObjectStoreMetadata::kInvalidId
) {
135 DCHECK_LT(metadata_
.max_object_store_id
, new_max_object_store_id
);
136 metadata_
.max_object_store_id
= new_max_object_store_id
;
138 metadata_
.object_stores
[object_store
.id
] = object_store
;
141 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id
) {
142 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
143 metadata_
.object_stores
.end());
144 metadata_
.object_stores
.erase(object_store_id
);
147 void IndexedDBDatabase::AddIndex(int64 object_store_id
,
148 const IndexedDBIndexMetadata
& index
,
149 int64 new_max_index_id
) {
150 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
151 metadata_
.object_stores
.end());
152 IndexedDBObjectStoreMetadata object_store
=
153 metadata_
.object_stores
[object_store_id
];
155 DCHECK(object_store
.indexes
.find(index
.id
) == object_store
.indexes
.end());
156 object_store
.indexes
[index
.id
] = index
;
157 if (new_max_index_id
!= IndexedDBIndexMetadata::kInvalidId
) {
158 DCHECK_LT(object_store
.max_index_id
, new_max_index_id
);
159 object_store
.max_index_id
= new_max_index_id
;
161 metadata_
.object_stores
[object_store_id
] = object_store
;
164 void IndexedDBDatabase::RemoveIndex(int64 object_store_id
, int64 index_id
) {
165 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
166 metadata_
.object_stores
.end());
167 IndexedDBObjectStoreMetadata object_store
=
168 metadata_
.object_stores
[object_store_id
];
170 DCHECK(object_store
.indexes
.find(index_id
) != object_store
.indexes
.end());
171 object_store
.indexes
.erase(index_id
);
172 metadata_
.object_stores
[object_store_id
] = object_store
;
175 leveldb::Status
IndexedDBDatabase::OpenInternal() {
176 bool success
= false;
177 leveldb::Status s
= backing_store_
->GetIDBDatabaseMetaData(
178 metadata_
.name
, &metadata_
, &success
);
179 DCHECK(success
== (metadata_
.id
!= kInvalidId
)) << "success = " << success
180 << " id = " << metadata_
.id
;
184 return backing_store_
->GetObjectStores(metadata_
.id
,
185 &metadata_
.object_stores
);
187 return backing_store_
->CreateIDBDatabaseMetaData(
188 metadata_
.name
, metadata_
.version
, metadata_
.int_version
, &metadata_
.id
);
191 IndexedDBDatabase::~IndexedDBDatabase() {
192 DCHECK(transactions_
.empty());
193 DCHECK(pending_open_calls_
.empty());
194 DCHECK(pending_delete_calls_
.empty());
197 scoped_ptr
<IndexedDBConnection
> IndexedDBDatabase::CreateConnection(
198 scoped_refptr
<IndexedDBDatabaseCallbacks
> database_callbacks
,
199 int child_process_id
) {
200 scoped_ptr
<IndexedDBConnection
> connection(
201 new IndexedDBConnection(this, database_callbacks
));
202 connections_
.insert(connection
.get());
203 backing_store_
->GrantChildProcessPermissions(child_process_id
);
204 return connection
.Pass();
207 IndexedDBTransaction
* IndexedDBDatabase::GetTransaction(
208 int64 transaction_id
) const {
209 TransactionMap::const_iterator trans_iterator
=
210 transactions_
.find(transaction_id
);
211 if (trans_iterator
== transactions_
.end())
213 return trans_iterator
->second
;
216 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id
) const {
217 if (!ContainsKey(metadata_
.object_stores
, object_store_id
)) {
218 DLOG(ERROR
) << "Invalid object_store_id";
224 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id
,
225 int64 index_id
) const {
226 if (!ValidateObjectStoreId(object_store_id
))
228 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
229 metadata_
.object_stores
.find(object_store_id
)->second
;
230 if (!ContainsKey(object_store_metadata
.indexes
, index_id
)) {
231 DLOG(ERROR
) << "Invalid index_id";
237 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
238 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 (index_id
!= IndexedDBIndexMetadata::kInvalidId
&&
245 !ContainsKey(object_store_metadata
.indexes
, index_id
)) {
246 DLOG(ERROR
) << "Invalid index_id";
252 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
253 int64 object_store_id
,
254 int64 index_id
) const {
255 if (!ValidateObjectStoreId(object_store_id
))
257 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
258 metadata_
.object_stores
.find(object_store_id
)->second
;
259 if (ContainsKey(object_store_metadata
.indexes
, index_id
)) {
260 DLOG(ERROR
) << "Invalid index_id";
266 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id
,
267 int64 object_store_id
,
268 const base::string16
& name
,
269 const IndexedDBKeyPath
& key_path
,
270 bool auto_increment
) {
271 IDB_TRACE1("IndexedDBDatabase::CreateObjectStore", "txn.id", transaction_id
);
272 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
275 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
277 if (ContainsKey(metadata_
.object_stores
, object_store_id
)) {
278 DLOG(ERROR
) << "Invalid object_store_id";
282 // Store creation is done synchronously, as it may be followed by
283 // index creation (also sync) since preemptive OpenCursor/SetIndexKeys
285 IndexedDBObjectStoreMetadata
object_store_metadata(
290 IndexedDBDatabase::kMinimumIndexId
);
293 backing_store_
->CreateObjectStore(transaction
->BackingStoreTransaction(),
294 transaction
->database()->id(),
295 object_store_metadata
.id
,
296 object_store_metadata
.name
,
297 object_store_metadata
.key_path
,
298 object_store_metadata
.auto_increment
);
300 IndexedDBDatabaseError
error(
301 blink::WebIDBDatabaseExceptionUnknownError
,
302 ASCIIToUTF16("Internal error creating object store '") +
303 object_store_metadata
.name
+ ASCIIToUTF16("'."));
304 transaction
->Abort(error
);
305 if (s
.IsCorruption())
306 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
311 AddObjectStore(object_store_metadata
, object_store_id
);
312 transaction
->ScheduleAbortTask(
313 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation
,
318 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id
,
319 int64 object_store_id
) {
320 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStore", "txn.id", transaction_id
);
321 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
324 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
326 if (!ValidateObjectStoreId(object_store_id
))
329 transaction
->ScheduleTask(
330 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation
,
335 void IndexedDBDatabase::CreateIndex(int64 transaction_id
,
336 int64 object_store_id
,
338 const base::string16
& name
,
339 const IndexedDBKeyPath
& key_path
,
342 IDB_TRACE1("IndexedDBDatabase::CreateIndex", "txn.id", transaction_id
);
343 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
346 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
348 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id
, index_id
))
351 // Index creation is done synchronously since preemptive
352 // OpenCursor/SetIndexKeys may follow.
353 const IndexedDBIndexMetadata
index_metadata(
354 name
, index_id
, key_path
, unique
, multi_entry
);
356 if (!backing_store_
->CreateIndex(transaction
->BackingStoreTransaction(),
357 transaction
->database()->id(),
361 index_metadata
.key_path
,
362 index_metadata
.unique
,
363 index_metadata
.multi_entry
).ok()) {
364 base::string16 error_string
=
365 ASCIIToUTF16("Internal error creating index '") +
366 index_metadata
.name
+ ASCIIToUTF16("'.");
367 transaction
->Abort(IndexedDBDatabaseError(
368 blink::WebIDBDatabaseExceptionUnknownError
, error_string
));
372 AddIndex(object_store_id
, index_metadata
, index_id
);
373 transaction
->ScheduleAbortTask(
374 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation
,
380 void IndexedDBDatabase::CreateIndexAbortOperation(
381 int64 object_store_id
,
383 IndexedDBTransaction
* transaction
) {
384 IDB_TRACE1("IndexedDBDatabase::CreateIndexAbortOperation",
387 DCHECK(!transaction
);
388 RemoveIndex(object_store_id
, index_id
);
391 void IndexedDBDatabase::DeleteIndex(int64 transaction_id
,
392 int64 object_store_id
,
394 IDB_TRACE1("IndexedDBDatabase::DeleteIndex", "txn.id", transaction_id
);
395 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
398 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
400 if (!ValidateObjectStoreIdAndIndexId(object_store_id
, index_id
))
403 transaction
->ScheduleTask(
404 base::Bind(&IndexedDBDatabase::DeleteIndexOperation
,
410 void IndexedDBDatabase::DeleteIndexOperation(
411 int64 object_store_id
,
413 IndexedDBTransaction
* transaction
) {
415 "IndexedDBDatabase::DeleteIndexOperation", "txn.id", transaction
->id());
417 const IndexedDBIndexMetadata index_metadata
=
418 metadata_
.object_stores
[object_store_id
].indexes
[index_id
];
421 backing_store_
->DeleteIndex(transaction
->BackingStoreTransaction(),
422 transaction
->database()->id(),
426 base::string16 error_string
=
427 ASCIIToUTF16("Internal error deleting index '") +
428 index_metadata
.name
+ ASCIIToUTF16("'.");
429 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
431 transaction
->Abort(error
);
432 if (s
.IsCorruption())
433 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
438 RemoveIndex(object_store_id
, index_id
);
439 transaction
->ScheduleAbortTask(
440 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation
,
446 void IndexedDBDatabase::DeleteIndexAbortOperation(
447 int64 object_store_id
,
448 const IndexedDBIndexMetadata
& index_metadata
,
449 IndexedDBTransaction
* transaction
) {
450 DCHECK(!transaction
);
451 IDB_TRACE1("IndexedDBDatabase::DeleteIndexAbortOperation",
454 AddIndex(object_store_id
, index_metadata
, IndexedDBIndexMetadata::kInvalidId
);
457 void IndexedDBDatabase::Commit(int64 transaction_id
) {
458 // The frontend suggests that we commit, but we may have previously initiated
459 // an abort, and so have disposed of the transaction. on_abort has already
460 // been dispatched to the frontend, so it will find out about that
462 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
464 scoped_refptr
<IndexedDBFactory
> factory
= factory_
;
465 leveldb::Status s
= transaction
->Commit();
466 if (s
.IsCorruption()) {
467 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
468 "Internal error committing transaction.");
469 factory
->HandleBackingStoreCorruption(identifier_
.first
, error
);
474 void IndexedDBDatabase::Abort(int64 transaction_id
) {
475 // If the transaction is unknown, then it has already been aborted by the
476 // backend before this call so it is safe to ignore it.
477 IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction_id
);
478 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
480 transaction
->Abort();
483 void IndexedDBDatabase::Abort(int64 transaction_id
,
484 const IndexedDBDatabaseError
& error
) {
485 IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction_id
);
486 // If the transaction is unknown, then it has already been aborted by the
487 // backend before this call so it is safe to ignore it.
488 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
490 transaction
->Abort(error
);
493 void IndexedDBDatabase::Get(int64 transaction_id
,
494 int64 object_store_id
,
496 scoped_ptr
<IndexedDBKeyRange
> key_range
,
498 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
499 IDB_TRACE1("IndexedDBDatabase::Get", "txn.id", transaction_id
);
500 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
504 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
507 transaction
->ScheduleTask(base::Bind(
508 &IndexedDBDatabase::GetOperation
,
513 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
,
517 void IndexedDBDatabase::GetOperation(
518 int64 object_store_id
,
520 scoped_ptr
<IndexedDBKeyRange
> key_range
,
521 indexed_db::CursorType cursor_type
,
522 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
523 IndexedDBTransaction
* transaction
) {
524 IDB_TRACE1("IndexedDBDatabase::GetOperation", "txn.id", transaction
->id());
526 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
527 metadata_
.object_stores
.end());
528 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
529 metadata_
.object_stores
[object_store_id
];
531 const IndexedDBKey
* key
;
534 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
535 if (key_range
->IsOnlyKey()) {
536 key
= &key_range
->lower();
538 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
539 DCHECK_NE(cursor_type
, indexed_db::CURSOR_KEY_ONLY
);
540 // ObjectStore Retrieval Operation
541 backing_store_cursor
= backing_store_
->OpenObjectStoreCursor(
542 transaction
->BackingStoreTransaction(),
546 blink::WebIDBCursorDirectionNext
,
548 } else if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
549 // Index Value Retrieval Operation
550 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
551 transaction
->BackingStoreTransaction(),
556 blink::WebIDBCursorDirectionNext
,
559 // Index Referenced Value Retrieval Operation
560 backing_store_cursor
= backing_store_
->OpenIndexCursor(
561 transaction
->BackingStoreTransaction(),
566 blink::WebIDBCursorDirectionNext
,
571 DLOG(ERROR
) << "Unable to open cursor operation: " << s
.ToString();
572 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
573 "Internal error deleting data in range");
574 if (s
.IsCorruption()) {
575 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
580 if (!backing_store_cursor
) {
581 callbacks
->OnSuccess();
585 key
= &backing_store_cursor
->key();
588 scoped_ptr
<IndexedDBKey
> primary_key
;
589 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
590 // Object Store Retrieval Operation
591 IndexedDBValue value
;
592 s
= backing_store_
->GetRecord(transaction
->BackingStoreTransaction(),
598 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
599 "Internal error in GetRecord.");
600 callbacks
->OnError(error
);
602 if (s
.IsCorruption())
603 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
609 callbacks
->OnSuccess();
613 if (object_store_metadata
.auto_increment
&&
614 !object_store_metadata
.key_path
.IsNull()) {
615 callbacks
->OnSuccess(&value
, *key
, object_store_metadata
.key_path
);
619 callbacks
->OnSuccess(&value
);
623 // From here we are dealing only with indexes.
624 s
= backing_store_
->GetPrimaryKeyViaIndex(
625 transaction
->BackingStoreTransaction(),
632 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
633 "Internal error in GetPrimaryKeyViaIndex.");
634 callbacks
->OnError(error
);
635 if (s
.IsCorruption())
636 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
641 callbacks
->OnSuccess();
644 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
645 // Index Value Retrieval Operation
646 callbacks
->OnSuccess(*primary_key
);
650 // Index Referenced Value Retrieval Operation
651 IndexedDBValue value
;
652 s
= backing_store_
->GetRecord(transaction
->BackingStoreTransaction(),
658 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
659 "Internal error in GetRecord.");
660 callbacks
->OnError(error
);
661 if (s
.IsCorruption())
662 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
668 callbacks
->OnSuccess();
671 if (object_store_metadata
.auto_increment
&&
672 !object_store_metadata
.key_path
.IsNull()) {
673 callbacks
->OnSuccess(&value
, *primary_key
, object_store_metadata
.key_path
);
676 callbacks
->OnSuccess(&value
);
679 static scoped_ptr
<IndexedDBKey
> GenerateKey(
680 IndexedDBBackingStore
* backing_store
,
681 IndexedDBTransaction
* transaction
,
683 int64 object_store_id
) {
684 const int64 max_generator_value
=
685 9007199254740992LL; // Maximum integer storable as ECMAScript number.
686 int64 current_number
;
687 leveldb::Status s
= backing_store
->GetKeyGeneratorCurrentNumber(
688 transaction
->BackingStoreTransaction(),
693 LOG(ERROR
) << "Failed to GetKeyGeneratorCurrentNumber";
694 return make_scoped_ptr(new IndexedDBKey());
696 if (current_number
< 0 || current_number
> max_generator_value
)
697 return make_scoped_ptr(new IndexedDBKey());
699 return make_scoped_ptr(new IndexedDBKey(current_number
, WebIDBKeyTypeNumber
));
702 static leveldb::Status
UpdateKeyGenerator(IndexedDBBackingStore
* backing_store
,
703 IndexedDBTransaction
* transaction
,
705 int64 object_store_id
,
706 const IndexedDBKey
& key
,
707 bool check_current
) {
708 DCHECK_EQ(WebIDBKeyTypeNumber
, key
.type());
709 return backing_store
->MaybeUpdateKeyGeneratorCurrentNumber(
710 transaction
->BackingStoreTransaction(),
713 static_cast<int64
>(floor(key
.number())) + 1,
717 struct IndexedDBDatabase::PutOperationParams
{
718 PutOperationParams() {}
719 int64 object_store_id
;
720 IndexedDBValue value
;
721 ScopedVector
<storage::BlobDataHandle
> handles
;
722 scoped_ptr
<IndexedDBKey
> key
;
723 blink::WebIDBPutMode put_mode
;
724 scoped_refptr
<IndexedDBCallbacks
> callbacks
;
725 std::vector
<IndexKeys
> index_keys
;
728 DISALLOW_COPY_AND_ASSIGN(PutOperationParams
);
731 void IndexedDBDatabase::Put(int64 transaction_id
,
732 int64 object_store_id
,
733 IndexedDBValue
* value
,
734 ScopedVector
<storage::BlobDataHandle
>* handles
,
735 scoped_ptr
<IndexedDBKey
> key
,
736 blink::WebIDBPutMode put_mode
,
737 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
738 const std::vector
<IndexKeys
>& index_keys
) {
739 IDB_TRACE1("IndexedDBDatabase::Put", "txn.id", transaction_id
);
740 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
743 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
745 if (!ValidateObjectStoreId(object_store_id
))
750 scoped_ptr
<PutOperationParams
> params(new PutOperationParams());
751 params
->object_store_id
= object_store_id
;
752 params
->value
.swap(*value
);
753 params
->handles
.swap(*handles
);
754 params
->key
= key
.Pass();
755 params
->put_mode
= put_mode
;
756 params
->callbacks
= callbacks
;
757 params
->index_keys
= index_keys
;
758 transaction
->ScheduleTask(base::Bind(
759 &IndexedDBDatabase::PutOperation
, this, base::Passed(¶ms
)));
762 void IndexedDBDatabase::PutOperation(scoped_ptr
<PutOperationParams
> params
,
763 IndexedDBTransaction
* transaction
) {
764 IDB_TRACE1("IndexedDBDatabase::PutOperation", "txn.id", transaction
->id());
765 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
766 bool key_was_generated
= false;
768 DCHECK(metadata_
.object_stores
.find(params
->object_store_id
) !=
769 metadata_
.object_stores
.end());
770 const IndexedDBObjectStoreMetadata
& object_store
=
771 metadata_
.object_stores
[params
->object_store_id
];
772 DCHECK(object_store
.auto_increment
|| params
->key
->IsValid());
774 scoped_ptr
<IndexedDBKey
> key
;
775 if (params
->put_mode
!= blink::WebIDBPutModeCursorUpdate
&&
776 object_store
.auto_increment
&& !params
->key
->IsValid()) {
777 scoped_ptr
<IndexedDBKey
> auto_inc_key
= GenerateKey(
778 backing_store_
.get(), transaction
, id(), params
->object_store_id
);
779 key_was_generated
= true;
780 if (!auto_inc_key
->IsValid()) {
781 params
->callbacks
->OnError(
782 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError
,
783 "Maximum key generator value reached."));
786 key
= auto_inc_key
.Pass();
788 key
= params
->key
.Pass();
791 DCHECK(key
->IsValid());
793 IndexedDBBackingStore::RecordIdentifier record_identifier
;
794 if (params
->put_mode
== blink::WebIDBPutModeAddOnly
) {
796 leveldb::Status s
= backing_store_
->KeyExistsInObjectStore(
797 transaction
->BackingStoreTransaction(),
799 params
->object_store_id
,
804 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
805 "Internal error checking key existence.");
806 params
->callbacks
->OnError(error
);
807 if (s
.IsCorruption())
808 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
813 params
->callbacks
->OnError(
814 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError
,
815 "Key already exists in the object store."));
820 ScopedVector
<IndexWriter
> index_writers
;
821 base::string16 error_message
;
822 bool obeys_constraints
= false;
823 bool backing_store_success
= MakeIndexWriters(transaction
,
824 backing_store_
.get(),
833 if (!backing_store_success
) {
834 params
->callbacks
->OnError(IndexedDBDatabaseError(
835 blink::WebIDBDatabaseExceptionUnknownError
,
836 "Internal error: backing store error updating index keys."));
839 if (!obeys_constraints
) {
840 params
->callbacks
->OnError(IndexedDBDatabaseError(
841 blink::WebIDBDatabaseExceptionConstraintError
, error_message
));
845 // Before this point, don't do any mutation. After this point, rollback the
846 // transaction in case of error.
848 backing_store_
->PutRecord(transaction
->BackingStoreTransaction(),
850 params
->object_store_id
,
856 IndexedDBDatabaseError
error(
857 blink::WebIDBDatabaseExceptionUnknownError
,
858 "Internal error: backing store error performing put/add.");
859 params
->callbacks
->OnError(error
);
860 if (s
.IsCorruption())
861 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
866 for (size_t i
= 0; i
< index_writers
.size(); ++i
) {
867 IndexWriter
* index_writer
= index_writers
[i
];
868 index_writer
->WriteIndexKeys(record_identifier
,
869 backing_store_
.get(),
870 transaction
->BackingStoreTransaction(),
872 params
->object_store_id
);
875 if (object_store
.auto_increment
&&
876 params
->put_mode
!= blink::WebIDBPutModeCursorUpdate
&&
877 key
->type() == WebIDBKeyTypeNumber
) {
878 leveldb::Status s
= UpdateKeyGenerator(backing_store_
.get(),
881 params
->object_store_id
,
885 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
886 "Internal error updating key generator.");
887 params
->callbacks
->OnError(error
);
888 if (s
.IsCorruption())
889 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
895 params
->callbacks
->OnSuccess(*key
);
898 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id
,
899 int64 object_store_id
,
900 scoped_ptr
<IndexedDBKey
> primary_key
,
901 const std::vector
<IndexKeys
>& index_keys
) {
902 IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction_id
);
903 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
906 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
908 // TODO(alecflett): This method could be asynchronous, but we need to
909 // evaluate if it's worth the extra complexity.
910 IndexedDBBackingStore::RecordIdentifier record_identifier
;
912 leveldb::Status s
= backing_store_
->KeyExistsInObjectStore(
913 transaction
->BackingStoreTransaction(),
920 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
921 "Internal error setting index keys.");
922 transaction
->Abort(error
);
923 if (s
.IsCorruption())
924 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
929 transaction
->Abort(IndexedDBDatabaseError(
930 blink::WebIDBDatabaseExceptionUnknownError
,
931 "Internal error setting index keys for object store."));
935 ScopedVector
<IndexWriter
> index_writers
;
936 base::string16 error_message
;
937 bool obeys_constraints
= false;
938 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
939 metadata_
.object_stores
.end());
940 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
941 metadata_
.object_stores
[object_store_id
];
942 bool backing_store_success
= MakeIndexWriters(transaction
,
943 backing_store_
.get(),
945 object_store_metadata
,
952 if (!backing_store_success
) {
953 transaction
->Abort(IndexedDBDatabaseError(
954 blink::WebIDBDatabaseExceptionUnknownError
,
955 "Internal error: backing store error updating index keys."));
958 if (!obeys_constraints
) {
959 transaction
->Abort(IndexedDBDatabaseError(
960 blink::WebIDBDatabaseExceptionConstraintError
, error_message
));
964 for (size_t i
= 0; i
< index_writers
.size(); ++i
) {
965 IndexWriter
* index_writer
= index_writers
[i
];
966 index_writer
->WriteIndexKeys(record_identifier
,
967 backing_store_
.get(),
968 transaction
->BackingStoreTransaction(),
974 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id
,
976 const std::vector
<int64
>& index_ids
) {
977 IDB_TRACE1("IndexedDBDatabase::SetIndexesReady", "txn.id", transaction_id
);
978 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
981 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
983 transaction
->ScheduleTask(
984 blink::WebIDBTaskTypePreemptive
,
985 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation
,
990 void IndexedDBDatabase::SetIndexesReadyOperation(
992 IndexedDBTransaction
* transaction
) {
993 IDB_TRACE1("IndexedDBDatabase::SetIndexesReadyOperation",
996 for (size_t i
= 0; i
< index_count
; ++i
)
997 transaction
->DidCompletePreemptiveEvent();
1000 struct IndexedDBDatabase::OpenCursorOperationParams
{
1001 OpenCursorOperationParams() {}
1002 int64 object_store_id
;
1004 scoped_ptr
<IndexedDBKeyRange
> key_range
;
1005 blink::WebIDBCursorDirection direction
;
1006 indexed_db::CursorType cursor_type
;
1007 blink::WebIDBTaskType task_type
;
1008 scoped_refptr
<IndexedDBCallbacks
> callbacks
;
1011 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams
);
1014 void IndexedDBDatabase::OpenCursor(
1015 int64 transaction_id
,
1016 int64 object_store_id
,
1018 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1019 blink::WebIDBCursorDirection direction
,
1021 blink::WebIDBTaskType task_type
,
1022 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1023 IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction_id
);
1024 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1028 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
1031 scoped_ptr
<OpenCursorOperationParams
> params(new OpenCursorOperationParams());
1032 params
->object_store_id
= object_store_id
;
1033 params
->index_id
= index_id
;
1034 params
->key_range
= key_range
.Pass();
1035 params
->direction
= direction
;
1036 params
->cursor_type
=
1037 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
;
1038 params
->task_type
= task_type
;
1039 params
->callbacks
= callbacks
;
1040 transaction
->ScheduleTask(base::Bind(
1041 &IndexedDBDatabase::OpenCursorOperation
, this, base::Passed(¶ms
)));
1044 void IndexedDBDatabase::OpenCursorOperation(
1045 scoped_ptr
<OpenCursorOperationParams
> params
,
1046 IndexedDBTransaction
* transaction
) {
1048 "IndexedDBDatabase::OpenCursorOperation", "txn.id", transaction
->id());
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
== blink::WebIDBTaskTypePreemptive
)
1055 transaction
->AddPreemptiveEvent();
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
, blink::WebIDBTaskTypeNormal
);
1062 backing_store_cursor
= backing_store_
->OpenObjectStoreKeyCursor(
1063 transaction
->BackingStoreTransaction(),
1065 params
->object_store_id
,
1070 backing_store_cursor
= backing_store_
->OpenObjectStoreCursor(
1071 transaction
->BackingStoreTransaction(),
1073 params
->object_store_id
,
1079 DCHECK_EQ(params
->task_type
, blink::WebIDBTaskTypeNormal
);
1080 if (params
->cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
1081 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
1082 transaction
->BackingStoreTransaction(),
1084 params
->object_store_id
,
1090 backing_store_cursor
= backing_store_
->OpenIndexCursor(
1091 transaction
->BackingStoreTransaction(),
1093 params
->object_store_id
,
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(),
1111 if (!backing_store_cursor
) {
1112 // Why is Success being called?
1113 params
->callbacks
->OnSuccess(static_cast<IndexedDBValue
*>(NULL
));
1117 scoped_refptr
<IndexedDBCursor
> cursor
=
1118 new IndexedDBCursor(backing_store_cursor
.Pass(),
1119 params
->cursor_type
,
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
,
1129 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1130 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1131 IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction_id
);
1132 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1136 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
1139 transaction
->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation
,
1143 base::Passed(&key_range
),
1147 void IndexedDBDatabase::CountOperation(
1148 int64 object_store_id
,
1150 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1151 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1152 IndexedDBTransaction
* transaction
) {
1153 IDB_TRACE1("IndexedDBDatabase::CountOperation", "txn.id", transaction
->id());
1155 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
1158 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
1159 backing_store_cursor
= backing_store_
->OpenObjectStoreKeyCursor(
1160 transaction
->BackingStoreTransaction(),
1164 blink::WebIDBCursorDirectionNext
,
1167 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
1168 transaction
->BackingStoreTransaction(),
1173 blink::WebIDBCursorDirectionNext
,
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(),
1185 if (!backing_store_cursor
) {
1186 callbacks
->OnSuccess(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_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction_id
);
1205 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1208 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
1210 if (!ValidateObjectStoreId(object_store_id
))
1213 transaction
->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation
,
1216 base::Passed(&key_range
),
1220 void IndexedDBDatabase::DeleteRangeOperation(
1221 int64 object_store_id
,
1222 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1223 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1224 IndexedDBTransaction
* transaction
) {
1226 "IndexedDBDatabase::DeleteRangeOperation", "txn.id", transaction
->id());
1228 backing_store_
->DeleteRange(transaction
->BackingStoreTransaction(),
1233 base::string16 error_string
=
1234 ASCIIToUTF16("Internal error deleting data in range");
1235 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1237 transaction
->Abort(error
);
1238 if (s
.IsCorruption()) {
1239 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1244 callbacks
->OnSuccess();
1247 void IndexedDBDatabase::Clear(int64 transaction_id
,
1248 int64 object_store_id
,
1249 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1250 IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction_id
);
1251 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1254 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
1256 if (!ValidateObjectStoreId(object_store_id
))
1259 transaction
->ScheduleTask(base::Bind(
1260 &IndexedDBDatabase::ClearOperation
, this, object_store_id
, callbacks
));
1263 void IndexedDBDatabase::ClearOperation(
1264 int64 object_store_id
,
1265 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1266 IndexedDBTransaction
* transaction
) {
1267 IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction
->id());
1268 leveldb::Status s
= backing_store_
->ClearObjectStore(
1269 transaction
->BackingStoreTransaction(), id(), object_store_id
);
1271 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1272 "Internal error clearing object store");
1273 callbacks
->OnError(error
);
1274 if (s
.IsCorruption()) {
1275 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1280 callbacks
->OnSuccess();
1283 void IndexedDBDatabase::DeleteObjectStoreOperation(
1284 int64 object_store_id
,
1285 IndexedDBTransaction
* transaction
) {
1286 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreOperation",
1290 const IndexedDBObjectStoreMetadata object_store_metadata
=
1291 metadata_
.object_stores
[object_store_id
];
1293 backing_store_
->DeleteObjectStore(transaction
->BackingStoreTransaction(),
1294 transaction
->database()->id(),
1297 base::string16 error_string
=
1298 ASCIIToUTF16("Internal error deleting object store '") +
1299 object_store_metadata
.name
+ ASCIIToUTF16("'.");
1300 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1302 transaction
->Abort(error
);
1303 if (s
.IsCorruption())
1304 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1309 RemoveObjectStore(object_store_id
);
1310 transaction
->ScheduleAbortTask(
1311 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation
,
1313 object_store_metadata
));
1316 void IndexedDBDatabase::VersionChangeOperation(
1318 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1319 scoped_ptr
<IndexedDBConnection
> connection
,
1320 IndexedDBTransaction
* transaction
) {
1322 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction
->id());
1323 int64 old_version
= metadata_
.int_version
;
1324 DCHECK_GT(version
, old_version
);
1326 if (!backing_store_
->UpdateIDBDatabaseIntVersion(
1327 transaction
->BackingStoreTransaction(), id(), version
)) {
1328 IndexedDBDatabaseError
error(
1329 blink::WebIDBDatabaseExceptionUnknownError
,
1331 "Internal error writing data to stable storage when "
1332 "updating version."));
1333 callbacks
->OnError(error
);
1334 transaction
->Abort(error
);
1338 transaction
->ScheduleAbortTask(
1339 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation
,
1342 metadata_
.int_version
));
1343 metadata_
.int_version
= version
;
1344 metadata_
.version
= kNoStringVersion
;
1346 DCHECK(!pending_second_half_open_
);
1347 pending_second_half_open_
.reset(
1348 new PendingSuccessCall(callbacks
, connection
.get(), version
));
1349 callbacks
->OnUpgradeNeeded(old_version
, connection
.Pass(), metadata());
1352 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction
* transaction
,
1354 DCHECK(transactions_
.find(transaction
->id()) != transactions_
.end());
1355 DCHECK_EQ(transactions_
[transaction
->id()], transaction
);
1356 transactions_
.erase(transaction
->id());
1358 if (transaction
->mode() == blink::WebIDBTransactionModeVersionChange
) {
1359 if (pending_second_half_open_
) {
1361 DCHECK_EQ(pending_second_half_open_
->version(), metadata_
.int_version
);
1362 DCHECK(metadata_
.id
!= kInvalidId
);
1364 // Connection was already minted for OnUpgradeNeeded callback.
1365 scoped_ptr
<IndexedDBConnection
> connection
;
1366 pending_second_half_open_
->callbacks()->OnSuccess(connection
.Pass(),
1369 pending_second_half_open_
->callbacks()->OnError(
1370 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError
,
1371 "Version change transaction was aborted in "
1372 "upgradeneeded event handler."));
1374 pending_second_half_open_
.reset();
1377 // Connection queue is now unblocked.
1378 ProcessPendingCalls();
1382 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status
& status
) {
1383 if (status
.IsCorruption()) {
1384 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1385 "Error committing transaction");
1386 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(), error
);
1388 factory_
->HandleBackingStoreFailure(backing_store_
->origin_url());
1392 size_t IndexedDBDatabase::ConnectionCount() const {
1393 // This does not include pending open calls, as those should not block version
1394 // changes and deletes.
1395 return connections_
.size();
1398 size_t IndexedDBDatabase::PendingOpenCount() const {
1399 return pending_open_calls_
.size();
1402 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1403 return pending_run_version_change_transaction_call_
? 1 : 0;
1406 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1407 return pending_second_half_open_
? 1 : 0;
1410 size_t IndexedDBDatabase::PendingDeleteCount() const {
1411 return pending_delete_calls_
.size();
1414 void IndexedDBDatabase::ProcessPendingCalls() {
1415 if (pending_run_version_change_transaction_call_
&& ConnectionCount() == 1) {
1416 DCHECK(pending_run_version_change_transaction_call_
->version() >
1417 metadata_
.int_version
);
1418 scoped_ptr
<PendingUpgradeCall
> pending_call
=
1419 pending_run_version_change_transaction_call_
.Pass();
1420 RunVersionChangeTransactionFinal(pending_call
->callbacks(),
1421 pending_call
->ReleaseConnection(),
1422 pending_call
->transaction_id(),
1423 pending_call
->version());
1424 DCHECK_EQ(1u, ConnectionCount());
1425 // Fall through would be a no-op, since transaction must complete
1427 DCHECK(IsDeleteDatabaseBlocked());
1428 DCHECK(IsOpenConnectionBlocked());
1432 if (!IsDeleteDatabaseBlocked()) {
1433 PendingDeleteCallList pending_delete_calls
;
1434 pending_delete_calls_
.swap(pending_delete_calls
);
1435 while (!pending_delete_calls
.empty()) {
1436 // Only the first delete call will delete the database, but each must fire
1438 scoped_ptr
<PendingDeleteCall
> pending_delete_call(
1439 pending_delete_calls
.front());
1440 pending_delete_calls
.pop_front();
1441 DeleteDatabaseFinal(pending_delete_call
->callbacks());
1443 // delete_database_final should never re-queue calls.
1444 DCHECK(pending_delete_calls_
.empty());
1445 // Fall through when complete, as pending opens may be unblocked.
1448 if (!IsOpenConnectionBlocked()) {
1449 PendingOpenCallList pending_open_calls
;
1450 pending_open_calls_
.swap(pending_open_calls
);
1451 while (!pending_open_calls
.empty()) {
1452 OpenConnection(pending_open_calls
.front());
1453 pending_open_calls
.pop_front();
1458 void IndexedDBDatabase::CreateTransaction(
1459 int64 transaction_id
,
1460 IndexedDBConnection
* connection
,
1461 const std::vector
<int64
>& object_store_ids
,
1462 blink::WebIDBTransactionMode mode
) {
1463 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id
);
1464 DCHECK(connections_
.count(connection
));
1465 DCHECK(transactions_
.find(transaction_id
) == transactions_
.end());
1466 if (transactions_
.find(transaction_id
) != transactions_
.end())
1469 // The transaction will add itself to this database's coordinator, which
1470 // manages the lifetime of the object.
1471 TransactionCreated(new IndexedDBTransaction(
1473 connection
->callbacks(),
1474 std::set
<int64
>(object_store_ids
.begin(), object_store_ids
.end()),
1477 new IndexedDBBackingStore::Transaction(backing_store_
.get())));
1480 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction
* transaction
) {
1481 transactions_
[transaction
->id()] = transaction
;
1484 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1485 return !pending_delete_calls_
.empty() ||
1486 transaction_coordinator_
.IsRunningVersionChangeTransaction() ||
1487 pending_run_version_change_transaction_call_
;
1490 void IndexedDBDatabase::OpenConnection(
1491 const IndexedDBPendingConnection
& connection
) {
1492 DCHECK(backing_store_
.get());
1494 // TODO(jsbell): Should have a priority queue so that higher version
1495 // requests are processed first. http://crbug.com/225850
1496 if (IsOpenConnectionBlocked()) {
1497 // The backing store only detects data loss when it is first opened. The
1498 // presence of existing connections means we didn't even check for data loss
1499 // so there'd better not be any.
1500 DCHECK_NE(blink::WebIDBDataLossTotal
, connection
.callbacks
->data_loss());
1501 pending_open_calls_
.push_back(connection
);
1505 if (metadata_
.id
== kInvalidId
) {
1506 // The database was deleted then immediately re-opened; OpenInternal()
1507 // recreates it in the backing store.
1508 if (OpenInternal().ok()) {
1509 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION
,
1510 metadata_
.int_version
);
1512 base::string16 message
;
1513 if (connection
.version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
) {
1514 message
= ASCIIToUTF16(
1515 "Internal error opening database with no version specified.");
1518 ASCIIToUTF16("Internal error opening database with version ") +
1519 Int64ToString16(connection
.version
);
1521 connection
.callbacks
->OnError(IndexedDBDatabaseError(
1522 blink::WebIDBDatabaseExceptionUnknownError
, message
));
1527 // We infer that the database didn't exist from its lack of either type of
1529 bool is_new_database
=
1530 metadata_
.version
== kNoStringVersion
&&
1531 metadata_
.int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1533 if (connection
.version
== IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
) {
1534 // For unit tests only - skip upgrade steps. Calling from script with
1535 // DEFAULT_INT_VERSION throws exception.
1536 // TODO(jsbell): DCHECK that not in unit tests.
1537 DCHECK(is_new_database
);
1538 connection
.callbacks
->OnSuccess(
1539 CreateConnection(connection
.database_callbacks
,
1540 connection
.child_process_id
),
1545 // We may need to change the version.
1546 int64 local_version
= connection
.version
;
1547 if (local_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
) {
1548 if (!is_new_database
) {
1549 connection
.callbacks
->OnSuccess(
1550 CreateConnection(connection
.database_callbacks
,
1551 connection
.child_process_id
),
1555 // Spec says: If no version is specified and no database exists, set
1556 // database version to 1.
1560 if (local_version
> metadata_
.int_version
) {
1561 RunVersionChangeTransaction(connection
.callbacks
,
1562 CreateConnection(connection
.database_callbacks
,
1563 connection
.child_process_id
),
1564 connection
.transaction_id
,
1568 if (local_version
< metadata_
.int_version
) {
1569 connection
.callbacks
->OnError(IndexedDBDatabaseError(
1570 blink::WebIDBDatabaseExceptionVersionError
,
1571 ASCIIToUTF16("The requested version (") +
1572 Int64ToString16(local_version
) +
1573 ASCIIToUTF16(") is less than the existing version (") +
1574 Int64ToString16(metadata_
.int_version
) + ASCIIToUTF16(").")));
1577 DCHECK_EQ(local_version
, metadata_
.int_version
);
1578 connection
.callbacks
->OnSuccess(
1579 CreateConnection(connection
.database_callbacks
,
1580 connection
.child_process_id
),
1584 void IndexedDBDatabase::RunVersionChangeTransaction(
1585 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1586 scoped_ptr
<IndexedDBConnection
> connection
,
1587 int64 transaction_id
,
1588 int64 requested_version
) {
1589 DCHECK(callbacks
.get());
1590 DCHECK(connections_
.count(connection
.get()));
1591 if (ConnectionCount() > 1) {
1592 DCHECK_NE(blink::WebIDBDataLossTotal
, callbacks
->data_loss());
1593 // Front end ensures the event is not fired at connections that have
1594 // close_pending set.
1595 for (const auto* iter
: connections_
) {
1596 if (iter
!= connection
.get()) {
1597 iter
->callbacks()->OnVersionChange(metadata_
.int_version
,
1601 // OnBlocked will be fired at the request when one of the other
1602 // connections acks that the OnVersionChange was ignored.
1604 DCHECK(!pending_run_version_change_transaction_call_
);
1605 pending_run_version_change_transaction_call_
.reset(new PendingUpgradeCall(
1606 callbacks
, connection
.Pass(), transaction_id
, requested_version
));
1609 RunVersionChangeTransactionFinal(
1610 callbacks
, connection
.Pass(), transaction_id
, requested_version
);
1613 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1614 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1615 scoped_ptr
<IndexedDBConnection
> connection
,
1616 int64 transaction_id
,
1617 int64 requested_version
) {
1619 std::vector
<int64
> object_store_ids
;
1620 CreateTransaction(transaction_id
,
1623 blink::WebIDBTransactionModeVersionChange
);
1625 transactions_
[transaction_id
]->ScheduleTask(
1626 base::Bind(&IndexedDBDatabase::VersionChangeOperation
,
1630 base::Passed(&connection
)));
1631 DCHECK(!pending_second_half_open_
);
1634 void IndexedDBDatabase::DeleteDatabase(
1635 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1637 if (IsDeleteDatabaseBlocked()) {
1638 for (const auto* connection
: connections_
) {
1639 // Front end ensures the event is not fired at connections that have
1640 // close_pending set.
1641 connection
->callbacks()->OnVersionChange(
1642 metadata_
.int_version
, IndexedDBDatabaseMetadata::NO_INT_VERSION
);
1644 // OnBlocked will be fired at the request when one of the other
1645 // connections acks that the OnVersionChange was ignored.
1647 pending_delete_calls_
.push_back(new PendingDeleteCall(callbacks
));
1650 DeleteDatabaseFinal(callbacks
);
1653 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1654 return !!ConnectionCount();
1657 void IndexedDBDatabase::DeleteDatabaseFinal(
1658 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1659 DCHECK(!IsDeleteDatabaseBlocked());
1660 DCHECK(backing_store_
.get());
1661 leveldb::Status s
= backing_store_
->DeleteDatabase(metadata_
.name
);
1663 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1664 "Internal error deleting database.");
1665 callbacks
->OnError(error
);
1666 if (s
.IsCorruption()) {
1667 GURL origin_url
= backing_store_
->origin_url();
1668 backing_store_
= NULL
;
1669 factory_
->HandleBackingStoreCorruption(origin_url
, error
);
1673 int64 old_version
= metadata_
.int_version
;
1674 metadata_
.version
= kNoStringVersion
;
1675 metadata_
.id
= kInvalidId
;
1676 metadata_
.int_version
= IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1677 metadata_
.object_stores
.clear();
1678 callbacks
->OnSuccess(old_version
);
1679 factory_
->DatabaseDeleted(identifier_
);
1682 void IndexedDBDatabase::ForceClose() {
1683 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1684 scoped_refptr
<IndexedDBDatabase
> protect(this);
1685 ConnectionSet::const_iterator it
= connections_
.begin();
1686 while (it
!= connections_
.end()) {
1687 IndexedDBConnection
* connection
= *it
++;
1688 connection
->ForceClose();
1690 DCHECK(connections_
.empty());
1693 void IndexedDBDatabase::VersionChangeIgnored() {
1694 if (pending_run_version_change_transaction_call_
)
1695 pending_run_version_change_transaction_call_
->callbacks()->OnBlocked(
1696 metadata_
.int_version
);
1698 for (const auto& pending_delete_call
: pending_delete_calls_
)
1699 pending_delete_call
->callbacks()->OnBlocked(metadata_
.int_version
);
1703 void IndexedDBDatabase::Close(IndexedDBConnection
* connection
, bool forced
) {
1704 DCHECK(connections_
.count(connection
));
1705 DCHECK(connection
->IsConnected());
1706 DCHECK(connection
->database() == this);
1708 IDB_TRACE("IndexedDBDatabase::Close");
1710 connections_
.erase(connection
);
1712 // Abort outstanding transactions from the closing connection. This
1713 // can not happen if the close is requested by the connection itself
1714 // as the front-end defers the close until all transactions are
1715 // complete, but can occur on process termination or forced close.
1717 TransactionMap
transactions(transactions_
);
1718 for (const auto& it
: transactions
) {
1719 if (it
.second
->connection() == connection
->callbacks())
1721 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
,
1722 "Connection is closing."));
1726 if (pending_second_half_open_
&&
1727 pending_second_half_open_
->connection() == connection
) {
1728 pending_second_half_open_
->callbacks()->OnError(
1729 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError
,
1730 "The connection was closed."));
1731 pending_second_half_open_
.reset();
1734 ProcessPendingCalls();
1736 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1737 if (!ConnectionCount() && !pending_open_calls_
.size() &&
1738 !pending_delete_calls_
.size()) {
1739 DCHECK(transactions_
.empty());
1741 const GURL origin_url
= backing_store_
->origin_url();
1742 backing_store_
= NULL
;
1744 factory_
->ReleaseDatabase(identifier_
, forced
);
1748 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1749 int64 object_store_id
,
1750 IndexedDBTransaction
* transaction
) {
1751 DCHECK(!transaction
);
1752 IDB_TRACE1("IndexedDBDatabase::CreateObjectStoreAbortOperation",
1755 RemoveObjectStore(object_store_id
);
1758 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1759 const IndexedDBObjectStoreMetadata
& object_store_metadata
,
1760 IndexedDBTransaction
* transaction
) {
1761 DCHECK(!transaction
);
1762 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreAbortOperation",
1765 AddObjectStore(object_store_metadata
,
1766 IndexedDBObjectStoreMetadata::kInvalidId
);
1769 void IndexedDBDatabase::VersionChangeAbortOperation(
1770 const base::string16
& previous_version
,
1771 int64 previous_int_version
,
1772 IndexedDBTransaction
* transaction
) {
1773 DCHECK(!transaction
);
1774 IDB_TRACE1("IndexedDBDatabase::VersionChangeAbortOperation",
1777 metadata_
.version
= previous_version
;
1778 metadata_
.int_version
= previous_int_version
;
1781 } // namespace content