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"
11 #include "base/auto_reset.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/scoped_vector.h"
15 #include "base/metrics/histogram_macros.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "content/browser/indexed_db/indexed_db_blob_info.h"
20 #include "content/browser/indexed_db/indexed_db_class_factory.h"
21 #include "content/browser/indexed_db/indexed_db_connection.h"
22 #include "content/browser/indexed_db/indexed_db_context_impl.h"
23 #include "content/browser/indexed_db/indexed_db_cursor.h"
24 #include "content/browser/indexed_db/indexed_db_factory.h"
25 #include "content/browser/indexed_db/indexed_db_index_writer.h"
26 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
27 #include "content/browser/indexed_db/indexed_db_return_value.h"
28 #include "content/browser/indexed_db/indexed_db_tracing.h"
29 #include "content/browser/indexed_db/indexed_db_transaction.h"
30 #include "content/browser/indexed_db/indexed_db_value.h"
31 #include "content/common/indexed_db/indexed_db_constants.h"
32 #include "content/common/indexed_db/indexed_db_key_path.h"
33 #include "content/common/indexed_db/indexed_db_key_range.h"
34 #include "storage/browser/blob/blob_data_handle.h"
35 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseException.h"
36 #include "third_party/leveldatabase/env_chromium.h"
38 using base::ASCIIToUTF16
;
39 using base::Int64ToString16
;
40 using blink::WebIDBKeyTypeNumber
;
46 // Used for WebCore.IndexedDB.Schema.ObjectStore.KeyPathType and
47 // WebCore.IndexedDB.Schema.Index.KeyPathType histograms. Do not
48 // modify (delete, re-order, renumber) these values other than
50 enum HistogramIDBKeyPathType
{
51 KEY_PATH_TYPE_NONE
= 0,
52 KEY_PATH_TYPE_STRING
= 1,
53 KEY_PATH_TYPE_ARRAY
= 2,
54 KEY_PATH_TYPE_MAX
= 3, // Keep as last/max entry, for histogram range.
57 HistogramIDBKeyPathType
HistogramKeyPathType(const IndexedDBKeyPath
& key_path
) {
58 switch (key_path
.type()) {
59 case blink::WebIDBKeyPathTypeNull
:
60 return KEY_PATH_TYPE_NONE
;
61 case blink::WebIDBKeyPathTypeString
:
62 return KEY_PATH_TYPE_STRING
;
63 case blink::WebIDBKeyPathTypeArray
:
64 return KEY_PATH_TYPE_ARRAY
;
67 return KEY_PATH_TYPE_NONE
;
72 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
73 // in-progress connection.
74 class IndexedDBDatabase::PendingUpgradeCall
{
76 PendingUpgradeCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
,
77 scoped_ptr
<IndexedDBConnection
> connection
,
80 : callbacks_(callbacks
),
81 connection_(connection
.Pass()),
83 transaction_id_(transaction_id
) {}
84 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
85 // Takes ownership of the connection object.
86 scoped_ptr
<IndexedDBConnection
> ReleaseConnection() WARN_UNUSED_RESULT
{
87 return connection_
.Pass();
89 int64
version() const { return version_
; }
90 int64
transaction_id() const { return transaction_id_
; }
93 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
94 scoped_ptr
<IndexedDBConnection
> connection_
;
96 const int64 transaction_id_
;
99 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
100 // owned elsewhere, but we need to cancel the success call if that connection
101 // closes before it is sent.
102 class IndexedDBDatabase::PendingSuccessCall
{
104 PendingSuccessCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
,
105 IndexedDBConnection
* connection
,
107 : callbacks_(callbacks
), connection_(connection
), version_(version
) {}
108 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
109 IndexedDBConnection
* connection() const { return connection_
; }
110 int64
version() const { return version_
; }
113 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
114 IndexedDBConnection
* connection_
;
118 class IndexedDBDatabase::PendingDeleteCall
{
120 explicit PendingDeleteCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
)
121 : callbacks_(callbacks
) {}
122 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
125 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
128 scoped_refptr
<IndexedDBDatabase
> IndexedDBDatabase::Create(
129 const base::string16
& name
,
130 IndexedDBBackingStore
* backing_store
,
131 IndexedDBFactory
* factory
,
132 const Identifier
& unique_identifier
,
133 leveldb::Status
* s
) {
134 scoped_refptr
<IndexedDBDatabase
> database
=
135 IndexedDBClassFactory::Get()->CreateIndexedDBDatabase(
136 name
, backing_store
, factory
, unique_identifier
);
137 *s
= database
->OpenInternal();
145 const base::string16::value_type kNoStringVersion
[] = {0};
148 IndexedDBDatabase::IndexedDBDatabase(const base::string16
& name
,
149 IndexedDBBackingStore
* backing_store
,
150 IndexedDBFactory
* factory
,
151 const Identifier
& unique_identifier
)
152 : backing_store_(backing_store
),
156 IndexedDBDatabaseMetadata::NO_INT_VERSION
,
158 identifier_(unique_identifier
),
160 DCHECK(factory
!= NULL
);
163 void IndexedDBDatabase::AddObjectStore(
164 const IndexedDBObjectStoreMetadata
& object_store
,
165 int64 new_max_object_store_id
) {
166 DCHECK(metadata_
.object_stores
.find(object_store
.id
) ==
167 metadata_
.object_stores
.end());
168 if (new_max_object_store_id
!= IndexedDBObjectStoreMetadata::kInvalidId
) {
169 DCHECK_LT(metadata_
.max_object_store_id
, new_max_object_store_id
);
170 metadata_
.max_object_store_id
= new_max_object_store_id
;
172 metadata_
.object_stores
[object_store
.id
] = object_store
;
175 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id
) {
176 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
177 metadata_
.object_stores
.end());
178 metadata_
.object_stores
.erase(object_store_id
);
181 void IndexedDBDatabase::AddIndex(int64 object_store_id
,
182 const IndexedDBIndexMetadata
& index
,
183 int64 new_max_index_id
) {
184 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
185 metadata_
.object_stores
.end());
186 IndexedDBObjectStoreMetadata object_store
=
187 metadata_
.object_stores
[object_store_id
];
189 DCHECK(object_store
.indexes
.find(index
.id
) == object_store
.indexes
.end());
190 object_store
.indexes
[index
.id
] = index
;
191 if (new_max_index_id
!= IndexedDBIndexMetadata::kInvalidId
) {
192 DCHECK_LT(object_store
.max_index_id
, new_max_index_id
);
193 object_store
.max_index_id
= new_max_index_id
;
195 metadata_
.object_stores
[object_store_id
] = object_store
;
198 void IndexedDBDatabase::RemoveIndex(int64 object_store_id
, int64 index_id
) {
199 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
200 metadata_
.object_stores
.end());
201 IndexedDBObjectStoreMetadata object_store
=
202 metadata_
.object_stores
[object_store_id
];
204 DCHECK(object_store
.indexes
.find(index_id
) != object_store
.indexes
.end());
205 object_store
.indexes
.erase(index_id
);
206 metadata_
.object_stores
[object_store_id
] = object_store
;
209 leveldb::Status
IndexedDBDatabase::OpenInternal() {
210 bool success
= false;
211 leveldb::Status s
= backing_store_
->GetIDBDatabaseMetaData(
212 metadata_
.name
, &metadata_
, &success
);
213 DCHECK(success
== (metadata_
.id
!= kInvalidId
)) << "success = " << success
214 << " id = " << metadata_
.id
;
218 return backing_store_
->GetObjectStores(metadata_
.id
,
219 &metadata_
.object_stores
);
221 return backing_store_
->CreateIDBDatabaseMetaData(
222 metadata_
.name
, metadata_
.version
, metadata_
.int_version
, &metadata_
.id
);
225 IndexedDBDatabase::~IndexedDBDatabase() {
226 DCHECK(transactions_
.empty());
227 DCHECK(pending_open_calls_
.empty());
228 DCHECK(pending_delete_calls_
.empty());
231 size_t IndexedDBDatabase::GetMaxMessageSizeInBytes() const {
232 return kMaxIDBMessageSizeInBytes
;
235 scoped_ptr
<IndexedDBConnection
> IndexedDBDatabase::CreateConnection(
236 scoped_refptr
<IndexedDBDatabaseCallbacks
> database_callbacks
,
237 int child_process_id
) {
238 scoped_ptr
<IndexedDBConnection
> connection(
239 new IndexedDBConnection(this, database_callbacks
));
240 connections_
.insert(connection
.get());
241 backing_store_
->GrantChildProcessPermissions(child_process_id
);
242 return connection
.Pass();
245 IndexedDBTransaction
* IndexedDBDatabase::GetTransaction(
246 int64 transaction_id
) const {
247 TransactionMap::const_iterator trans_iterator
=
248 transactions_
.find(transaction_id
);
249 if (trans_iterator
== transactions_
.end())
251 return trans_iterator
->second
;
254 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id
) const {
255 if (!ContainsKey(metadata_
.object_stores
, object_store_id
)) {
256 DLOG(ERROR
) << "Invalid object_store_id";
262 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id
,
263 int64 index_id
) const {
264 if (!ValidateObjectStoreId(object_store_id
))
266 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
267 metadata_
.object_stores
.find(object_store_id
)->second
;
268 if (!ContainsKey(object_store_metadata
.indexes
, index_id
)) {
269 DLOG(ERROR
) << "Invalid index_id";
275 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
276 int64 object_store_id
,
277 int64 index_id
) const {
278 if (!ValidateObjectStoreId(object_store_id
))
280 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
281 metadata_
.object_stores
.find(object_store_id
)->second
;
282 if (index_id
!= IndexedDBIndexMetadata::kInvalidId
&&
283 !ContainsKey(object_store_metadata
.indexes
, index_id
)) {
284 DLOG(ERROR
) << "Invalid index_id";
290 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
291 int64 object_store_id
,
292 int64 index_id
) const {
293 if (!ValidateObjectStoreId(object_store_id
))
295 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
296 metadata_
.object_stores
.find(object_store_id
)->second
;
297 if (ContainsKey(object_store_metadata
.indexes
, index_id
)) {
298 DLOG(ERROR
) << "Invalid index_id";
304 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id
,
305 int64 object_store_id
,
306 const base::string16
& name
,
307 const IndexedDBKeyPath
& key_path
,
308 bool auto_increment
) {
309 IDB_TRACE1("IndexedDBDatabase::CreateObjectStore", "txn.id", transaction_id
);
310 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
313 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
315 if (ContainsKey(metadata_
.object_stores
, object_store_id
)) {
316 DLOG(ERROR
) << "Invalid object_store_id";
320 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.ObjectStore.KeyPathType",
321 HistogramKeyPathType(key_path
), KEY_PATH_TYPE_MAX
);
322 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.ObjectStore.AutoIncrement",
325 // Store creation is done synchronously, as it may be followed by
326 // index creation (also sync) since preemptive OpenCursor/SetIndexKeys
328 IndexedDBObjectStoreMetadata
object_store_metadata(
333 IndexedDBDatabase::kMinimumIndexId
);
336 backing_store_
->CreateObjectStore(transaction
->BackingStoreTransaction(),
337 transaction
->database()->id(),
338 object_store_metadata
.id
,
339 object_store_metadata
.name
,
340 object_store_metadata
.key_path
,
341 object_store_metadata
.auto_increment
);
343 IndexedDBDatabaseError
error(
344 blink::WebIDBDatabaseExceptionUnknownError
,
345 ASCIIToUTF16("Internal error creating object store '") +
346 object_store_metadata
.name
+ ASCIIToUTF16("'."));
347 transaction
->Abort(error
);
348 if (s
.IsCorruption())
349 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
354 AddObjectStore(object_store_metadata
, object_store_id
);
355 transaction
->ScheduleAbortTask(
356 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation
,
361 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id
,
362 int64 object_store_id
) {
363 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStore", "txn.id", transaction_id
);
364 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
367 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
369 if (!ValidateObjectStoreId(object_store_id
))
372 transaction
->ScheduleTask(
373 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation
,
378 void IndexedDBDatabase::CreateIndex(int64 transaction_id
,
379 int64 object_store_id
,
381 const base::string16
& name
,
382 const IndexedDBKeyPath
& key_path
,
385 IDB_TRACE1("IndexedDBDatabase::CreateIndex", "txn.id", transaction_id
);
386 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
389 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
391 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id
, index_id
))
394 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.Index.KeyPathType",
395 HistogramKeyPathType(key_path
), KEY_PATH_TYPE_MAX
);
396 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.Unique", unique
);
397 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.MultiEntry",
400 // Index creation is done synchronously since preemptive
401 // OpenCursor/SetIndexKeys may follow.
402 const IndexedDBIndexMetadata
index_metadata(
403 name
, index_id
, key_path
, unique
, multi_entry
);
405 if (!backing_store_
->CreateIndex(transaction
->BackingStoreTransaction(),
406 transaction
->database()->id(),
410 index_metadata
.key_path
,
411 index_metadata
.unique
,
412 index_metadata
.multi_entry
).ok()) {
413 base::string16 error_string
=
414 ASCIIToUTF16("Internal error creating index '") +
415 index_metadata
.name
+ ASCIIToUTF16("'.");
416 transaction
->Abort(IndexedDBDatabaseError(
417 blink::WebIDBDatabaseExceptionUnknownError
, error_string
));
421 AddIndex(object_store_id
, index_metadata
, index_id
);
422 transaction
->ScheduleAbortTask(
423 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation
,
429 void IndexedDBDatabase::CreateIndexAbortOperation(
430 int64 object_store_id
,
432 IndexedDBTransaction
* transaction
) {
433 DCHECK(!transaction
);
434 IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation");
435 RemoveIndex(object_store_id
, index_id
);
438 void IndexedDBDatabase::DeleteIndex(int64 transaction_id
,
439 int64 object_store_id
,
441 IDB_TRACE1("IndexedDBDatabase::DeleteIndex", "txn.id", transaction_id
);
442 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
445 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
447 if (!ValidateObjectStoreIdAndIndexId(object_store_id
, index_id
))
450 transaction
->ScheduleTask(
451 base::Bind(&IndexedDBDatabase::DeleteIndexOperation
,
457 void IndexedDBDatabase::DeleteIndexOperation(
458 int64 object_store_id
,
460 IndexedDBTransaction
* transaction
) {
462 "IndexedDBDatabase::DeleteIndexOperation", "txn.id", transaction
->id());
464 const IndexedDBIndexMetadata index_metadata
=
465 metadata_
.object_stores
[object_store_id
].indexes
[index_id
];
468 backing_store_
->DeleteIndex(transaction
->BackingStoreTransaction(),
469 transaction
->database()->id(),
473 base::string16 error_string
=
474 ASCIIToUTF16("Internal error deleting index '") +
475 index_metadata
.name
+ ASCIIToUTF16("'.");
476 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
478 transaction
->Abort(error
);
479 if (s
.IsCorruption())
480 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
485 RemoveIndex(object_store_id
, index_id
);
486 transaction
->ScheduleAbortTask(
487 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation
,
493 void IndexedDBDatabase::DeleteIndexAbortOperation(
494 int64 object_store_id
,
495 const IndexedDBIndexMetadata
& index_metadata
,
496 IndexedDBTransaction
* transaction
) {
497 DCHECK(!transaction
);
498 IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation");
499 AddIndex(object_store_id
, index_metadata
, IndexedDBIndexMetadata::kInvalidId
);
502 void IndexedDBDatabase::Commit(int64 transaction_id
) {
503 // The frontend suggests that we commit, but we may have previously initiated
504 // an abort, and so have disposed of the transaction. on_abort has already
505 // been dispatched to the frontend, so it will find out about that
507 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
509 scoped_refptr
<IndexedDBFactory
> factory
= factory_
;
510 leveldb::Status s
= transaction
->Commit();
511 if (s
.IsCorruption()) {
512 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
513 "Internal error committing transaction.");
514 factory
->HandleBackingStoreCorruption(identifier_
.first
, error
);
519 void IndexedDBDatabase::Abort(int64 transaction_id
) {
520 // If the transaction is unknown, then it has already been aborted by the
521 // backend before this call so it is safe to ignore it.
522 IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction_id
);
523 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
525 transaction
->Abort();
528 void IndexedDBDatabase::Abort(int64 transaction_id
,
529 const IndexedDBDatabaseError
& error
) {
530 IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction_id
);
531 // If the transaction is unknown, then it has already been aborted by the
532 // backend before this call so it is safe to ignore it.
533 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
535 transaction
->Abort(error
);
538 void IndexedDBDatabase::GetAll(int64 transaction_id
,
539 int64 object_store_id
,
541 scoped_ptr
<IndexedDBKeyRange
> key_range
,
544 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
545 IDB_TRACE1("IndexedDBDatabase::GetAll", "txn.id", transaction_id
);
546 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
550 if (!ValidateObjectStoreId(object_store_id
))
553 transaction
->ScheduleTask(base::Bind(
554 &IndexedDBDatabase::GetAllOperation
, this, object_store_id
, index_id
,
556 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
,
557 max_count
, callbacks
));
560 void IndexedDBDatabase::Get(int64 transaction_id
,
561 int64 object_store_id
,
563 scoped_ptr
<IndexedDBKeyRange
> key_range
,
565 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
566 IDB_TRACE1("IndexedDBDatabase::Get", "txn.id", transaction_id
);
567 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
571 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
574 transaction
->ScheduleTask(base::Bind(
575 &IndexedDBDatabase::GetOperation
,
580 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
,
584 void IndexedDBDatabase::GetOperation(
585 int64 object_store_id
,
587 scoped_ptr
<IndexedDBKeyRange
> key_range
,
588 indexed_db::CursorType cursor_type
,
589 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
590 IndexedDBTransaction
* transaction
) {
591 IDB_TRACE1("IndexedDBDatabase::GetOperation", "txn.id", transaction
->id());
593 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
594 metadata_
.object_stores
.end());
595 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
596 metadata_
.object_stores
[object_store_id
];
598 const IndexedDBKey
* key
;
601 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
602 if (key_range
->IsOnlyKey()) {
603 key
= &key_range
->lower();
605 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
606 DCHECK_NE(cursor_type
, indexed_db::CURSOR_KEY_ONLY
);
607 // ObjectStore Retrieval Operation
608 backing_store_cursor
= backing_store_
->OpenObjectStoreCursor(
609 transaction
->BackingStoreTransaction(),
613 blink::WebIDBCursorDirectionNext
,
615 } else if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
616 // Index Value Retrieval Operation
617 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
618 transaction
->BackingStoreTransaction(),
623 blink::WebIDBCursorDirectionNext
,
626 // Index Referenced Value Retrieval Operation
627 backing_store_cursor
= backing_store_
->OpenIndexCursor(
628 transaction
->BackingStoreTransaction(),
633 blink::WebIDBCursorDirectionNext
,
638 DLOG(ERROR
) << "Unable to open cursor operation: " << s
.ToString();
639 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
640 "Internal error deleting data in range");
641 if (s
.IsCorruption()) {
642 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
647 if (!backing_store_cursor
) {
648 callbacks
->OnSuccess();
652 key
= &backing_store_cursor
->key();
655 scoped_ptr
<IndexedDBKey
> primary_key
;
656 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
657 // Object Store Retrieval Operation
658 IndexedDBReturnValue value
;
659 s
= backing_store_
->GetRecord(transaction
->BackingStoreTransaction(),
665 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
666 "Internal error in GetRecord.");
667 callbacks
->OnError(error
);
669 if (s
.IsCorruption())
670 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
676 callbacks
->OnSuccess();
680 if (object_store_metadata
.auto_increment
&&
681 !object_store_metadata
.key_path
.IsNull()) {
682 value
.primary_key
= *key
;
683 value
.key_path
= object_store_metadata
.key_path
;
686 callbacks
->OnSuccess(&value
);
690 // From here we are dealing only with indexes.
691 s
= backing_store_
->GetPrimaryKeyViaIndex(
692 transaction
->BackingStoreTransaction(),
699 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
700 "Internal error in GetPrimaryKeyViaIndex.");
701 callbacks
->OnError(error
);
702 if (s
.IsCorruption())
703 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
708 callbacks
->OnSuccess();
711 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
712 // Index Value Retrieval Operation
713 callbacks
->OnSuccess(*primary_key
);
717 // Index Referenced Value Retrieval Operation
718 IndexedDBReturnValue value
;
719 s
= backing_store_
->GetRecord(transaction
->BackingStoreTransaction(),
725 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
726 "Internal error in GetRecord.");
727 callbacks
->OnError(error
);
728 if (s
.IsCorruption())
729 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
735 callbacks
->OnSuccess();
738 if (object_store_metadata
.auto_increment
&&
739 !object_store_metadata
.key_path
.IsNull()) {
740 value
.primary_key
= *primary_key
;
741 value
.key_path
= object_store_metadata
.key_path
;
743 callbacks
->OnSuccess(&value
);
746 void IndexedDBDatabase::GetAllOperation(
747 int64 object_store_id
,
749 scoped_ptr
<IndexedDBKeyRange
> key_range
,
750 indexed_db::CursorType cursor_type
,
752 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
753 IndexedDBTransaction
* transaction
) {
754 IDB_TRACE1("IndexedDBDatabase::GetAllOperation", "txn.id", transaction
->id());
756 DCHECK_GT(max_count
, 0);
758 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
759 metadata_
.object_stores
.end());
760 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
761 metadata_
.object_stores
[object_store_id
];
765 scoped_ptr
<IndexedDBBackingStore::Cursor
> cursor
;
767 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
769 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
770 // Object Store: Key Retrieval Operation
771 cursor
= backing_store_
->OpenObjectStoreKeyCursor(
772 transaction
->BackingStoreTransaction(), id(), object_store_id
,
773 *key_range
, blink::WebIDBCursorDirectionNext
, &s
);
775 // Index Value: (Primary Key) Retrieval Operation
776 cursor
= backing_store_
->OpenIndexKeyCursor(
777 transaction
->BackingStoreTransaction(), id(), object_store_id
,
778 index_id
, *key_range
, blink::WebIDBCursorDirectionNext
, &s
);
782 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
783 // Object Store: Value Retrieval Operation
784 cursor
= backing_store_
->OpenObjectStoreCursor(
785 transaction
->BackingStoreTransaction(), id(), object_store_id
,
786 *key_range
, blink::WebIDBCursorDirectionNext
, &s
);
788 // Object Store: Referenced Value Retrieval Operation
789 cursor
= backing_store_
->OpenIndexCursor(
790 transaction
->BackingStoreTransaction(), id(), object_store_id
,
791 index_id
, *key_range
, blink::WebIDBCursorDirectionNext
, &s
);
796 DLOG(ERROR
) << "Unable to open cursor operation: " << s
.ToString();
797 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
798 "Internal error in GetAllOperation");
799 callbacks
->OnError(error
);
800 if (s
.IsCorruption()) {
801 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
807 std::vector
<IndexedDBKey
> found_keys
;
808 std::vector
<IndexedDBReturnValue
> found_values
;
810 // Doesn't matter if key or value array here - will be empty array when it
812 callbacks
->OnSuccessArray(&found_values
, object_store_metadata
.key_path
);
816 bool did_first_seek
= false;
817 bool generated_key
= object_store_metadata
.auto_increment
&&
818 !object_store_metadata
.key_path
.IsNull();
820 size_t response_size
= kMaxIDBMessageOverhead
;
821 int64 num_found_items
= 0;
822 while (num_found_items
++ < max_count
) {
824 if (did_first_seek
) {
825 cursor_valid
= cursor
->Continue(&s
);
827 cursor_valid
= cursor
->FirstSeek(&s
);
828 did_first_seek
= true;
831 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
832 "Internal error in GetAllOperation.");
833 callbacks
->OnError(error
);
834 if (s
.IsCorruption())
835 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
843 IndexedDBReturnValue return_value
;
844 IndexedDBKey return_key
;
846 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
847 return_key
= cursor
->primary_key();
850 return_value
.swap(*cursor
->value());
851 if (!return_value
.empty() && generated_key
) {
852 return_value
.primary_key
= cursor
->primary_key();
853 return_value
.key_path
= object_store_metadata
.key_path
;
857 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
)
858 response_size
+= return_key
.size_estimate();
860 response_size
+= return_value
.SizeEstimate();
861 if (response_size
> GetMaxMessageSizeInBytes()) {
863 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
,
864 "Maximum IPC message size exceeded."));
868 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
)
869 found_keys
.push_back(return_key
);
871 found_values
.push_back(return_value
);
874 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
875 // IndexedDBKey already supports an array of values so we can leverage this
876 // to return an array of keys - no need to create our own array of keys.
877 callbacks
->OnSuccess(IndexedDBKey(found_keys
));
879 callbacks
->OnSuccessArray(&found_values
, object_store_metadata
.key_path
);
883 static scoped_ptr
<IndexedDBKey
> GenerateKey(
884 IndexedDBBackingStore
* backing_store
,
885 IndexedDBTransaction
* transaction
,
887 int64 object_store_id
) {
888 const int64 max_generator_value
=
889 9007199254740992LL; // Maximum integer storable as ECMAScript number.
890 int64 current_number
;
891 leveldb::Status s
= backing_store
->GetKeyGeneratorCurrentNumber(
892 transaction
->BackingStoreTransaction(),
897 LOG(ERROR
) << "Failed to GetKeyGeneratorCurrentNumber";
898 return make_scoped_ptr(new IndexedDBKey());
900 if (current_number
< 0 || current_number
> max_generator_value
)
901 return make_scoped_ptr(new IndexedDBKey());
903 return make_scoped_ptr(new IndexedDBKey(current_number
, WebIDBKeyTypeNumber
));
906 static leveldb::Status
UpdateKeyGenerator(IndexedDBBackingStore
* backing_store
,
907 IndexedDBTransaction
* transaction
,
909 int64 object_store_id
,
910 const IndexedDBKey
& key
,
911 bool check_current
) {
912 DCHECK_EQ(WebIDBKeyTypeNumber
, key
.type());
913 return backing_store
->MaybeUpdateKeyGeneratorCurrentNumber(
914 transaction
->BackingStoreTransaction(),
917 static_cast<int64
>(floor(key
.number())) + 1,
921 struct IndexedDBDatabase::PutOperationParams
{
922 PutOperationParams() {}
923 int64 object_store_id
;
924 IndexedDBValue value
;
925 ScopedVector
<storage::BlobDataHandle
> handles
;
926 scoped_ptr
<IndexedDBKey
> key
;
927 blink::WebIDBPutMode put_mode
;
928 scoped_refptr
<IndexedDBCallbacks
> callbacks
;
929 std::vector
<IndexKeys
> index_keys
;
932 DISALLOW_COPY_AND_ASSIGN(PutOperationParams
);
935 void IndexedDBDatabase::Put(int64 transaction_id
,
936 int64 object_store_id
,
937 IndexedDBValue
* value
,
938 ScopedVector
<storage::BlobDataHandle
>* handles
,
939 scoped_ptr
<IndexedDBKey
> key
,
940 blink::WebIDBPutMode put_mode
,
941 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
942 const std::vector
<IndexKeys
>& index_keys
) {
943 IDB_TRACE1("IndexedDBDatabase::Put", "txn.id", transaction_id
);
944 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
947 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
949 if (!ValidateObjectStoreId(object_store_id
))
954 scoped_ptr
<PutOperationParams
> params(new PutOperationParams());
955 params
->object_store_id
= object_store_id
;
956 params
->value
.swap(*value
);
957 params
->handles
.swap(*handles
);
958 params
->key
= key
.Pass();
959 params
->put_mode
= put_mode
;
960 params
->callbacks
= callbacks
;
961 params
->index_keys
= index_keys
;
962 transaction
->ScheduleTask(base::Bind(
963 &IndexedDBDatabase::PutOperation
, this, base::Passed(¶ms
)));
966 void IndexedDBDatabase::PutOperation(scoped_ptr
<PutOperationParams
> params
,
967 IndexedDBTransaction
* transaction
) {
968 IDB_TRACE1("IndexedDBDatabase::PutOperation", "txn.id", transaction
->id());
969 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
970 bool key_was_generated
= false;
972 DCHECK(metadata_
.object_stores
.find(params
->object_store_id
) !=
973 metadata_
.object_stores
.end());
974 const IndexedDBObjectStoreMetadata
& object_store
=
975 metadata_
.object_stores
[params
->object_store_id
];
976 DCHECK(object_store
.auto_increment
|| params
->key
->IsValid());
978 scoped_ptr
<IndexedDBKey
> key
;
979 if (params
->put_mode
!= blink::WebIDBPutModeCursorUpdate
&&
980 object_store
.auto_increment
&& !params
->key
->IsValid()) {
981 scoped_ptr
<IndexedDBKey
> auto_inc_key
= GenerateKey(
982 backing_store_
.get(), transaction
, id(), params
->object_store_id
);
983 key_was_generated
= true;
984 if (!auto_inc_key
->IsValid()) {
985 params
->callbacks
->OnError(
986 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError
,
987 "Maximum key generator value reached."));
990 key
= auto_inc_key
.Pass();
992 key
= params
->key
.Pass();
995 DCHECK(key
->IsValid());
997 IndexedDBBackingStore::RecordIdentifier record_identifier
;
998 if (params
->put_mode
== blink::WebIDBPutModeAddOnly
) {
1000 leveldb::Status s
= backing_store_
->KeyExistsInObjectStore(
1001 transaction
->BackingStoreTransaction(),
1003 params
->object_store_id
,
1008 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1009 "Internal error checking key existence.");
1010 params
->callbacks
->OnError(error
);
1011 if (s
.IsCorruption())
1012 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1017 params
->callbacks
->OnError(
1018 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError
,
1019 "Key already exists in the object store."));
1024 ScopedVector
<IndexWriter
> index_writers
;
1025 base::string16 error_message
;
1026 bool obeys_constraints
= false;
1027 bool backing_store_success
= MakeIndexWriters(transaction
,
1028 backing_store_
.get(),
1036 &obeys_constraints
);
1037 if (!backing_store_success
) {
1038 params
->callbacks
->OnError(IndexedDBDatabaseError(
1039 blink::WebIDBDatabaseExceptionUnknownError
,
1040 "Internal error: backing store error updating index keys."));
1043 if (!obeys_constraints
) {
1044 params
->callbacks
->OnError(IndexedDBDatabaseError(
1045 blink::WebIDBDatabaseExceptionConstraintError
, error_message
));
1049 // Before this point, don't do any mutation. After this point, rollback the
1050 // transaction in case of error.
1052 backing_store_
->PutRecord(transaction
->BackingStoreTransaction(),
1054 params
->object_store_id
,
1058 &record_identifier
);
1060 IndexedDBDatabaseError
error(
1061 blink::WebIDBDatabaseExceptionUnknownError
,
1062 "Internal error: backing store error performing put/add.");
1063 params
->callbacks
->OnError(error
);
1064 if (s
.IsCorruption())
1065 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1070 for (size_t i
= 0; i
< index_writers
.size(); ++i
) {
1071 IndexWriter
* index_writer
= index_writers
[i
];
1072 index_writer
->WriteIndexKeys(record_identifier
,
1073 backing_store_
.get(),
1074 transaction
->BackingStoreTransaction(),
1076 params
->object_store_id
);
1079 if (object_store
.auto_increment
&&
1080 params
->put_mode
!= blink::WebIDBPutModeCursorUpdate
&&
1081 key
->type() == WebIDBKeyTypeNumber
) {
1082 leveldb::Status s
= UpdateKeyGenerator(backing_store_
.get(),
1085 params
->object_store_id
,
1087 !key_was_generated
);
1089 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1090 "Internal error updating key generator.");
1091 params
->callbacks
->OnError(error
);
1092 if (s
.IsCorruption())
1093 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1099 params
->callbacks
->OnSuccess(*key
);
1102 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id
,
1103 int64 object_store_id
,
1104 scoped_ptr
<IndexedDBKey
> primary_key
,
1105 const std::vector
<IndexKeys
>& index_keys
) {
1106 IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction_id
);
1107 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1110 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
1112 // TODO(alecflett): This method could be asynchronous, but we need to
1113 // evaluate if it's worth the extra complexity.
1114 IndexedDBBackingStore::RecordIdentifier record_identifier
;
1116 leveldb::Status s
= backing_store_
->KeyExistsInObjectStore(
1117 transaction
->BackingStoreTransaction(),
1124 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1125 "Internal error setting index keys.");
1126 transaction
->Abort(error
);
1127 if (s
.IsCorruption())
1128 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1133 transaction
->Abort(IndexedDBDatabaseError(
1134 blink::WebIDBDatabaseExceptionUnknownError
,
1135 "Internal error setting index keys for object store."));
1139 ScopedVector
<IndexWriter
> index_writers
;
1140 base::string16 error_message
;
1141 bool obeys_constraints
= false;
1142 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
1143 metadata_
.object_stores
.end());
1144 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
1145 metadata_
.object_stores
[object_store_id
];
1146 bool backing_store_success
= MakeIndexWriters(transaction
,
1147 backing_store_
.get(),
1149 object_store_metadata
,
1155 &obeys_constraints
);
1156 if (!backing_store_success
) {
1157 transaction
->Abort(IndexedDBDatabaseError(
1158 blink::WebIDBDatabaseExceptionUnknownError
,
1159 "Internal error: backing store error updating index keys."));
1162 if (!obeys_constraints
) {
1163 transaction
->Abort(IndexedDBDatabaseError(
1164 blink::WebIDBDatabaseExceptionConstraintError
, error_message
));
1168 for (size_t i
= 0; i
< index_writers
.size(); ++i
) {
1169 IndexWriter
* index_writer
= index_writers
[i
];
1170 index_writer
->WriteIndexKeys(record_identifier
,
1171 backing_store_
.get(),
1172 transaction
->BackingStoreTransaction(),
1178 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id
,
1180 const std::vector
<int64
>& index_ids
) {
1181 IDB_TRACE1("IndexedDBDatabase::SetIndexesReady", "txn.id", transaction_id
);
1182 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1185 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
1187 transaction
->ScheduleTask(
1188 blink::WebIDBTaskTypePreemptive
,
1189 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation
,
1194 void IndexedDBDatabase::SetIndexesReadyOperation(
1196 IndexedDBTransaction
* transaction
) {
1197 IDB_TRACE1("IndexedDBDatabase::SetIndexesReadyOperation",
1200 for (size_t i
= 0; i
< index_count
; ++i
)
1201 transaction
->DidCompletePreemptiveEvent();
1204 struct IndexedDBDatabase::OpenCursorOperationParams
{
1205 OpenCursorOperationParams() {}
1206 int64 object_store_id
;
1208 scoped_ptr
<IndexedDBKeyRange
> key_range
;
1209 blink::WebIDBCursorDirection direction
;
1210 indexed_db::CursorType cursor_type
;
1211 blink::WebIDBTaskType task_type
;
1212 scoped_refptr
<IndexedDBCallbacks
> callbacks
;
1215 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams
);
1218 void IndexedDBDatabase::OpenCursor(
1219 int64 transaction_id
,
1220 int64 object_store_id
,
1222 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1223 blink::WebIDBCursorDirection direction
,
1225 blink::WebIDBTaskType task_type
,
1226 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1227 IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction_id
);
1228 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1232 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
1235 scoped_ptr
<OpenCursorOperationParams
> params(new OpenCursorOperationParams());
1236 params
->object_store_id
= object_store_id
;
1237 params
->index_id
= index_id
;
1238 params
->key_range
= key_range
.Pass();
1239 params
->direction
= direction
;
1240 params
->cursor_type
=
1241 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
;
1242 params
->task_type
= task_type
;
1243 params
->callbacks
= callbacks
;
1244 transaction
->ScheduleTask(base::Bind(
1245 &IndexedDBDatabase::OpenCursorOperation
, this, base::Passed(¶ms
)));
1248 void IndexedDBDatabase::OpenCursorOperation(
1249 scoped_ptr
<OpenCursorOperationParams
> params
,
1250 IndexedDBTransaction
* transaction
) {
1252 "IndexedDBDatabase::OpenCursorOperation", "txn.id", transaction
->id());
1254 // The frontend has begun indexing, so this pauses the transaction
1255 // until the indexing is complete. This can't happen any earlier
1256 // because we don't want to switch to early mode in case multiple
1257 // indexes are being created in a row, with Put()'s in between.
1258 if (params
->task_type
== blink::WebIDBTaskTypePreemptive
)
1259 transaction
->AddPreemptiveEvent();
1262 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
1263 if (params
->index_id
== IndexedDBIndexMetadata::kInvalidId
) {
1264 if (params
->cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
1265 DCHECK_EQ(params
->task_type
, blink::WebIDBTaskTypeNormal
);
1266 backing_store_cursor
= backing_store_
->OpenObjectStoreKeyCursor(
1267 transaction
->BackingStoreTransaction(),
1269 params
->object_store_id
,
1274 backing_store_cursor
= backing_store_
->OpenObjectStoreCursor(
1275 transaction
->BackingStoreTransaction(),
1277 params
->object_store_id
,
1283 DCHECK_EQ(params
->task_type
, blink::WebIDBTaskTypeNormal
);
1284 if (params
->cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
1285 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
1286 transaction
->BackingStoreTransaction(),
1288 params
->object_store_id
,
1294 backing_store_cursor
= backing_store_
->OpenIndexCursor(
1295 transaction
->BackingStoreTransaction(),
1297 params
->object_store_id
,
1306 DLOG(ERROR
) << "Unable to open cursor operation: " << s
.ToString();
1307 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1308 "Internal error opening cursor operation");
1309 if (s
.IsCorruption()) {
1310 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1315 if (!backing_store_cursor
) {
1316 // Why is Success being called?
1317 params
->callbacks
->OnSuccess(nullptr);
1321 scoped_refptr
<IndexedDBCursor
> cursor
=
1322 new IndexedDBCursor(backing_store_cursor
.Pass(),
1323 params
->cursor_type
,
1326 params
->callbacks
->OnSuccess(
1327 cursor
, cursor
->key(), cursor
->primary_key(), cursor
->Value());
1330 void IndexedDBDatabase::Count(int64 transaction_id
,
1331 int64 object_store_id
,
1333 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1334 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1335 IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction_id
);
1336 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1340 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
1343 transaction
->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation
,
1347 base::Passed(&key_range
),
1351 void IndexedDBDatabase::CountOperation(
1352 int64 object_store_id
,
1354 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1355 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1356 IndexedDBTransaction
* transaction
) {
1357 IDB_TRACE1("IndexedDBDatabase::CountOperation", "txn.id", transaction
->id());
1359 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
1362 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
1363 backing_store_cursor
= backing_store_
->OpenObjectStoreKeyCursor(
1364 transaction
->BackingStoreTransaction(),
1368 blink::WebIDBCursorDirectionNext
,
1371 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
1372 transaction
->BackingStoreTransaction(),
1377 blink::WebIDBCursorDirectionNext
,
1381 DLOG(ERROR
) << "Unable perform count operation: " << s
.ToString();
1382 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1383 "Internal error performing count operation");
1384 if (s
.IsCorruption()) {
1385 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1389 if (!backing_store_cursor
) {
1390 callbacks
->OnSuccess(count
);
1396 } while (backing_store_cursor
->Continue(&s
));
1398 // TODO(cmumford): Check for database corruption.
1400 callbacks
->OnSuccess(count
);
1403 void IndexedDBDatabase::DeleteRange(
1404 int64 transaction_id
,
1405 int64 object_store_id
,
1406 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1407 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1408 IDB_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction_id
);
1409 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1412 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
1414 if (!ValidateObjectStoreId(object_store_id
))
1417 transaction
->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation
,
1420 base::Passed(&key_range
),
1424 void IndexedDBDatabase::DeleteRangeOperation(
1425 int64 object_store_id
,
1426 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1427 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1428 IndexedDBTransaction
* transaction
) {
1430 "IndexedDBDatabase::DeleteRangeOperation", "txn.id", transaction
->id());
1432 backing_store_
->DeleteRange(transaction
->BackingStoreTransaction(),
1437 base::string16 error_string
=
1438 ASCIIToUTF16("Internal error deleting data in range");
1439 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1441 transaction
->Abort(error
);
1442 if (s
.IsCorruption()) {
1443 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1448 callbacks
->OnSuccess();
1451 void IndexedDBDatabase::Clear(int64 transaction_id
,
1452 int64 object_store_id
,
1453 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1454 IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction_id
);
1455 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1458 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
1460 if (!ValidateObjectStoreId(object_store_id
))
1463 transaction
->ScheduleTask(base::Bind(
1464 &IndexedDBDatabase::ClearOperation
, this, object_store_id
, callbacks
));
1467 void IndexedDBDatabase::ClearOperation(
1468 int64 object_store_id
,
1469 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1470 IndexedDBTransaction
* transaction
) {
1471 IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction
->id());
1472 leveldb::Status s
= backing_store_
->ClearObjectStore(
1473 transaction
->BackingStoreTransaction(), id(), object_store_id
);
1475 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1476 "Internal error clearing object store");
1477 callbacks
->OnError(error
);
1478 if (s
.IsCorruption()) {
1479 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1484 callbacks
->OnSuccess();
1487 void IndexedDBDatabase::DeleteObjectStoreOperation(
1488 int64 object_store_id
,
1489 IndexedDBTransaction
* transaction
) {
1490 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreOperation",
1494 const IndexedDBObjectStoreMetadata object_store_metadata
=
1495 metadata_
.object_stores
[object_store_id
];
1497 backing_store_
->DeleteObjectStore(transaction
->BackingStoreTransaction(),
1498 transaction
->database()->id(),
1501 base::string16 error_string
=
1502 ASCIIToUTF16("Internal error deleting object store '") +
1503 object_store_metadata
.name
+ ASCIIToUTF16("'.");
1504 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1506 transaction
->Abort(error
);
1507 if (s
.IsCorruption())
1508 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1513 RemoveObjectStore(object_store_id
);
1514 transaction
->ScheduleAbortTask(
1515 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation
,
1517 object_store_metadata
));
1520 void IndexedDBDatabase::VersionChangeOperation(
1522 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1523 scoped_ptr
<IndexedDBConnection
> connection
,
1524 IndexedDBTransaction
* transaction
) {
1526 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction
->id());
1527 int64 old_version
= metadata_
.int_version
;
1528 DCHECK_GT(version
, old_version
);
1530 if (!backing_store_
->UpdateIDBDatabaseIntVersion(
1531 transaction
->BackingStoreTransaction(), id(), version
)) {
1532 IndexedDBDatabaseError
error(
1533 blink::WebIDBDatabaseExceptionUnknownError
,
1535 "Internal error writing data to stable storage when "
1536 "updating version."));
1537 callbacks
->OnError(error
);
1538 transaction
->Abort(error
);
1542 transaction
->ScheduleAbortTask(
1543 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation
,
1546 metadata_
.int_version
));
1547 metadata_
.int_version
= version
;
1548 metadata_
.version
= kNoStringVersion
;
1550 DCHECK(!pending_second_half_open_
);
1551 pending_second_half_open_
.reset(
1552 new PendingSuccessCall(callbacks
, connection
.get(), version
));
1553 callbacks
->OnUpgradeNeeded(old_version
, connection
.Pass(), metadata());
1556 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction
* transaction
,
1558 DCHECK(transactions_
.find(transaction
->id()) != transactions_
.end());
1559 DCHECK_EQ(transactions_
[transaction
->id()], transaction
);
1560 transactions_
.erase(transaction
->id());
1562 if (transaction
->mode() == blink::WebIDBTransactionModeVersionChange
) {
1563 if (pending_second_half_open_
) {
1565 DCHECK_EQ(pending_second_half_open_
->version(), metadata_
.int_version
);
1566 DCHECK(metadata_
.id
!= kInvalidId
);
1568 // Connection was already minted for OnUpgradeNeeded callback.
1569 scoped_ptr
<IndexedDBConnection
> connection
;
1570 pending_second_half_open_
->callbacks()->OnSuccess(connection
.Pass(),
1573 pending_second_half_open_
->callbacks()->OnError(
1574 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError
,
1575 "Version change transaction was aborted in "
1576 "upgradeneeded event handler."));
1578 pending_second_half_open_
.reset();
1581 // Connection queue is now unblocked.
1582 ProcessPendingCalls();
1586 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status
& status
) {
1587 if (status
.IsCorruption()) {
1588 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1589 "Error committing transaction");
1590 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(), error
);
1592 factory_
->HandleBackingStoreFailure(backing_store_
->origin_url());
1596 size_t IndexedDBDatabase::ConnectionCount() const {
1597 // This does not include pending open calls, as those should not block version
1598 // changes and deletes.
1599 return connections_
.size();
1602 size_t IndexedDBDatabase::PendingOpenCount() const {
1603 return pending_open_calls_
.size();
1606 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1607 return pending_run_version_change_transaction_call_
? 1 : 0;
1610 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1611 return pending_second_half_open_
? 1 : 0;
1614 size_t IndexedDBDatabase::PendingDeleteCount() const {
1615 return pending_delete_calls_
.size();
1618 void IndexedDBDatabase::ProcessPendingCalls() {
1619 if (pending_run_version_change_transaction_call_
&& ConnectionCount() == 1) {
1620 DCHECK(pending_run_version_change_transaction_call_
->version() >
1621 metadata_
.int_version
);
1622 scoped_ptr
<PendingUpgradeCall
> pending_call
=
1623 pending_run_version_change_transaction_call_
.Pass();
1624 RunVersionChangeTransactionFinal(pending_call
->callbacks(),
1625 pending_call
->ReleaseConnection(),
1626 pending_call
->transaction_id(),
1627 pending_call
->version());
1628 DCHECK_EQ(1u, ConnectionCount());
1629 // Fall through would be a no-op, since transaction must complete
1631 DCHECK(IsDeleteDatabaseBlocked());
1632 DCHECK(IsOpenConnectionBlocked());
1636 if (!IsDeleteDatabaseBlocked()) {
1637 PendingDeleteCallList pending_delete_calls
;
1638 pending_delete_calls_
.swap(pending_delete_calls
);
1639 while (!pending_delete_calls
.empty()) {
1640 // Only the first delete call will delete the database, but each must fire
1642 scoped_ptr
<PendingDeleteCall
> pending_delete_call(
1643 pending_delete_calls
.front());
1644 pending_delete_calls
.pop_front();
1645 DeleteDatabaseFinal(pending_delete_call
->callbacks());
1647 // delete_database_final should never re-queue calls.
1648 DCHECK(pending_delete_calls_
.empty());
1649 // Fall through when complete, as pending opens may be unblocked.
1652 if (!IsOpenConnectionBlocked()) {
1653 PendingOpenCallList pending_open_calls
;
1654 pending_open_calls_
.swap(pending_open_calls
);
1655 while (!pending_open_calls
.empty()) {
1656 OpenConnection(pending_open_calls
.front());
1657 pending_open_calls
.pop_front();
1662 void IndexedDBDatabase::CreateTransaction(
1663 int64 transaction_id
,
1664 IndexedDBConnection
* connection
,
1665 const std::vector
<int64
>& object_store_ids
,
1666 blink::WebIDBTransactionMode mode
) {
1667 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id
);
1668 DCHECK(connections_
.count(connection
));
1669 DCHECK(transactions_
.find(transaction_id
) == transactions_
.end());
1670 if (transactions_
.find(transaction_id
) != transactions_
.end())
1673 // The transaction will add itself to this database's coordinator, which
1674 // manages the lifetime of the object.
1675 TransactionCreated(IndexedDBClassFactory::Get()->CreateIndexedDBTransaction(
1676 transaction_id
, connection
->callbacks(),
1677 std::set
<int64
>(object_store_ids
.begin(), object_store_ids
.end()), mode
,
1678 this, new IndexedDBBackingStore::Transaction(backing_store_
.get())));
1681 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction
* transaction
) {
1682 transactions_
[transaction
->id()] = transaction
;
1685 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1686 return !pending_delete_calls_
.empty() ||
1687 transaction_coordinator_
.IsRunningVersionChangeTransaction() ||
1688 pending_run_version_change_transaction_call_
;
1691 void IndexedDBDatabase::OpenConnection(
1692 const IndexedDBPendingConnection
& connection
) {
1693 DCHECK(backing_store_
.get());
1695 // TODO(jsbell): Should have a priority queue so that higher version
1696 // requests are processed first. http://crbug.com/225850
1697 if (IsOpenConnectionBlocked()) {
1698 // The backing store only detects data loss when it is first opened. The
1699 // presence of existing connections means we didn't even check for data loss
1700 // so there'd better not be any.
1701 DCHECK_NE(blink::WebIDBDataLossTotal
, connection
.callbacks
->data_loss());
1702 pending_open_calls_
.push_back(connection
);
1706 if (metadata_
.id
== kInvalidId
) {
1707 // The database was deleted then immediately re-opened; OpenInternal()
1708 // recreates it in the backing store.
1709 if (OpenInternal().ok()) {
1710 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION
,
1711 metadata_
.int_version
);
1713 base::string16 message
;
1714 if (connection
.version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
) {
1715 message
= ASCIIToUTF16(
1716 "Internal error opening database with no version specified.");
1719 ASCIIToUTF16("Internal error opening database with version ") +
1720 Int64ToString16(connection
.version
);
1722 connection
.callbacks
->OnError(IndexedDBDatabaseError(
1723 blink::WebIDBDatabaseExceptionUnknownError
, message
));
1728 // We infer that the database didn't exist from its lack of either type of
1730 bool is_new_database
=
1731 metadata_
.version
== kNoStringVersion
&&
1732 metadata_
.int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1734 if (connection
.version
== IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
) {
1735 // For unit tests only - skip upgrade steps. Calling from script with
1736 // DEFAULT_INT_VERSION throws exception.
1737 // TODO(jsbell): DCHECK that not in unit tests.
1738 DCHECK(is_new_database
);
1739 connection
.callbacks
->OnSuccess(
1740 CreateConnection(connection
.database_callbacks
,
1741 connection
.child_process_id
),
1746 // We may need to change the version.
1747 int64 local_version
= connection
.version
;
1748 if (local_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
) {
1749 if (!is_new_database
) {
1750 connection
.callbacks
->OnSuccess(
1751 CreateConnection(connection
.database_callbacks
,
1752 connection
.child_process_id
),
1756 // Spec says: If no version is specified and no database exists, set
1757 // database version to 1.
1761 if (local_version
> metadata_
.int_version
) {
1762 RunVersionChangeTransaction(connection
.callbacks
,
1763 CreateConnection(connection
.database_callbacks
,
1764 connection
.child_process_id
),
1765 connection
.transaction_id
,
1769 if (local_version
< metadata_
.int_version
) {
1770 connection
.callbacks
->OnError(IndexedDBDatabaseError(
1771 blink::WebIDBDatabaseExceptionVersionError
,
1772 ASCIIToUTF16("The requested version (") +
1773 Int64ToString16(local_version
) +
1774 ASCIIToUTF16(") is less than the existing version (") +
1775 Int64ToString16(metadata_
.int_version
) + ASCIIToUTF16(").")));
1778 DCHECK_EQ(local_version
, metadata_
.int_version
);
1779 connection
.callbacks
->OnSuccess(
1780 CreateConnection(connection
.database_callbacks
,
1781 connection
.child_process_id
),
1785 void IndexedDBDatabase::RunVersionChangeTransaction(
1786 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1787 scoped_ptr
<IndexedDBConnection
> connection
,
1788 int64 transaction_id
,
1789 int64 requested_version
) {
1790 DCHECK(callbacks
.get());
1791 DCHECK(connections_
.count(connection
.get()));
1792 if (ConnectionCount() > 1) {
1793 DCHECK_NE(blink::WebIDBDataLossTotal
, callbacks
->data_loss());
1794 // Front end ensures the event is not fired at connections that have
1795 // close_pending set.
1796 for (const auto* iter
: connections_
) {
1797 if (iter
!= connection
.get()) {
1798 iter
->callbacks()->OnVersionChange(metadata_
.int_version
,
1802 // OnBlocked will be fired at the request when one of the other
1803 // connections acks that the OnVersionChange was ignored.
1805 DCHECK(!pending_run_version_change_transaction_call_
);
1806 pending_run_version_change_transaction_call_
.reset(new PendingUpgradeCall(
1807 callbacks
, connection
.Pass(), transaction_id
, requested_version
));
1810 RunVersionChangeTransactionFinal(
1811 callbacks
, connection
.Pass(), transaction_id
, requested_version
);
1814 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1815 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1816 scoped_ptr
<IndexedDBConnection
> connection
,
1817 int64 transaction_id
,
1818 int64 requested_version
) {
1820 std::vector
<int64
> object_store_ids
;
1821 CreateTransaction(transaction_id
,
1824 blink::WebIDBTransactionModeVersionChange
);
1826 transactions_
[transaction_id
]->ScheduleTask(
1827 base::Bind(&IndexedDBDatabase::VersionChangeOperation
,
1831 base::Passed(&connection
)));
1832 DCHECK(!pending_second_half_open_
);
1835 void IndexedDBDatabase::DeleteDatabase(
1836 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1838 if (IsDeleteDatabaseBlocked()) {
1839 for (const auto* connection
: connections_
) {
1840 // Front end ensures the event is not fired at connections that have
1841 // close_pending set.
1842 connection
->callbacks()->OnVersionChange(
1843 metadata_
.int_version
, IndexedDBDatabaseMetadata::NO_INT_VERSION
);
1845 // OnBlocked will be fired at the request when one of the other
1846 // connections acks that the OnVersionChange was ignored.
1848 pending_delete_calls_
.push_back(new PendingDeleteCall(callbacks
));
1851 DeleteDatabaseFinal(callbacks
);
1854 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1855 return !!ConnectionCount();
1858 void IndexedDBDatabase::DeleteDatabaseFinal(
1859 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1860 DCHECK(!IsDeleteDatabaseBlocked());
1861 DCHECK(backing_store_
.get());
1862 leveldb::Status s
= backing_store_
->DeleteDatabase(metadata_
.name
);
1864 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1865 "Internal error deleting database.");
1866 callbacks
->OnError(error
);
1867 if (s
.IsCorruption()) {
1868 GURL origin_url
= backing_store_
->origin_url();
1869 backing_store_
= NULL
;
1870 factory_
->HandleBackingStoreCorruption(origin_url
, error
);
1874 int64 old_version
= metadata_
.int_version
;
1875 metadata_
.version
= kNoStringVersion
;
1876 metadata_
.id
= kInvalidId
;
1877 metadata_
.int_version
= IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1878 metadata_
.object_stores
.clear();
1879 callbacks
->OnSuccess(old_version
);
1880 factory_
->DatabaseDeleted(identifier_
);
1883 void IndexedDBDatabase::ForceClose() {
1884 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1885 scoped_refptr
<IndexedDBDatabase
> protect(this);
1886 ConnectionSet::const_iterator it
= connections_
.begin();
1887 while (it
!= connections_
.end()) {
1888 IndexedDBConnection
* connection
= *it
++;
1889 connection
->ForceClose();
1891 DCHECK(connections_
.empty());
1894 void IndexedDBDatabase::VersionChangeIgnored() {
1895 if (pending_run_version_change_transaction_call_
)
1896 pending_run_version_change_transaction_call_
->callbacks()->OnBlocked(
1897 metadata_
.int_version
);
1899 for (const auto& pending_delete_call
: pending_delete_calls_
)
1900 pending_delete_call
->callbacks()->OnBlocked(metadata_
.int_version
);
1904 void IndexedDBDatabase::Close(IndexedDBConnection
* connection
, bool forced
) {
1905 DCHECK(connections_
.count(connection
));
1906 DCHECK(connection
->IsConnected());
1907 DCHECK(connection
->database() == this);
1909 IDB_TRACE("IndexedDBDatabase::Close");
1911 connections_
.erase(connection
);
1913 // Abort outstanding transactions from the closing connection. This
1914 // can not happen if the close is requested by the connection itself
1915 // as the front-end defers the close until all transactions are
1916 // complete, but can occur on process termination or forced close.
1918 TransactionMap
transactions(transactions_
);
1919 for (const auto& it
: transactions
) {
1920 if (it
.second
->connection() == connection
->callbacks())
1922 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
,
1923 "Connection is closing."));
1927 if (pending_second_half_open_
&&
1928 pending_second_half_open_
->connection() == connection
) {
1929 pending_second_half_open_
->callbacks()->OnError(
1930 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError
,
1931 "The connection was closed."));
1932 pending_second_half_open_
.reset();
1935 ProcessPendingCalls();
1937 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1938 if (!ConnectionCount() && !pending_open_calls_
.size() &&
1939 !pending_delete_calls_
.size()) {
1940 DCHECK(transactions_
.empty());
1942 const GURL origin_url
= backing_store_
->origin_url();
1943 backing_store_
= NULL
;
1945 factory_
->ReleaseDatabase(identifier_
, forced
);
1949 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1950 int64 object_store_id
,
1951 IndexedDBTransaction
* transaction
) {
1952 DCHECK(!transaction
);
1953 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
1954 RemoveObjectStore(object_store_id
);
1957 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1958 const IndexedDBObjectStoreMetadata
& object_store_metadata
,
1959 IndexedDBTransaction
* transaction
) {
1960 DCHECK(!transaction
);
1961 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation");
1962 AddObjectStore(object_store_metadata
,
1963 IndexedDBObjectStoreMetadata::kInvalidId
);
1966 void IndexedDBDatabase::VersionChangeAbortOperation(
1967 const base::string16
& previous_version
,
1968 int64 previous_int_version
,
1969 IndexedDBTransaction
* transaction
) {
1970 DCHECK(!transaction
);
1971 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
1972 metadata_
.version
= previous_version
;
1973 metadata_
.int_version
= previous_int_version
;
1976 } // namespace content