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 IDB_TRACE1("IndexedDBDatabase::PutOperation.UpdateIndexes", "txn.id",
1072 for (size_t i
= 0; i
< index_writers
.size(); ++i
) {
1073 IndexWriter
* index_writer
= index_writers
[i
];
1074 index_writer
->WriteIndexKeys(record_identifier
, backing_store_
.get(),
1075 transaction
->BackingStoreTransaction(), id(),
1076 params
->object_store_id
);
1080 if (object_store
.auto_increment
&&
1081 params
->put_mode
!= blink::WebIDBPutModeCursorUpdate
&&
1082 key
->type() == WebIDBKeyTypeNumber
) {
1083 IDB_TRACE1("IndexedDBDatabase::PutOperation.AutoIncrement", "txn.id",
1085 leveldb::Status s
= UpdateKeyGenerator(backing_store_
.get(),
1088 params
->object_store_id
,
1090 !key_was_generated
);
1092 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1093 "Internal error updating key generator.");
1094 params
->callbacks
->OnError(error
);
1095 if (s
.IsCorruption())
1096 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1102 IDB_TRACE1("IndexedDBDatabase::PutOperation.Callbacks", "txn.id",
1104 params
->callbacks
->OnSuccess(*key
);
1108 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id
,
1109 int64 object_store_id
,
1110 scoped_ptr
<IndexedDBKey
> primary_key
,
1111 const std::vector
<IndexKeys
>& index_keys
) {
1112 IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction_id
);
1113 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1116 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
1118 // TODO(alecflett): This method could be asynchronous, but we need to
1119 // evaluate if it's worth the extra complexity.
1120 IndexedDBBackingStore::RecordIdentifier record_identifier
;
1122 leveldb::Status s
= backing_store_
->KeyExistsInObjectStore(
1123 transaction
->BackingStoreTransaction(),
1130 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1131 "Internal error setting index keys.");
1132 transaction
->Abort(error
);
1133 if (s
.IsCorruption())
1134 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1139 transaction
->Abort(IndexedDBDatabaseError(
1140 blink::WebIDBDatabaseExceptionUnknownError
,
1141 "Internal error setting index keys for object store."));
1145 ScopedVector
<IndexWriter
> index_writers
;
1146 base::string16 error_message
;
1147 bool obeys_constraints
= false;
1148 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
1149 metadata_
.object_stores
.end());
1150 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
1151 metadata_
.object_stores
[object_store_id
];
1152 bool backing_store_success
= MakeIndexWriters(transaction
,
1153 backing_store_
.get(),
1155 object_store_metadata
,
1161 &obeys_constraints
);
1162 if (!backing_store_success
) {
1163 transaction
->Abort(IndexedDBDatabaseError(
1164 blink::WebIDBDatabaseExceptionUnknownError
,
1165 "Internal error: backing store error updating index keys."));
1168 if (!obeys_constraints
) {
1169 transaction
->Abort(IndexedDBDatabaseError(
1170 blink::WebIDBDatabaseExceptionConstraintError
, error_message
));
1174 for (size_t i
= 0; i
< index_writers
.size(); ++i
) {
1175 IndexWriter
* index_writer
= index_writers
[i
];
1176 index_writer
->WriteIndexKeys(record_identifier
,
1177 backing_store_
.get(),
1178 transaction
->BackingStoreTransaction(),
1184 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id
,
1186 const std::vector
<int64
>& index_ids
) {
1187 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1190 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
1192 transaction
->ScheduleTask(
1193 blink::WebIDBTaskTypePreemptive
,
1194 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation
,
1199 void IndexedDBDatabase::SetIndexesReadyOperation(
1201 IndexedDBTransaction
* transaction
) {
1202 for (size_t i
= 0; i
< index_count
; ++i
)
1203 transaction
->DidCompletePreemptiveEvent();
1206 struct IndexedDBDatabase::OpenCursorOperationParams
{
1207 OpenCursorOperationParams() {}
1208 int64 object_store_id
;
1210 scoped_ptr
<IndexedDBKeyRange
> key_range
;
1211 blink::WebIDBCursorDirection direction
;
1212 indexed_db::CursorType cursor_type
;
1213 blink::WebIDBTaskType task_type
;
1214 scoped_refptr
<IndexedDBCallbacks
> callbacks
;
1217 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams
);
1220 void IndexedDBDatabase::OpenCursor(
1221 int64 transaction_id
,
1222 int64 object_store_id
,
1224 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1225 blink::WebIDBCursorDirection direction
,
1227 blink::WebIDBTaskType task_type
,
1228 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1229 IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction_id
);
1230 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1234 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
1237 scoped_ptr
<OpenCursorOperationParams
> params(new OpenCursorOperationParams());
1238 params
->object_store_id
= object_store_id
;
1239 params
->index_id
= index_id
;
1240 params
->key_range
= key_range
.Pass();
1241 params
->direction
= direction
;
1242 params
->cursor_type
=
1243 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
;
1244 params
->task_type
= task_type
;
1245 params
->callbacks
= callbacks
;
1246 transaction
->ScheduleTask(base::Bind(
1247 &IndexedDBDatabase::OpenCursorOperation
, this, base::Passed(¶ms
)));
1250 void IndexedDBDatabase::OpenCursorOperation(
1251 scoped_ptr
<OpenCursorOperationParams
> params
,
1252 IndexedDBTransaction
* transaction
) {
1254 "IndexedDBDatabase::OpenCursorOperation", "txn.id", transaction
->id());
1256 // The frontend has begun indexing, so this pauses the transaction
1257 // until the indexing is complete. This can't happen any earlier
1258 // because we don't want to switch to early mode in case multiple
1259 // indexes are being created in a row, with Put()'s in between.
1260 if (params
->task_type
== blink::WebIDBTaskTypePreemptive
)
1261 transaction
->AddPreemptiveEvent();
1264 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
1265 if (params
->index_id
== IndexedDBIndexMetadata::kInvalidId
) {
1266 if (params
->cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
1267 DCHECK_EQ(params
->task_type
, blink::WebIDBTaskTypeNormal
);
1268 backing_store_cursor
= backing_store_
->OpenObjectStoreKeyCursor(
1269 transaction
->BackingStoreTransaction(),
1271 params
->object_store_id
,
1276 backing_store_cursor
= backing_store_
->OpenObjectStoreCursor(
1277 transaction
->BackingStoreTransaction(),
1279 params
->object_store_id
,
1285 DCHECK_EQ(params
->task_type
, blink::WebIDBTaskTypeNormal
);
1286 if (params
->cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
1287 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
1288 transaction
->BackingStoreTransaction(),
1290 params
->object_store_id
,
1296 backing_store_cursor
= backing_store_
->OpenIndexCursor(
1297 transaction
->BackingStoreTransaction(),
1299 params
->object_store_id
,
1308 DLOG(ERROR
) << "Unable to open cursor operation: " << s
.ToString();
1309 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1310 "Internal error opening cursor operation");
1311 if (s
.IsCorruption()) {
1312 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1317 if (!backing_store_cursor
) {
1318 // Why is Success being called?
1319 params
->callbacks
->OnSuccess(nullptr);
1323 scoped_refptr
<IndexedDBCursor
> cursor
=
1324 new IndexedDBCursor(backing_store_cursor
.Pass(),
1325 params
->cursor_type
,
1328 params
->callbacks
->OnSuccess(
1329 cursor
, cursor
->key(), cursor
->primary_key(), cursor
->Value());
1332 void IndexedDBDatabase::Count(int64 transaction_id
,
1333 int64 object_store_id
,
1335 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1336 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1337 IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction_id
);
1338 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1342 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
1345 transaction
->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation
,
1349 base::Passed(&key_range
),
1353 void IndexedDBDatabase::CountOperation(
1354 int64 object_store_id
,
1356 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1357 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1358 IndexedDBTransaction
* transaction
) {
1359 IDB_TRACE1("IndexedDBDatabase::CountOperation", "txn.id", transaction
->id());
1361 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
1364 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
1365 backing_store_cursor
= backing_store_
->OpenObjectStoreKeyCursor(
1366 transaction
->BackingStoreTransaction(),
1370 blink::WebIDBCursorDirectionNext
,
1373 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
1374 transaction
->BackingStoreTransaction(),
1379 blink::WebIDBCursorDirectionNext
,
1383 DLOG(ERROR
) << "Unable perform count operation: " << s
.ToString();
1384 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1385 "Internal error performing count operation");
1386 if (s
.IsCorruption()) {
1387 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1391 if (!backing_store_cursor
) {
1392 callbacks
->OnSuccess(count
);
1398 } while (backing_store_cursor
->Continue(&s
));
1400 // TODO(cmumford): Check for database corruption.
1402 callbacks
->OnSuccess(count
);
1405 void IndexedDBDatabase::DeleteRange(
1406 int64 transaction_id
,
1407 int64 object_store_id
,
1408 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1409 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1410 IDB_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction_id
);
1411 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1414 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
1416 if (!ValidateObjectStoreId(object_store_id
))
1419 transaction
->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation
,
1422 base::Passed(&key_range
),
1426 void IndexedDBDatabase::DeleteRangeOperation(
1427 int64 object_store_id
,
1428 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1429 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1430 IndexedDBTransaction
* transaction
) {
1432 "IndexedDBDatabase::DeleteRangeOperation", "txn.id", transaction
->id());
1434 backing_store_
->DeleteRange(transaction
->BackingStoreTransaction(),
1439 base::string16 error_string
=
1440 ASCIIToUTF16("Internal error deleting data in range");
1441 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1443 transaction
->Abort(error
);
1444 if (s
.IsCorruption()) {
1445 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1450 callbacks
->OnSuccess();
1453 void IndexedDBDatabase::Clear(int64 transaction_id
,
1454 int64 object_store_id
,
1455 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1456 IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction_id
);
1457 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1460 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
1462 if (!ValidateObjectStoreId(object_store_id
))
1465 transaction
->ScheduleTask(base::Bind(
1466 &IndexedDBDatabase::ClearOperation
, this, object_store_id
, callbacks
));
1469 void IndexedDBDatabase::ClearOperation(
1470 int64 object_store_id
,
1471 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1472 IndexedDBTransaction
* transaction
) {
1473 IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction
->id());
1474 leveldb::Status s
= backing_store_
->ClearObjectStore(
1475 transaction
->BackingStoreTransaction(), id(), object_store_id
);
1477 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1478 "Internal error clearing object store");
1479 callbacks
->OnError(error
);
1480 if (s
.IsCorruption()) {
1481 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1486 callbacks
->OnSuccess();
1489 void IndexedDBDatabase::DeleteObjectStoreOperation(
1490 int64 object_store_id
,
1491 IndexedDBTransaction
* transaction
) {
1492 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreOperation",
1496 const IndexedDBObjectStoreMetadata object_store_metadata
=
1497 metadata_
.object_stores
[object_store_id
];
1499 backing_store_
->DeleteObjectStore(transaction
->BackingStoreTransaction(),
1500 transaction
->database()->id(),
1503 base::string16 error_string
=
1504 ASCIIToUTF16("Internal error deleting object store '") +
1505 object_store_metadata
.name
+ ASCIIToUTF16("'.");
1506 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1508 transaction
->Abort(error
);
1509 if (s
.IsCorruption())
1510 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1515 RemoveObjectStore(object_store_id
);
1516 transaction
->ScheduleAbortTask(
1517 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation
,
1519 object_store_metadata
));
1522 void IndexedDBDatabase::VersionChangeOperation(
1524 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1525 scoped_ptr
<IndexedDBConnection
> connection
,
1526 IndexedDBTransaction
* transaction
) {
1528 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction
->id());
1529 int64 old_version
= metadata_
.int_version
;
1530 DCHECK_GT(version
, old_version
);
1532 if (!backing_store_
->UpdateIDBDatabaseIntVersion(
1533 transaction
->BackingStoreTransaction(), id(), version
)) {
1534 IndexedDBDatabaseError
error(
1535 blink::WebIDBDatabaseExceptionUnknownError
,
1537 "Internal error writing data to stable storage when "
1538 "updating version."));
1539 callbacks
->OnError(error
);
1540 transaction
->Abort(error
);
1544 transaction
->ScheduleAbortTask(
1545 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation
,
1548 metadata_
.int_version
));
1549 metadata_
.int_version
= version
;
1550 metadata_
.version
= kNoStringVersion
;
1552 DCHECK(!pending_second_half_open_
);
1553 pending_second_half_open_
.reset(
1554 new PendingSuccessCall(callbacks
, connection
.get(), version
));
1555 callbacks
->OnUpgradeNeeded(old_version
, connection
.Pass(), metadata());
1558 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction
* transaction
,
1560 IDB_TRACE1("IndexedDBTransaction::TransactionFinished", "txn.id", id());
1561 DCHECK(transactions_
.find(transaction
->id()) != transactions_
.end());
1562 DCHECK_EQ(transactions_
[transaction
->id()], transaction
);
1563 transactions_
.erase(transaction
->id());
1565 if (transaction
->mode() == blink::WebIDBTransactionModeVersionChange
) {
1566 if (pending_second_half_open_
) {
1568 DCHECK_EQ(pending_second_half_open_
->version(), metadata_
.int_version
);
1569 DCHECK(metadata_
.id
!= kInvalidId
);
1571 // Connection was already minted for OnUpgradeNeeded callback.
1572 scoped_ptr
<IndexedDBConnection
> connection
;
1573 pending_second_half_open_
->callbacks()->OnSuccess(connection
.Pass(),
1576 pending_second_half_open_
->callbacks()->OnError(
1577 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError
,
1578 "Version change transaction was aborted in "
1579 "upgradeneeded event handler."));
1581 pending_second_half_open_
.reset();
1584 // Connection queue is now unblocked.
1585 ProcessPendingCalls();
1589 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status
& status
) {
1590 if (status
.IsCorruption()) {
1591 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1592 "Error committing transaction");
1593 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(), error
);
1595 factory_
->HandleBackingStoreFailure(backing_store_
->origin_url());
1599 size_t IndexedDBDatabase::ConnectionCount() const {
1600 // This does not include pending open calls, as those should not block version
1601 // changes and deletes.
1602 return connections_
.size();
1605 size_t IndexedDBDatabase::PendingOpenCount() const {
1606 return pending_open_calls_
.size();
1609 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1610 return pending_run_version_change_transaction_call_
? 1 : 0;
1613 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1614 return pending_second_half_open_
? 1 : 0;
1617 size_t IndexedDBDatabase::PendingDeleteCount() const {
1618 return pending_delete_calls_
.size();
1621 void IndexedDBDatabase::ProcessPendingCalls() {
1622 if (pending_run_version_change_transaction_call_
&& ConnectionCount() == 1) {
1623 DCHECK(pending_run_version_change_transaction_call_
->version() >
1624 metadata_
.int_version
);
1625 scoped_ptr
<PendingUpgradeCall
> pending_call
=
1626 pending_run_version_change_transaction_call_
.Pass();
1627 RunVersionChangeTransactionFinal(pending_call
->callbacks(),
1628 pending_call
->ReleaseConnection(),
1629 pending_call
->transaction_id(),
1630 pending_call
->version());
1631 DCHECK_EQ(1u, ConnectionCount());
1632 // Fall through would be a no-op, since transaction must complete
1634 DCHECK(IsDeleteDatabaseBlocked());
1635 DCHECK(IsOpenConnectionBlocked());
1639 if (!IsDeleteDatabaseBlocked()) {
1640 PendingDeleteCallList pending_delete_calls
;
1641 pending_delete_calls_
.swap(pending_delete_calls
);
1642 while (!pending_delete_calls
.empty()) {
1643 // Only the first delete call will delete the database, but each must fire
1645 scoped_ptr
<PendingDeleteCall
> pending_delete_call(
1646 pending_delete_calls
.front());
1647 pending_delete_calls
.pop_front();
1648 DeleteDatabaseFinal(pending_delete_call
->callbacks());
1650 // delete_database_final should never re-queue calls.
1651 DCHECK(pending_delete_calls_
.empty());
1652 // Fall through when complete, as pending opens may be unblocked.
1655 if (!IsOpenConnectionBlocked()) {
1656 PendingOpenCallList pending_open_calls
;
1657 pending_open_calls_
.swap(pending_open_calls
);
1658 while (!pending_open_calls
.empty()) {
1659 OpenConnection(pending_open_calls
.front());
1660 pending_open_calls
.pop_front();
1665 void IndexedDBDatabase::CreateTransaction(
1666 int64 transaction_id
,
1667 IndexedDBConnection
* connection
,
1668 const std::vector
<int64
>& object_store_ids
,
1669 blink::WebIDBTransactionMode mode
) {
1670 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id
);
1671 DCHECK(connections_
.count(connection
));
1672 DCHECK(transactions_
.find(transaction_id
) == transactions_
.end());
1673 if (transactions_
.find(transaction_id
) != transactions_
.end())
1676 // The transaction will add itself to this database's coordinator, which
1677 // manages the lifetime of the object.
1678 TransactionCreated(IndexedDBClassFactory::Get()->CreateIndexedDBTransaction(
1679 transaction_id
, connection
->callbacks(),
1680 std::set
<int64
>(object_store_ids
.begin(), object_store_ids
.end()), mode
,
1681 this, new IndexedDBBackingStore::Transaction(backing_store_
.get())));
1684 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction
* transaction
) {
1685 transactions_
[transaction
->id()] = transaction
;
1688 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1689 return !pending_delete_calls_
.empty() ||
1690 transaction_coordinator_
.IsRunningVersionChangeTransaction() ||
1691 pending_run_version_change_transaction_call_
;
1694 void IndexedDBDatabase::OpenConnection(
1695 const IndexedDBPendingConnection
& connection
) {
1696 DCHECK(backing_store_
.get());
1698 // TODO(jsbell): Should have a priority queue so that higher version
1699 // requests are processed first. http://crbug.com/225850
1700 if (IsOpenConnectionBlocked()) {
1701 // The backing store only detects data loss when it is first opened. The
1702 // presence of existing connections means we didn't even check for data loss
1703 // so there'd better not be any.
1704 DCHECK_NE(blink::WebIDBDataLossTotal
, connection
.callbacks
->data_loss());
1705 pending_open_calls_
.push_back(connection
);
1709 if (metadata_
.id
== kInvalidId
) {
1710 // The database was deleted then immediately re-opened; OpenInternal()
1711 // recreates it in the backing store.
1712 if (OpenInternal().ok()) {
1713 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION
,
1714 metadata_
.int_version
);
1716 base::string16 message
;
1717 if (connection
.version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
) {
1718 message
= ASCIIToUTF16(
1719 "Internal error opening database with no version specified.");
1722 ASCIIToUTF16("Internal error opening database with version ") +
1723 Int64ToString16(connection
.version
);
1725 connection
.callbacks
->OnError(IndexedDBDatabaseError(
1726 blink::WebIDBDatabaseExceptionUnknownError
, message
));
1731 // We infer that the database didn't exist from its lack of either type of
1733 bool is_new_database
=
1734 metadata_
.version
== kNoStringVersion
&&
1735 metadata_
.int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1737 if (connection
.version
== IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
) {
1738 // For unit tests only - skip upgrade steps. Calling from script with
1739 // DEFAULT_INT_VERSION throws exception.
1740 // TODO(jsbell): DCHECK that not in unit tests.
1741 DCHECK(is_new_database
);
1742 connection
.callbacks
->OnSuccess(
1743 CreateConnection(connection
.database_callbacks
,
1744 connection
.child_process_id
),
1749 // We may need to change the version.
1750 int64 local_version
= connection
.version
;
1751 if (local_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
) {
1752 if (!is_new_database
) {
1753 connection
.callbacks
->OnSuccess(
1754 CreateConnection(connection
.database_callbacks
,
1755 connection
.child_process_id
),
1759 // Spec says: If no version is specified and no database exists, set
1760 // database version to 1.
1764 if (local_version
> metadata_
.int_version
) {
1765 RunVersionChangeTransaction(connection
.callbacks
,
1766 CreateConnection(connection
.database_callbacks
,
1767 connection
.child_process_id
),
1768 connection
.transaction_id
,
1772 if (local_version
< metadata_
.int_version
) {
1773 connection
.callbacks
->OnError(IndexedDBDatabaseError(
1774 blink::WebIDBDatabaseExceptionVersionError
,
1775 ASCIIToUTF16("The requested version (") +
1776 Int64ToString16(local_version
) +
1777 ASCIIToUTF16(") is less than the existing version (") +
1778 Int64ToString16(metadata_
.int_version
) + ASCIIToUTF16(").")));
1781 DCHECK_EQ(local_version
, metadata_
.int_version
);
1782 connection
.callbacks
->OnSuccess(
1783 CreateConnection(connection
.database_callbacks
,
1784 connection
.child_process_id
),
1788 void IndexedDBDatabase::RunVersionChangeTransaction(
1789 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1790 scoped_ptr
<IndexedDBConnection
> connection
,
1791 int64 transaction_id
,
1792 int64 requested_version
) {
1793 DCHECK(callbacks
.get());
1794 DCHECK(connections_
.count(connection
.get()));
1795 if (ConnectionCount() > 1) {
1796 DCHECK_NE(blink::WebIDBDataLossTotal
, callbacks
->data_loss());
1797 // Front end ensures the event is not fired at connections that have
1798 // close_pending set.
1799 for (const auto* iter
: connections_
) {
1800 if (iter
!= connection
.get()) {
1801 iter
->callbacks()->OnVersionChange(metadata_
.int_version
,
1805 // OnBlocked will be fired at the request when one of the other
1806 // connections acks that the OnVersionChange was ignored.
1808 DCHECK(!pending_run_version_change_transaction_call_
);
1809 pending_run_version_change_transaction_call_
.reset(new PendingUpgradeCall(
1810 callbacks
, connection
.Pass(), transaction_id
, requested_version
));
1813 RunVersionChangeTransactionFinal(
1814 callbacks
, connection
.Pass(), transaction_id
, requested_version
);
1817 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1818 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1819 scoped_ptr
<IndexedDBConnection
> connection
,
1820 int64 transaction_id
,
1821 int64 requested_version
) {
1823 std::vector
<int64
> object_store_ids
;
1824 CreateTransaction(transaction_id
,
1827 blink::WebIDBTransactionModeVersionChange
);
1829 transactions_
[transaction_id
]->ScheduleTask(
1830 base::Bind(&IndexedDBDatabase::VersionChangeOperation
,
1834 base::Passed(&connection
)));
1835 DCHECK(!pending_second_half_open_
);
1838 void IndexedDBDatabase::DeleteDatabase(
1839 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1841 if (IsDeleteDatabaseBlocked()) {
1842 for (const auto* connection
: connections_
) {
1843 // Front end ensures the event is not fired at connections that have
1844 // close_pending set.
1845 connection
->callbacks()->OnVersionChange(
1846 metadata_
.int_version
, IndexedDBDatabaseMetadata::NO_INT_VERSION
);
1848 // OnBlocked will be fired at the request when one of the other
1849 // connections acks that the OnVersionChange was ignored.
1851 pending_delete_calls_
.push_back(new PendingDeleteCall(callbacks
));
1854 DeleteDatabaseFinal(callbacks
);
1857 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1858 return !!ConnectionCount();
1861 void IndexedDBDatabase::DeleteDatabaseFinal(
1862 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1863 DCHECK(!IsDeleteDatabaseBlocked());
1864 DCHECK(backing_store_
.get());
1865 leveldb::Status s
= backing_store_
->DeleteDatabase(metadata_
.name
);
1867 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1868 "Internal error deleting database.");
1869 callbacks
->OnError(error
);
1870 if (s
.IsCorruption()) {
1871 GURL origin_url
= backing_store_
->origin_url();
1872 backing_store_
= NULL
;
1873 factory_
->HandleBackingStoreCorruption(origin_url
, error
);
1877 int64 old_version
= metadata_
.int_version
;
1878 metadata_
.version
= kNoStringVersion
;
1879 metadata_
.id
= kInvalidId
;
1880 metadata_
.int_version
= IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1881 metadata_
.object_stores
.clear();
1882 callbacks
->OnSuccess(old_version
);
1883 factory_
->DatabaseDeleted(identifier_
);
1886 void IndexedDBDatabase::ForceClose() {
1887 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1888 scoped_refptr
<IndexedDBDatabase
> protect(this);
1889 ConnectionSet::const_iterator it
= connections_
.begin();
1890 while (it
!= connections_
.end()) {
1891 IndexedDBConnection
* connection
= *it
++;
1892 connection
->ForceClose();
1894 DCHECK(connections_
.empty());
1897 void IndexedDBDatabase::VersionChangeIgnored() {
1898 if (pending_run_version_change_transaction_call_
)
1899 pending_run_version_change_transaction_call_
->callbacks()->OnBlocked(
1900 metadata_
.int_version
);
1902 for (const auto& pending_delete_call
: pending_delete_calls_
)
1903 pending_delete_call
->callbacks()->OnBlocked(metadata_
.int_version
);
1907 void IndexedDBDatabase::Close(IndexedDBConnection
* connection
, bool forced
) {
1908 DCHECK(connections_
.count(connection
));
1909 DCHECK(connection
->IsConnected());
1910 DCHECK(connection
->database() == this);
1912 IDB_TRACE("IndexedDBDatabase::Close");
1914 connections_
.erase(connection
);
1916 // Abort outstanding transactions from the closing connection. This
1917 // can not happen if the close is requested by the connection itself
1918 // as the front-end defers the close until all transactions are
1919 // complete, but can occur on process termination or forced close.
1921 TransactionMap
transactions(transactions_
);
1922 for (const auto& it
: transactions
) {
1923 if (it
.second
->connection() == connection
->callbacks())
1925 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
,
1926 "Connection is closing."));
1930 if (pending_second_half_open_
&&
1931 pending_second_half_open_
->connection() == connection
) {
1932 pending_second_half_open_
->callbacks()->OnError(
1933 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError
,
1934 "The connection was closed."));
1935 pending_second_half_open_
.reset();
1938 ProcessPendingCalls();
1940 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1941 if (!ConnectionCount() && !pending_open_calls_
.size() &&
1942 !pending_delete_calls_
.size()) {
1943 DCHECK(transactions_
.empty());
1945 const GURL origin_url
= backing_store_
->origin_url();
1946 backing_store_
= NULL
;
1948 factory_
->ReleaseDatabase(identifier_
, forced
);
1952 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1953 int64 object_store_id
,
1954 IndexedDBTransaction
* transaction
) {
1955 DCHECK(!transaction
);
1956 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
1957 RemoveObjectStore(object_store_id
);
1960 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1961 const IndexedDBObjectStoreMetadata
& object_store_metadata
,
1962 IndexedDBTransaction
* transaction
) {
1963 DCHECK(!transaction
);
1964 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation");
1965 AddObjectStore(object_store_metadata
,
1966 IndexedDBObjectStoreMetadata::kInvalidId
);
1969 void IndexedDBDatabase::VersionChangeAbortOperation(
1970 const base::string16
& previous_version
,
1971 int64 previous_int_version
,
1972 IndexedDBTransaction
* transaction
) {
1973 DCHECK(!transaction
);
1974 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
1975 metadata_
.version
= previous_version
;
1976 metadata_
.int_version
= previous_int_version
;
1979 } // namespace content