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_connection.h"
21 #include "content/browser/indexed_db/indexed_db_context_impl.h"
22 #include "content/browser/indexed_db/indexed_db_cursor.h"
23 #include "content/browser/indexed_db/indexed_db_factory.h"
24 #include "content/browser/indexed_db/indexed_db_index_writer.h"
25 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
26 #include "content/browser/indexed_db/indexed_db_return_value.h"
27 #include "content/browser/indexed_db/indexed_db_tracing.h"
28 #include "content/browser/indexed_db/indexed_db_transaction.h"
29 #include "content/browser/indexed_db/indexed_db_value.h"
30 #include "content/common/indexed_db/indexed_db_constants.h"
31 #include "content/common/indexed_db/indexed_db_key_path.h"
32 #include "content/common/indexed_db/indexed_db_key_range.h"
33 #include "storage/browser/blob/blob_data_handle.h"
34 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseException.h"
35 #include "third_party/leveldatabase/env_chromium.h"
37 using base::ASCIIToUTF16
;
38 using base::Int64ToString16
;
39 using blink::WebIDBKeyTypeNumber
;
45 // Used for WebCore.IndexedDB.Schema.ObjectStore.KeyPathType and
46 // WebCore.IndexedDB.Schema.Index.KeyPathType histograms. Do not
47 // modify (delete, re-order, renumber) these values other than
49 enum HistogramIDBKeyPathType
{
50 KEY_PATH_TYPE_NONE
= 0,
51 KEY_PATH_TYPE_STRING
= 1,
52 KEY_PATH_TYPE_ARRAY
= 2,
53 KEY_PATH_TYPE_MAX
= 3, // Keep as last/max entry, for histogram range.
56 HistogramIDBKeyPathType
HistogramKeyPathType(const IndexedDBKeyPath
& key_path
) {
57 switch (key_path
.type()) {
58 case blink::WebIDBKeyPathTypeNull
:
59 return KEY_PATH_TYPE_NONE
;
60 case blink::WebIDBKeyPathTypeString
:
61 return KEY_PATH_TYPE_STRING
;
62 case blink::WebIDBKeyPathTypeArray
:
63 return KEY_PATH_TYPE_ARRAY
;
66 return KEY_PATH_TYPE_NONE
;
71 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
72 // in-progress connection.
73 class IndexedDBDatabase::PendingUpgradeCall
{
75 PendingUpgradeCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
,
76 scoped_ptr
<IndexedDBConnection
> connection
,
79 : callbacks_(callbacks
),
80 connection_(connection
.Pass()),
82 transaction_id_(transaction_id
) {}
83 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
84 // Takes ownership of the connection object.
85 scoped_ptr
<IndexedDBConnection
> ReleaseConnection() WARN_UNUSED_RESULT
{
86 return connection_
.Pass();
88 int64
version() const { return version_
; }
89 int64
transaction_id() const { return transaction_id_
; }
92 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
93 scoped_ptr
<IndexedDBConnection
> connection_
;
95 const int64 transaction_id_
;
98 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
99 // owned elsewhere, but we need to cancel the success call if that connection
100 // closes before it is sent.
101 class IndexedDBDatabase::PendingSuccessCall
{
103 PendingSuccessCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
,
104 IndexedDBConnection
* connection
,
106 : callbacks_(callbacks
), connection_(connection
), version_(version
) {}
107 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
108 IndexedDBConnection
* connection() const { return connection_
; }
109 int64
version() const { return version_
; }
112 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
113 IndexedDBConnection
* connection_
;
117 class IndexedDBDatabase::PendingDeleteCall
{
119 explicit PendingDeleteCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
)
120 : callbacks_(callbacks
) {}
121 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
124 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
127 scoped_refptr
<IndexedDBDatabase
> IndexedDBDatabase::Create(
128 const base::string16
& name
,
129 IndexedDBBackingStore
* backing_store
,
130 IndexedDBFactory
* factory
,
131 const Identifier
& unique_identifier
,
132 leveldb::Status
* s
) {
133 scoped_refptr
<IndexedDBDatabase
> database
=
134 new IndexedDBDatabase(name
, backing_store
, factory
, unique_identifier
);
135 *s
= database
->OpenInternal();
143 const base::string16::value_type kNoStringVersion
[] = {0};
146 IndexedDBDatabase::IndexedDBDatabase(const base::string16
& name
,
147 IndexedDBBackingStore
* backing_store
,
148 IndexedDBFactory
* factory
,
149 const Identifier
& unique_identifier
)
150 : backing_store_(backing_store
),
154 IndexedDBDatabaseMetadata::NO_INT_VERSION
,
156 identifier_(unique_identifier
),
158 DCHECK(factory
!= NULL
);
161 void IndexedDBDatabase::AddObjectStore(
162 const IndexedDBObjectStoreMetadata
& object_store
,
163 int64 new_max_object_store_id
) {
164 DCHECK(metadata_
.object_stores
.find(object_store
.id
) ==
165 metadata_
.object_stores
.end());
166 if (new_max_object_store_id
!= IndexedDBObjectStoreMetadata::kInvalidId
) {
167 DCHECK_LT(metadata_
.max_object_store_id
, new_max_object_store_id
);
168 metadata_
.max_object_store_id
= new_max_object_store_id
;
170 metadata_
.object_stores
[object_store
.id
] = object_store
;
173 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id
) {
174 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
175 metadata_
.object_stores
.end());
176 metadata_
.object_stores
.erase(object_store_id
);
179 void IndexedDBDatabase::AddIndex(int64 object_store_id
,
180 const IndexedDBIndexMetadata
& index
,
181 int64 new_max_index_id
) {
182 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
183 metadata_
.object_stores
.end());
184 IndexedDBObjectStoreMetadata object_store
=
185 metadata_
.object_stores
[object_store_id
];
187 DCHECK(object_store
.indexes
.find(index
.id
) == object_store
.indexes
.end());
188 object_store
.indexes
[index
.id
] = index
;
189 if (new_max_index_id
!= IndexedDBIndexMetadata::kInvalidId
) {
190 DCHECK_LT(object_store
.max_index_id
, new_max_index_id
);
191 object_store
.max_index_id
= new_max_index_id
;
193 metadata_
.object_stores
[object_store_id
] = object_store
;
196 void IndexedDBDatabase::RemoveIndex(int64 object_store_id
, int64 index_id
) {
197 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
198 metadata_
.object_stores
.end());
199 IndexedDBObjectStoreMetadata object_store
=
200 metadata_
.object_stores
[object_store_id
];
202 DCHECK(object_store
.indexes
.find(index_id
) != object_store
.indexes
.end());
203 object_store
.indexes
.erase(index_id
);
204 metadata_
.object_stores
[object_store_id
] = object_store
;
207 leveldb::Status
IndexedDBDatabase::OpenInternal() {
208 bool success
= false;
209 leveldb::Status s
= backing_store_
->GetIDBDatabaseMetaData(
210 metadata_
.name
, &metadata_
, &success
);
211 DCHECK(success
== (metadata_
.id
!= kInvalidId
)) << "success = " << success
212 << " id = " << metadata_
.id
;
216 return backing_store_
->GetObjectStores(metadata_
.id
,
217 &metadata_
.object_stores
);
219 return backing_store_
->CreateIDBDatabaseMetaData(
220 metadata_
.name
, metadata_
.version
, metadata_
.int_version
, &metadata_
.id
);
223 IndexedDBDatabase::~IndexedDBDatabase() {
224 DCHECK(transactions_
.empty());
225 DCHECK(pending_open_calls_
.empty());
226 DCHECK(pending_delete_calls_
.empty());
229 scoped_ptr
<IndexedDBConnection
> IndexedDBDatabase::CreateConnection(
230 scoped_refptr
<IndexedDBDatabaseCallbacks
> database_callbacks
,
231 int child_process_id
) {
232 scoped_ptr
<IndexedDBConnection
> connection(
233 new IndexedDBConnection(this, database_callbacks
));
234 connections_
.insert(connection
.get());
235 backing_store_
->GrantChildProcessPermissions(child_process_id
);
236 return connection
.Pass();
239 IndexedDBTransaction
* IndexedDBDatabase::GetTransaction(
240 int64 transaction_id
) const {
241 TransactionMap::const_iterator trans_iterator
=
242 transactions_
.find(transaction_id
);
243 if (trans_iterator
== transactions_
.end())
245 return trans_iterator
->second
;
248 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id
) const {
249 if (!ContainsKey(metadata_
.object_stores
, object_store_id
)) {
250 DLOG(ERROR
) << "Invalid object_store_id";
256 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id
,
257 int64 index_id
) const {
258 if (!ValidateObjectStoreId(object_store_id
))
260 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
261 metadata_
.object_stores
.find(object_store_id
)->second
;
262 if (!ContainsKey(object_store_metadata
.indexes
, index_id
)) {
263 DLOG(ERROR
) << "Invalid index_id";
269 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
270 int64 object_store_id
,
271 int64 index_id
) const {
272 if (!ValidateObjectStoreId(object_store_id
))
274 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
275 metadata_
.object_stores
.find(object_store_id
)->second
;
276 if (index_id
!= IndexedDBIndexMetadata::kInvalidId
&&
277 !ContainsKey(object_store_metadata
.indexes
, index_id
)) {
278 DLOG(ERROR
) << "Invalid index_id";
284 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
285 int64 object_store_id
,
286 int64 index_id
) const {
287 if (!ValidateObjectStoreId(object_store_id
))
289 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
290 metadata_
.object_stores
.find(object_store_id
)->second
;
291 if (ContainsKey(object_store_metadata
.indexes
, index_id
)) {
292 DLOG(ERROR
) << "Invalid index_id";
298 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id
,
299 int64 object_store_id
,
300 const base::string16
& name
,
301 const IndexedDBKeyPath
& key_path
,
302 bool auto_increment
) {
303 IDB_TRACE1("IndexedDBDatabase::CreateObjectStore", "txn.id", transaction_id
);
304 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
307 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
309 if (ContainsKey(metadata_
.object_stores
, object_store_id
)) {
310 DLOG(ERROR
) << "Invalid object_store_id";
314 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.ObjectStore.KeyPathType",
315 HistogramKeyPathType(key_path
), KEY_PATH_TYPE_MAX
);
316 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.ObjectStore.AutoIncrement",
319 // Store creation is done synchronously, as it may be followed by
320 // index creation (also sync) since preemptive OpenCursor/SetIndexKeys
322 IndexedDBObjectStoreMetadata
object_store_metadata(
327 IndexedDBDatabase::kMinimumIndexId
);
330 backing_store_
->CreateObjectStore(transaction
->BackingStoreTransaction(),
331 transaction
->database()->id(),
332 object_store_metadata
.id
,
333 object_store_metadata
.name
,
334 object_store_metadata
.key_path
,
335 object_store_metadata
.auto_increment
);
337 IndexedDBDatabaseError
error(
338 blink::WebIDBDatabaseExceptionUnknownError
,
339 ASCIIToUTF16("Internal error creating object store '") +
340 object_store_metadata
.name
+ ASCIIToUTF16("'."));
341 transaction
->Abort(error
);
342 if (s
.IsCorruption())
343 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
348 AddObjectStore(object_store_metadata
, object_store_id
);
349 transaction
->ScheduleAbortTask(
350 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation
,
355 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id
,
356 int64 object_store_id
) {
357 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStore", "txn.id", transaction_id
);
358 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
361 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
363 if (!ValidateObjectStoreId(object_store_id
))
366 transaction
->ScheduleTask(
367 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation
,
372 void IndexedDBDatabase::CreateIndex(int64 transaction_id
,
373 int64 object_store_id
,
375 const base::string16
& name
,
376 const IndexedDBKeyPath
& key_path
,
379 IDB_TRACE1("IndexedDBDatabase::CreateIndex", "txn.id", transaction_id
);
380 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
383 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
385 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id
, index_id
))
388 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.Index.KeyPathType",
389 HistogramKeyPathType(key_path
), KEY_PATH_TYPE_MAX
);
390 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.Unique", unique
);
391 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.MultiEntry",
394 // Index creation is done synchronously since preemptive
395 // OpenCursor/SetIndexKeys may follow.
396 const IndexedDBIndexMetadata
index_metadata(
397 name
, index_id
, key_path
, unique
, multi_entry
);
399 if (!backing_store_
->CreateIndex(transaction
->BackingStoreTransaction(),
400 transaction
->database()->id(),
404 index_metadata
.key_path
,
405 index_metadata
.unique
,
406 index_metadata
.multi_entry
).ok()) {
407 base::string16 error_string
=
408 ASCIIToUTF16("Internal error creating index '") +
409 index_metadata
.name
+ ASCIIToUTF16("'.");
410 transaction
->Abort(IndexedDBDatabaseError(
411 blink::WebIDBDatabaseExceptionUnknownError
, error_string
));
415 AddIndex(object_store_id
, index_metadata
, index_id
);
416 transaction
->ScheduleAbortTask(
417 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation
,
423 void IndexedDBDatabase::CreateIndexAbortOperation(
424 int64 object_store_id
,
426 IndexedDBTransaction
* transaction
) {
427 DCHECK(!transaction
);
428 IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation");
429 RemoveIndex(object_store_id
, index_id
);
432 void IndexedDBDatabase::DeleteIndex(int64 transaction_id
,
433 int64 object_store_id
,
435 IDB_TRACE1("IndexedDBDatabase::DeleteIndex", "txn.id", transaction_id
);
436 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
439 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
441 if (!ValidateObjectStoreIdAndIndexId(object_store_id
, index_id
))
444 transaction
->ScheduleTask(
445 base::Bind(&IndexedDBDatabase::DeleteIndexOperation
,
451 void IndexedDBDatabase::DeleteIndexOperation(
452 int64 object_store_id
,
454 IndexedDBTransaction
* transaction
) {
456 "IndexedDBDatabase::DeleteIndexOperation", "txn.id", transaction
->id());
458 const IndexedDBIndexMetadata index_metadata
=
459 metadata_
.object_stores
[object_store_id
].indexes
[index_id
];
462 backing_store_
->DeleteIndex(transaction
->BackingStoreTransaction(),
463 transaction
->database()->id(),
467 base::string16 error_string
=
468 ASCIIToUTF16("Internal error deleting index '") +
469 index_metadata
.name
+ ASCIIToUTF16("'.");
470 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
472 transaction
->Abort(error
);
473 if (s
.IsCorruption())
474 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
479 RemoveIndex(object_store_id
, index_id
);
480 transaction
->ScheduleAbortTask(
481 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation
,
487 void IndexedDBDatabase::DeleteIndexAbortOperation(
488 int64 object_store_id
,
489 const IndexedDBIndexMetadata
& index_metadata
,
490 IndexedDBTransaction
* transaction
) {
491 DCHECK(!transaction
);
492 IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation");
493 AddIndex(object_store_id
, index_metadata
, IndexedDBIndexMetadata::kInvalidId
);
496 void IndexedDBDatabase::Commit(int64 transaction_id
) {
497 // The frontend suggests that we commit, but we may have previously initiated
498 // an abort, and so have disposed of the transaction. on_abort has already
499 // been dispatched to the frontend, so it will find out about that
501 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
503 scoped_refptr
<IndexedDBFactory
> factory
= factory_
;
504 leveldb::Status s
= transaction
->Commit();
505 if (s
.IsCorruption()) {
506 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
507 "Internal error committing transaction.");
508 factory
->HandleBackingStoreCorruption(identifier_
.first
, error
);
513 void IndexedDBDatabase::Abort(int64 transaction_id
) {
514 // If the transaction is unknown, then it has already been aborted by the
515 // backend before this call so it is safe to ignore it.
516 IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction_id
);
517 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
519 transaction
->Abort();
522 void IndexedDBDatabase::Abort(int64 transaction_id
,
523 const IndexedDBDatabaseError
& error
) {
524 IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction_id
);
525 // If the transaction is unknown, then it has already been aborted by the
526 // backend before this call so it is safe to ignore it.
527 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
529 transaction
->Abort(error
);
532 void IndexedDBDatabase::GetAll(int64 transaction_id
,
533 int64 object_store_id
,
535 scoped_ptr
<IndexedDBKeyRange
> key_range
,
538 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
539 IDB_TRACE1("IndexedDBDatabase::GetAll", "txn.id", transaction_id
);
540 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
544 if (!ValidateObjectStoreId(object_store_id
))
547 transaction
->ScheduleTask(base::Bind(
548 &IndexedDBDatabase::GetAllOperation
, this, object_store_id
, index_id
,
550 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
,
551 max_count
, callbacks
));
554 void IndexedDBDatabase::Get(int64 transaction_id
,
555 int64 object_store_id
,
557 scoped_ptr
<IndexedDBKeyRange
> key_range
,
559 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
560 IDB_TRACE1("IndexedDBDatabase::Get", "txn.id", transaction_id
);
561 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
565 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
568 transaction
->ScheduleTask(base::Bind(
569 &IndexedDBDatabase::GetOperation
,
574 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
,
578 void IndexedDBDatabase::GetOperation(
579 int64 object_store_id
,
581 scoped_ptr
<IndexedDBKeyRange
> key_range
,
582 indexed_db::CursorType cursor_type
,
583 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
584 IndexedDBTransaction
* transaction
) {
585 IDB_TRACE1("IndexedDBDatabase::GetOperation", "txn.id", transaction
->id());
587 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
588 metadata_
.object_stores
.end());
589 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
590 metadata_
.object_stores
[object_store_id
];
592 const IndexedDBKey
* key
;
595 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
596 if (key_range
->IsOnlyKey()) {
597 key
= &key_range
->lower();
599 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
600 DCHECK_NE(cursor_type
, indexed_db::CURSOR_KEY_ONLY
);
601 // ObjectStore Retrieval Operation
602 backing_store_cursor
= backing_store_
->OpenObjectStoreCursor(
603 transaction
->BackingStoreTransaction(),
607 blink::WebIDBCursorDirectionNext
,
609 } else if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
610 // Index Value Retrieval Operation
611 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
612 transaction
->BackingStoreTransaction(),
617 blink::WebIDBCursorDirectionNext
,
620 // Index Referenced Value Retrieval Operation
621 backing_store_cursor
= backing_store_
->OpenIndexCursor(
622 transaction
->BackingStoreTransaction(),
627 blink::WebIDBCursorDirectionNext
,
632 DLOG(ERROR
) << "Unable to open cursor operation: " << s
.ToString();
633 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
634 "Internal error deleting data in range");
635 if (s
.IsCorruption()) {
636 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
641 if (!backing_store_cursor
) {
642 callbacks
->OnSuccess();
646 key
= &backing_store_cursor
->key();
649 scoped_ptr
<IndexedDBKey
> primary_key
;
650 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
651 // Object Store Retrieval Operation
652 IndexedDBReturnValue value
;
653 s
= backing_store_
->GetRecord(transaction
->BackingStoreTransaction(),
659 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
660 "Internal error in GetRecord.");
661 callbacks
->OnError(error
);
663 if (s
.IsCorruption())
664 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
670 callbacks
->OnSuccess();
674 if (object_store_metadata
.auto_increment
&&
675 !object_store_metadata
.key_path
.IsNull()) {
676 value
.primary_key
= *key
;
677 value
.key_path
= object_store_metadata
.key_path
;
680 callbacks
->OnSuccess(&value
);
684 // From here we are dealing only with indexes.
685 s
= backing_store_
->GetPrimaryKeyViaIndex(
686 transaction
->BackingStoreTransaction(),
693 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
694 "Internal error in GetPrimaryKeyViaIndex.");
695 callbacks
->OnError(error
);
696 if (s
.IsCorruption())
697 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
702 callbacks
->OnSuccess();
705 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
706 // Index Value Retrieval Operation
707 callbacks
->OnSuccess(*primary_key
);
711 // Index Referenced Value Retrieval Operation
712 IndexedDBReturnValue value
;
713 s
= backing_store_
->GetRecord(transaction
->BackingStoreTransaction(),
719 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
720 "Internal error in GetRecord.");
721 callbacks
->OnError(error
);
722 if (s
.IsCorruption())
723 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
729 callbacks
->OnSuccess();
732 if (object_store_metadata
.auto_increment
&&
733 !object_store_metadata
.key_path
.IsNull()) {
734 value
.primary_key
= *primary_key
;
735 value
.key_path
= object_store_metadata
.key_path
;
737 callbacks
->OnSuccess(&value
);
740 void IndexedDBDatabase::GetAllOperation(
741 int64 object_store_id
,
743 scoped_ptr
<IndexedDBKeyRange
> key_range
,
744 indexed_db::CursorType cursor_type
,
746 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
747 IndexedDBTransaction
* transaction
) {
748 IDB_TRACE1("IndexedDBDatabase::GetAllOperation", "txn.id", transaction
->id());
750 DCHECK_GT(max_count
, 0);
752 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
753 metadata_
.object_stores
.end());
754 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
755 metadata_
.object_stores
[object_store_id
];
759 scoped_ptr
<IndexedDBBackingStore::Cursor
> cursor
;
761 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
763 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
764 // Object Store: Key Retrieval Operation
765 cursor
= backing_store_
->OpenObjectStoreKeyCursor(
766 transaction
->BackingStoreTransaction(), id(), object_store_id
,
767 *key_range
, blink::WebIDBCursorDirectionNext
, &s
);
769 // Index Value: (Primary Key) Retrieval Operation
770 cursor
= backing_store_
->OpenIndexKeyCursor(
771 transaction
->BackingStoreTransaction(), id(), object_store_id
,
772 index_id
, *key_range
, blink::WebIDBCursorDirectionNext
, &s
);
776 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
777 // Object Store: Value Retrieval Operation
778 cursor
= backing_store_
->OpenObjectStoreCursor(
779 transaction
->BackingStoreTransaction(), id(), object_store_id
,
780 *key_range
, blink::WebIDBCursorDirectionNext
, &s
);
782 // Object Store: Referenced Value Retrieval Operation
783 cursor
= backing_store_
->OpenIndexCursor(
784 transaction
->BackingStoreTransaction(), id(), object_store_id
,
785 index_id
, *key_range
, blink::WebIDBCursorDirectionNext
, &s
);
790 DLOG(ERROR
) << "Unable to open cursor operation: " << s
.ToString();
791 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
792 "Internal error in GetAllOperation");
793 callbacks
->OnError(error
);
794 if (s
.IsCorruption()) {
795 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
801 std::vector
<IndexedDBKey
> found_keys
;
802 std::vector
<IndexedDBReturnValue
> found_values
;
804 // Doesn't matter if key or value array here - will be empty array when it
806 callbacks
->OnSuccessArray(&found_values
, object_store_metadata
.key_path
);
810 bool did_first_seek
= false;
811 bool generated_key
= object_store_metadata
.auto_increment
&&
812 !object_store_metadata
.key_path
.IsNull();
814 size_t response_size
= kMaxIDBMessageOverhead
;
815 int64 num_found_items
= 0;
816 while (num_found_items
++ < max_count
) {
818 if (did_first_seek
) {
819 cursor_valid
= cursor
->Continue(&s
);
821 cursor_valid
= cursor
->FirstSeek(&s
);
822 did_first_seek
= true;
825 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
826 "Internal error in GetAllOperation.");
827 callbacks
->OnError(error
);
828 if (s
.IsCorruption())
829 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
837 IndexedDBReturnValue return_value
;
838 IndexedDBKey return_key
;
840 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
841 return_key
= cursor
->primary_key();
844 return_value
.swap(*cursor
->value());
845 if (!return_value
.empty() && generated_key
) {
846 return_value
.primary_key
= cursor
->primary_key();
847 return_value
.key_path
= object_store_metadata
.key_path
;
851 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
)
852 response_size
+= return_key
.size_estimate();
854 response_size
+= return_value
.SizeEstimate();
855 if (response_size
> IPC::Channel::kMaximumMessageSize
) {
856 // TODO(cmumford): Reach this limit more gracefully (crbug.com/478949)
860 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
)
861 found_keys
.push_back(return_key
);
863 found_values
.push_back(return_value
);
866 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
867 // IndexedDBKey already supports an array of values so we can leverage this
868 // to return an array of keys - no need to create our own array of keys.
869 callbacks
->OnSuccess(IndexedDBKey(found_keys
));
871 callbacks
->OnSuccessArray(&found_values
, object_store_metadata
.key_path
);
875 static scoped_ptr
<IndexedDBKey
> GenerateKey(
876 IndexedDBBackingStore
* backing_store
,
877 IndexedDBTransaction
* transaction
,
879 int64 object_store_id
) {
880 const int64 max_generator_value
=
881 9007199254740992LL; // Maximum integer storable as ECMAScript number.
882 int64 current_number
;
883 leveldb::Status s
= backing_store
->GetKeyGeneratorCurrentNumber(
884 transaction
->BackingStoreTransaction(),
889 LOG(ERROR
) << "Failed to GetKeyGeneratorCurrentNumber";
890 return make_scoped_ptr(new IndexedDBKey());
892 if (current_number
< 0 || current_number
> max_generator_value
)
893 return make_scoped_ptr(new IndexedDBKey());
895 return make_scoped_ptr(new IndexedDBKey(current_number
, WebIDBKeyTypeNumber
));
898 static leveldb::Status
UpdateKeyGenerator(IndexedDBBackingStore
* backing_store
,
899 IndexedDBTransaction
* transaction
,
901 int64 object_store_id
,
902 const IndexedDBKey
& key
,
903 bool check_current
) {
904 DCHECK_EQ(WebIDBKeyTypeNumber
, key
.type());
905 return backing_store
->MaybeUpdateKeyGeneratorCurrentNumber(
906 transaction
->BackingStoreTransaction(),
909 static_cast<int64
>(floor(key
.number())) + 1,
913 struct IndexedDBDatabase::PutOperationParams
{
914 PutOperationParams() {}
915 int64 object_store_id
;
916 IndexedDBValue value
;
917 ScopedVector
<storage::BlobDataHandle
> handles
;
918 scoped_ptr
<IndexedDBKey
> key
;
919 blink::WebIDBPutMode put_mode
;
920 scoped_refptr
<IndexedDBCallbacks
> callbacks
;
921 std::vector
<IndexKeys
> index_keys
;
924 DISALLOW_COPY_AND_ASSIGN(PutOperationParams
);
927 void IndexedDBDatabase::Put(int64 transaction_id
,
928 int64 object_store_id
,
929 IndexedDBValue
* value
,
930 ScopedVector
<storage::BlobDataHandle
>* handles
,
931 scoped_ptr
<IndexedDBKey
> key
,
932 blink::WebIDBPutMode put_mode
,
933 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
934 const std::vector
<IndexKeys
>& index_keys
) {
935 IDB_TRACE1("IndexedDBDatabase::Put", "txn.id", transaction_id
);
936 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
939 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
941 if (!ValidateObjectStoreId(object_store_id
))
946 scoped_ptr
<PutOperationParams
> params(new PutOperationParams());
947 params
->object_store_id
= object_store_id
;
948 params
->value
.swap(*value
);
949 params
->handles
.swap(*handles
);
950 params
->key
= key
.Pass();
951 params
->put_mode
= put_mode
;
952 params
->callbacks
= callbacks
;
953 params
->index_keys
= index_keys
;
954 transaction
->ScheduleTask(base::Bind(
955 &IndexedDBDatabase::PutOperation
, this, base::Passed(¶ms
)));
958 void IndexedDBDatabase::PutOperation(scoped_ptr
<PutOperationParams
> params
,
959 IndexedDBTransaction
* transaction
) {
960 IDB_TRACE1("IndexedDBDatabase::PutOperation", "txn.id", transaction
->id());
961 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
962 bool key_was_generated
= false;
964 DCHECK(metadata_
.object_stores
.find(params
->object_store_id
) !=
965 metadata_
.object_stores
.end());
966 const IndexedDBObjectStoreMetadata
& object_store
=
967 metadata_
.object_stores
[params
->object_store_id
];
968 DCHECK(object_store
.auto_increment
|| params
->key
->IsValid());
970 scoped_ptr
<IndexedDBKey
> key
;
971 if (params
->put_mode
!= blink::WebIDBPutModeCursorUpdate
&&
972 object_store
.auto_increment
&& !params
->key
->IsValid()) {
973 scoped_ptr
<IndexedDBKey
> auto_inc_key
= GenerateKey(
974 backing_store_
.get(), transaction
, id(), params
->object_store_id
);
975 key_was_generated
= true;
976 if (!auto_inc_key
->IsValid()) {
977 params
->callbacks
->OnError(
978 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError
,
979 "Maximum key generator value reached."));
982 key
= auto_inc_key
.Pass();
984 key
= params
->key
.Pass();
987 DCHECK(key
->IsValid());
989 IndexedDBBackingStore::RecordIdentifier record_identifier
;
990 if (params
->put_mode
== blink::WebIDBPutModeAddOnly
) {
992 leveldb::Status s
= backing_store_
->KeyExistsInObjectStore(
993 transaction
->BackingStoreTransaction(),
995 params
->object_store_id
,
1000 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1001 "Internal error checking key existence.");
1002 params
->callbacks
->OnError(error
);
1003 if (s
.IsCorruption())
1004 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1009 params
->callbacks
->OnError(
1010 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError
,
1011 "Key already exists in the object store."));
1016 ScopedVector
<IndexWriter
> index_writers
;
1017 base::string16 error_message
;
1018 bool obeys_constraints
= false;
1019 bool backing_store_success
= MakeIndexWriters(transaction
,
1020 backing_store_
.get(),
1028 &obeys_constraints
);
1029 if (!backing_store_success
) {
1030 params
->callbacks
->OnError(IndexedDBDatabaseError(
1031 blink::WebIDBDatabaseExceptionUnknownError
,
1032 "Internal error: backing store error updating index keys."));
1035 if (!obeys_constraints
) {
1036 params
->callbacks
->OnError(IndexedDBDatabaseError(
1037 blink::WebIDBDatabaseExceptionConstraintError
, error_message
));
1041 // Before this point, don't do any mutation. After this point, rollback the
1042 // transaction in case of error.
1044 backing_store_
->PutRecord(transaction
->BackingStoreTransaction(),
1046 params
->object_store_id
,
1050 &record_identifier
);
1052 IndexedDBDatabaseError
error(
1053 blink::WebIDBDatabaseExceptionUnknownError
,
1054 "Internal error: backing store error performing put/add.");
1055 params
->callbacks
->OnError(error
);
1056 if (s
.IsCorruption())
1057 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1062 for (size_t i
= 0; i
< index_writers
.size(); ++i
) {
1063 IndexWriter
* index_writer
= index_writers
[i
];
1064 index_writer
->WriteIndexKeys(record_identifier
,
1065 backing_store_
.get(),
1066 transaction
->BackingStoreTransaction(),
1068 params
->object_store_id
);
1071 if (object_store
.auto_increment
&&
1072 params
->put_mode
!= blink::WebIDBPutModeCursorUpdate
&&
1073 key
->type() == WebIDBKeyTypeNumber
) {
1074 leveldb::Status s
= UpdateKeyGenerator(backing_store_
.get(),
1077 params
->object_store_id
,
1079 !key_was_generated
);
1081 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1082 "Internal error updating key generator.");
1083 params
->callbacks
->OnError(error
);
1084 if (s
.IsCorruption())
1085 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1091 params
->callbacks
->OnSuccess(*key
);
1094 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id
,
1095 int64 object_store_id
,
1096 scoped_ptr
<IndexedDBKey
> primary_key
,
1097 const std::vector
<IndexKeys
>& index_keys
) {
1098 IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction_id
);
1099 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1102 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
1104 // TODO(alecflett): This method could be asynchronous, but we need to
1105 // evaluate if it's worth the extra complexity.
1106 IndexedDBBackingStore::RecordIdentifier record_identifier
;
1108 leveldb::Status s
= backing_store_
->KeyExistsInObjectStore(
1109 transaction
->BackingStoreTransaction(),
1116 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1117 "Internal error setting index keys.");
1118 transaction
->Abort(error
);
1119 if (s
.IsCorruption())
1120 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1125 transaction
->Abort(IndexedDBDatabaseError(
1126 blink::WebIDBDatabaseExceptionUnknownError
,
1127 "Internal error setting index keys for object store."));
1131 ScopedVector
<IndexWriter
> index_writers
;
1132 base::string16 error_message
;
1133 bool obeys_constraints
= false;
1134 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
1135 metadata_
.object_stores
.end());
1136 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
1137 metadata_
.object_stores
[object_store_id
];
1138 bool backing_store_success
= MakeIndexWriters(transaction
,
1139 backing_store_
.get(),
1141 object_store_metadata
,
1147 &obeys_constraints
);
1148 if (!backing_store_success
) {
1149 transaction
->Abort(IndexedDBDatabaseError(
1150 blink::WebIDBDatabaseExceptionUnknownError
,
1151 "Internal error: backing store error updating index keys."));
1154 if (!obeys_constraints
) {
1155 transaction
->Abort(IndexedDBDatabaseError(
1156 blink::WebIDBDatabaseExceptionConstraintError
, error_message
));
1160 for (size_t i
= 0; i
< index_writers
.size(); ++i
) {
1161 IndexWriter
* index_writer
= index_writers
[i
];
1162 index_writer
->WriteIndexKeys(record_identifier
,
1163 backing_store_
.get(),
1164 transaction
->BackingStoreTransaction(),
1170 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id
,
1172 const std::vector
<int64
>& index_ids
) {
1173 IDB_TRACE1("IndexedDBDatabase::SetIndexesReady", "txn.id", transaction_id
);
1174 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1177 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
1179 transaction
->ScheduleTask(
1180 blink::WebIDBTaskTypePreemptive
,
1181 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation
,
1186 void IndexedDBDatabase::SetIndexesReadyOperation(
1188 IndexedDBTransaction
* transaction
) {
1189 IDB_TRACE1("IndexedDBDatabase::SetIndexesReadyOperation",
1192 for (size_t i
= 0; i
< index_count
; ++i
)
1193 transaction
->DidCompletePreemptiveEvent();
1196 struct IndexedDBDatabase::OpenCursorOperationParams
{
1197 OpenCursorOperationParams() {}
1198 int64 object_store_id
;
1200 scoped_ptr
<IndexedDBKeyRange
> key_range
;
1201 blink::WebIDBCursorDirection direction
;
1202 indexed_db::CursorType cursor_type
;
1203 blink::WebIDBTaskType task_type
;
1204 scoped_refptr
<IndexedDBCallbacks
> callbacks
;
1207 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams
);
1210 void IndexedDBDatabase::OpenCursor(
1211 int64 transaction_id
,
1212 int64 object_store_id
,
1214 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1215 blink::WebIDBCursorDirection direction
,
1217 blink::WebIDBTaskType task_type
,
1218 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1219 IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction_id
);
1220 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1224 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
1227 scoped_ptr
<OpenCursorOperationParams
> params(new OpenCursorOperationParams());
1228 params
->object_store_id
= object_store_id
;
1229 params
->index_id
= index_id
;
1230 params
->key_range
= key_range
.Pass();
1231 params
->direction
= direction
;
1232 params
->cursor_type
=
1233 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
;
1234 params
->task_type
= task_type
;
1235 params
->callbacks
= callbacks
;
1236 transaction
->ScheduleTask(base::Bind(
1237 &IndexedDBDatabase::OpenCursorOperation
, this, base::Passed(¶ms
)));
1240 void IndexedDBDatabase::OpenCursorOperation(
1241 scoped_ptr
<OpenCursorOperationParams
> params
,
1242 IndexedDBTransaction
* transaction
) {
1244 "IndexedDBDatabase::OpenCursorOperation", "txn.id", transaction
->id());
1246 // The frontend has begun indexing, so this pauses the transaction
1247 // until the indexing is complete. This can't happen any earlier
1248 // because we don't want to switch to early mode in case multiple
1249 // indexes are being created in a row, with Put()'s in between.
1250 if (params
->task_type
== blink::WebIDBTaskTypePreemptive
)
1251 transaction
->AddPreemptiveEvent();
1254 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
1255 if (params
->index_id
== IndexedDBIndexMetadata::kInvalidId
) {
1256 if (params
->cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
1257 DCHECK_EQ(params
->task_type
, blink::WebIDBTaskTypeNormal
);
1258 backing_store_cursor
= backing_store_
->OpenObjectStoreKeyCursor(
1259 transaction
->BackingStoreTransaction(),
1261 params
->object_store_id
,
1266 backing_store_cursor
= backing_store_
->OpenObjectStoreCursor(
1267 transaction
->BackingStoreTransaction(),
1269 params
->object_store_id
,
1275 DCHECK_EQ(params
->task_type
, blink::WebIDBTaskTypeNormal
);
1276 if (params
->cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
1277 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
1278 transaction
->BackingStoreTransaction(),
1280 params
->object_store_id
,
1286 backing_store_cursor
= backing_store_
->OpenIndexCursor(
1287 transaction
->BackingStoreTransaction(),
1289 params
->object_store_id
,
1298 DLOG(ERROR
) << "Unable to open cursor operation: " << s
.ToString();
1299 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1300 "Internal error opening cursor operation");
1301 if (s
.IsCorruption()) {
1302 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1307 if (!backing_store_cursor
) {
1308 // Why is Success being called?
1309 params
->callbacks
->OnSuccess(nullptr);
1313 scoped_refptr
<IndexedDBCursor
> cursor
=
1314 new IndexedDBCursor(backing_store_cursor
.Pass(),
1315 params
->cursor_type
,
1318 params
->callbacks
->OnSuccess(
1319 cursor
, cursor
->key(), cursor
->primary_key(), cursor
->Value());
1322 void IndexedDBDatabase::Count(int64 transaction_id
,
1323 int64 object_store_id
,
1325 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1326 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1327 IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction_id
);
1328 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1332 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
1335 transaction
->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation
,
1339 base::Passed(&key_range
),
1343 void IndexedDBDatabase::CountOperation(
1344 int64 object_store_id
,
1346 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1347 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1348 IndexedDBTransaction
* transaction
) {
1349 IDB_TRACE1("IndexedDBDatabase::CountOperation", "txn.id", transaction
->id());
1351 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
1354 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
1355 backing_store_cursor
= backing_store_
->OpenObjectStoreKeyCursor(
1356 transaction
->BackingStoreTransaction(),
1360 blink::WebIDBCursorDirectionNext
,
1363 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
1364 transaction
->BackingStoreTransaction(),
1369 blink::WebIDBCursorDirectionNext
,
1373 DLOG(ERROR
) << "Unable perform count operation: " << s
.ToString();
1374 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1375 "Internal error performing count operation");
1376 if (s
.IsCorruption()) {
1377 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1381 if (!backing_store_cursor
) {
1382 callbacks
->OnSuccess(count
);
1388 } while (backing_store_cursor
->Continue(&s
));
1390 // TODO(cmumford): Check for database corruption.
1392 callbacks
->OnSuccess(count
);
1395 void IndexedDBDatabase::DeleteRange(
1396 int64 transaction_id
,
1397 int64 object_store_id
,
1398 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1399 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1400 IDB_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction_id
);
1401 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1404 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
1406 if (!ValidateObjectStoreId(object_store_id
))
1409 transaction
->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation
,
1412 base::Passed(&key_range
),
1416 void IndexedDBDatabase::DeleteRangeOperation(
1417 int64 object_store_id
,
1418 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1419 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1420 IndexedDBTransaction
* transaction
) {
1422 "IndexedDBDatabase::DeleteRangeOperation", "txn.id", transaction
->id());
1424 backing_store_
->DeleteRange(transaction
->BackingStoreTransaction(),
1429 base::string16 error_string
=
1430 ASCIIToUTF16("Internal error deleting data in range");
1431 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1433 transaction
->Abort(error
);
1434 if (s
.IsCorruption()) {
1435 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1440 callbacks
->OnSuccess();
1443 void IndexedDBDatabase::Clear(int64 transaction_id
,
1444 int64 object_store_id
,
1445 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1446 IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction_id
);
1447 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1450 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
1452 if (!ValidateObjectStoreId(object_store_id
))
1455 transaction
->ScheduleTask(base::Bind(
1456 &IndexedDBDatabase::ClearOperation
, this, object_store_id
, callbacks
));
1459 void IndexedDBDatabase::ClearOperation(
1460 int64 object_store_id
,
1461 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1462 IndexedDBTransaction
* transaction
) {
1463 IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction
->id());
1464 leveldb::Status s
= backing_store_
->ClearObjectStore(
1465 transaction
->BackingStoreTransaction(), id(), object_store_id
);
1467 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1468 "Internal error clearing object store");
1469 callbacks
->OnError(error
);
1470 if (s
.IsCorruption()) {
1471 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1476 callbacks
->OnSuccess();
1479 void IndexedDBDatabase::DeleteObjectStoreOperation(
1480 int64 object_store_id
,
1481 IndexedDBTransaction
* transaction
) {
1482 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreOperation",
1486 const IndexedDBObjectStoreMetadata object_store_metadata
=
1487 metadata_
.object_stores
[object_store_id
];
1489 backing_store_
->DeleteObjectStore(transaction
->BackingStoreTransaction(),
1490 transaction
->database()->id(),
1493 base::string16 error_string
=
1494 ASCIIToUTF16("Internal error deleting object store '") +
1495 object_store_metadata
.name
+ ASCIIToUTF16("'.");
1496 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1498 transaction
->Abort(error
);
1499 if (s
.IsCorruption())
1500 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1505 RemoveObjectStore(object_store_id
);
1506 transaction
->ScheduleAbortTask(
1507 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation
,
1509 object_store_metadata
));
1512 void IndexedDBDatabase::VersionChangeOperation(
1514 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1515 scoped_ptr
<IndexedDBConnection
> connection
,
1516 IndexedDBTransaction
* transaction
) {
1518 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction
->id());
1519 int64 old_version
= metadata_
.int_version
;
1520 DCHECK_GT(version
, old_version
);
1522 if (!backing_store_
->UpdateIDBDatabaseIntVersion(
1523 transaction
->BackingStoreTransaction(), id(), version
)) {
1524 IndexedDBDatabaseError
error(
1525 blink::WebIDBDatabaseExceptionUnknownError
,
1527 "Internal error writing data to stable storage when "
1528 "updating version."));
1529 callbacks
->OnError(error
);
1530 transaction
->Abort(error
);
1534 transaction
->ScheduleAbortTask(
1535 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation
,
1538 metadata_
.int_version
));
1539 metadata_
.int_version
= version
;
1540 metadata_
.version
= kNoStringVersion
;
1542 DCHECK(!pending_second_half_open_
);
1543 pending_second_half_open_
.reset(
1544 new PendingSuccessCall(callbacks
, connection
.get(), version
));
1545 callbacks
->OnUpgradeNeeded(old_version
, connection
.Pass(), metadata());
1548 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction
* transaction
,
1550 DCHECK(transactions_
.find(transaction
->id()) != transactions_
.end());
1551 DCHECK_EQ(transactions_
[transaction
->id()], transaction
);
1552 transactions_
.erase(transaction
->id());
1554 if (transaction
->mode() == blink::WebIDBTransactionModeVersionChange
) {
1555 if (pending_second_half_open_
) {
1557 DCHECK_EQ(pending_second_half_open_
->version(), metadata_
.int_version
);
1558 DCHECK(metadata_
.id
!= kInvalidId
);
1560 // Connection was already minted for OnUpgradeNeeded callback.
1561 scoped_ptr
<IndexedDBConnection
> connection
;
1562 pending_second_half_open_
->callbacks()->OnSuccess(connection
.Pass(),
1565 pending_second_half_open_
->callbacks()->OnError(
1566 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError
,
1567 "Version change transaction was aborted in "
1568 "upgradeneeded event handler."));
1570 pending_second_half_open_
.reset();
1573 // Connection queue is now unblocked.
1574 ProcessPendingCalls();
1578 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status
& status
) {
1579 if (status
.IsCorruption()) {
1580 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1581 "Error committing transaction");
1582 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(), error
);
1584 factory_
->HandleBackingStoreFailure(backing_store_
->origin_url());
1588 size_t IndexedDBDatabase::ConnectionCount() const {
1589 // This does not include pending open calls, as those should not block version
1590 // changes and deletes.
1591 return connections_
.size();
1594 size_t IndexedDBDatabase::PendingOpenCount() const {
1595 return pending_open_calls_
.size();
1598 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1599 return pending_run_version_change_transaction_call_
? 1 : 0;
1602 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1603 return pending_second_half_open_
? 1 : 0;
1606 size_t IndexedDBDatabase::PendingDeleteCount() const {
1607 return pending_delete_calls_
.size();
1610 void IndexedDBDatabase::ProcessPendingCalls() {
1611 if (pending_run_version_change_transaction_call_
&& ConnectionCount() == 1) {
1612 DCHECK(pending_run_version_change_transaction_call_
->version() >
1613 metadata_
.int_version
);
1614 scoped_ptr
<PendingUpgradeCall
> pending_call
=
1615 pending_run_version_change_transaction_call_
.Pass();
1616 RunVersionChangeTransactionFinal(pending_call
->callbacks(),
1617 pending_call
->ReleaseConnection(),
1618 pending_call
->transaction_id(),
1619 pending_call
->version());
1620 DCHECK_EQ(1u, ConnectionCount());
1621 // Fall through would be a no-op, since transaction must complete
1623 DCHECK(IsDeleteDatabaseBlocked());
1624 DCHECK(IsOpenConnectionBlocked());
1628 if (!IsDeleteDatabaseBlocked()) {
1629 PendingDeleteCallList pending_delete_calls
;
1630 pending_delete_calls_
.swap(pending_delete_calls
);
1631 while (!pending_delete_calls
.empty()) {
1632 // Only the first delete call will delete the database, but each must fire
1634 scoped_ptr
<PendingDeleteCall
> pending_delete_call(
1635 pending_delete_calls
.front());
1636 pending_delete_calls
.pop_front();
1637 DeleteDatabaseFinal(pending_delete_call
->callbacks());
1639 // delete_database_final should never re-queue calls.
1640 DCHECK(pending_delete_calls_
.empty());
1641 // Fall through when complete, as pending opens may be unblocked.
1644 if (!IsOpenConnectionBlocked()) {
1645 PendingOpenCallList pending_open_calls
;
1646 pending_open_calls_
.swap(pending_open_calls
);
1647 while (!pending_open_calls
.empty()) {
1648 OpenConnection(pending_open_calls
.front());
1649 pending_open_calls
.pop_front();
1654 void IndexedDBDatabase::CreateTransaction(
1655 int64 transaction_id
,
1656 IndexedDBConnection
* connection
,
1657 const std::vector
<int64
>& object_store_ids
,
1658 blink::WebIDBTransactionMode mode
) {
1659 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id
);
1660 DCHECK(connections_
.count(connection
));
1661 DCHECK(transactions_
.find(transaction_id
) == transactions_
.end());
1662 if (transactions_
.find(transaction_id
) != transactions_
.end())
1665 // The transaction will add itself to this database's coordinator, which
1666 // manages the lifetime of the object.
1667 TransactionCreated(new IndexedDBTransaction(
1669 connection
->callbacks(),
1670 std::set
<int64
>(object_store_ids
.begin(), object_store_ids
.end()),
1673 new IndexedDBBackingStore::Transaction(backing_store_
.get())));
1676 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction
* transaction
) {
1677 transactions_
[transaction
->id()] = transaction
;
1680 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1681 return !pending_delete_calls_
.empty() ||
1682 transaction_coordinator_
.IsRunningVersionChangeTransaction() ||
1683 pending_run_version_change_transaction_call_
;
1686 void IndexedDBDatabase::OpenConnection(
1687 const IndexedDBPendingConnection
& connection
) {
1688 DCHECK(backing_store_
.get());
1690 // TODO(jsbell): Should have a priority queue so that higher version
1691 // requests are processed first. http://crbug.com/225850
1692 if (IsOpenConnectionBlocked()) {
1693 // The backing store only detects data loss when it is first opened. The
1694 // presence of existing connections means we didn't even check for data loss
1695 // so there'd better not be any.
1696 DCHECK_NE(blink::WebIDBDataLossTotal
, connection
.callbacks
->data_loss());
1697 pending_open_calls_
.push_back(connection
);
1701 if (metadata_
.id
== kInvalidId
) {
1702 // The database was deleted then immediately re-opened; OpenInternal()
1703 // recreates it in the backing store.
1704 if (OpenInternal().ok()) {
1705 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION
,
1706 metadata_
.int_version
);
1708 base::string16 message
;
1709 if (connection
.version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
) {
1710 message
= ASCIIToUTF16(
1711 "Internal error opening database with no version specified.");
1714 ASCIIToUTF16("Internal error opening database with version ") +
1715 Int64ToString16(connection
.version
);
1717 connection
.callbacks
->OnError(IndexedDBDatabaseError(
1718 blink::WebIDBDatabaseExceptionUnknownError
, message
));
1723 // We infer that the database didn't exist from its lack of either type of
1725 bool is_new_database
=
1726 metadata_
.version
== kNoStringVersion
&&
1727 metadata_
.int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1729 if (connection
.version
== IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
) {
1730 // For unit tests only - skip upgrade steps. Calling from script with
1731 // DEFAULT_INT_VERSION throws exception.
1732 // TODO(jsbell): DCHECK that not in unit tests.
1733 DCHECK(is_new_database
);
1734 connection
.callbacks
->OnSuccess(
1735 CreateConnection(connection
.database_callbacks
,
1736 connection
.child_process_id
),
1741 // We may need to change the version.
1742 int64 local_version
= connection
.version
;
1743 if (local_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
) {
1744 if (!is_new_database
) {
1745 connection
.callbacks
->OnSuccess(
1746 CreateConnection(connection
.database_callbacks
,
1747 connection
.child_process_id
),
1751 // Spec says: If no version is specified and no database exists, set
1752 // database version to 1.
1756 if (local_version
> metadata_
.int_version
) {
1757 RunVersionChangeTransaction(connection
.callbacks
,
1758 CreateConnection(connection
.database_callbacks
,
1759 connection
.child_process_id
),
1760 connection
.transaction_id
,
1764 if (local_version
< metadata_
.int_version
) {
1765 connection
.callbacks
->OnError(IndexedDBDatabaseError(
1766 blink::WebIDBDatabaseExceptionVersionError
,
1767 ASCIIToUTF16("The requested version (") +
1768 Int64ToString16(local_version
) +
1769 ASCIIToUTF16(") is less than the existing version (") +
1770 Int64ToString16(metadata_
.int_version
) + ASCIIToUTF16(").")));
1773 DCHECK_EQ(local_version
, metadata_
.int_version
);
1774 connection
.callbacks
->OnSuccess(
1775 CreateConnection(connection
.database_callbacks
,
1776 connection
.child_process_id
),
1780 void IndexedDBDatabase::RunVersionChangeTransaction(
1781 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1782 scoped_ptr
<IndexedDBConnection
> connection
,
1783 int64 transaction_id
,
1784 int64 requested_version
) {
1785 DCHECK(callbacks
.get());
1786 DCHECK(connections_
.count(connection
.get()));
1787 if (ConnectionCount() > 1) {
1788 DCHECK_NE(blink::WebIDBDataLossTotal
, callbacks
->data_loss());
1789 // Front end ensures the event is not fired at connections that have
1790 // close_pending set.
1791 for (const auto* iter
: connections_
) {
1792 if (iter
!= connection
.get()) {
1793 iter
->callbacks()->OnVersionChange(metadata_
.int_version
,
1797 // OnBlocked will be fired at the request when one of the other
1798 // connections acks that the OnVersionChange was ignored.
1800 DCHECK(!pending_run_version_change_transaction_call_
);
1801 pending_run_version_change_transaction_call_
.reset(new PendingUpgradeCall(
1802 callbacks
, connection
.Pass(), transaction_id
, requested_version
));
1805 RunVersionChangeTransactionFinal(
1806 callbacks
, connection
.Pass(), transaction_id
, requested_version
);
1809 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1810 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1811 scoped_ptr
<IndexedDBConnection
> connection
,
1812 int64 transaction_id
,
1813 int64 requested_version
) {
1815 std::vector
<int64
> object_store_ids
;
1816 CreateTransaction(transaction_id
,
1819 blink::WebIDBTransactionModeVersionChange
);
1821 transactions_
[transaction_id
]->ScheduleTask(
1822 base::Bind(&IndexedDBDatabase::VersionChangeOperation
,
1826 base::Passed(&connection
)));
1827 DCHECK(!pending_second_half_open_
);
1830 void IndexedDBDatabase::DeleteDatabase(
1831 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1833 if (IsDeleteDatabaseBlocked()) {
1834 for (const auto* connection
: connections_
) {
1835 // Front end ensures the event is not fired at connections that have
1836 // close_pending set.
1837 connection
->callbacks()->OnVersionChange(
1838 metadata_
.int_version
, IndexedDBDatabaseMetadata::NO_INT_VERSION
);
1840 // OnBlocked will be fired at the request when one of the other
1841 // connections acks that the OnVersionChange was ignored.
1843 pending_delete_calls_
.push_back(new PendingDeleteCall(callbacks
));
1846 DeleteDatabaseFinal(callbacks
);
1849 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1850 return !!ConnectionCount();
1853 void IndexedDBDatabase::DeleteDatabaseFinal(
1854 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1855 DCHECK(!IsDeleteDatabaseBlocked());
1856 DCHECK(backing_store_
.get());
1857 leveldb::Status s
= backing_store_
->DeleteDatabase(metadata_
.name
);
1859 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1860 "Internal error deleting database.");
1861 callbacks
->OnError(error
);
1862 if (s
.IsCorruption()) {
1863 GURL origin_url
= backing_store_
->origin_url();
1864 backing_store_
= NULL
;
1865 factory_
->HandleBackingStoreCorruption(origin_url
, error
);
1869 int64 old_version
= metadata_
.int_version
;
1870 metadata_
.version
= kNoStringVersion
;
1871 metadata_
.id
= kInvalidId
;
1872 metadata_
.int_version
= IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1873 metadata_
.object_stores
.clear();
1874 callbacks
->OnSuccess(old_version
);
1875 factory_
->DatabaseDeleted(identifier_
);
1878 void IndexedDBDatabase::ForceClose() {
1879 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1880 scoped_refptr
<IndexedDBDatabase
> protect(this);
1881 ConnectionSet::const_iterator it
= connections_
.begin();
1882 while (it
!= connections_
.end()) {
1883 IndexedDBConnection
* connection
= *it
++;
1884 connection
->ForceClose();
1886 DCHECK(connections_
.empty());
1889 void IndexedDBDatabase::VersionChangeIgnored() {
1890 if (pending_run_version_change_transaction_call_
)
1891 pending_run_version_change_transaction_call_
->callbacks()->OnBlocked(
1892 metadata_
.int_version
);
1894 for (const auto& pending_delete_call
: pending_delete_calls_
)
1895 pending_delete_call
->callbacks()->OnBlocked(metadata_
.int_version
);
1899 void IndexedDBDatabase::Close(IndexedDBConnection
* connection
, bool forced
) {
1900 DCHECK(connections_
.count(connection
));
1901 DCHECK(connection
->IsConnected());
1902 DCHECK(connection
->database() == this);
1904 IDB_TRACE("IndexedDBDatabase::Close");
1906 connections_
.erase(connection
);
1908 // Abort outstanding transactions from the closing connection. This
1909 // can not happen if the close is requested by the connection itself
1910 // as the front-end defers the close until all transactions are
1911 // complete, but can occur on process termination or forced close.
1913 TransactionMap
transactions(transactions_
);
1914 for (const auto& it
: transactions
) {
1915 if (it
.second
->connection() == connection
->callbacks())
1917 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
,
1918 "Connection is closing."));
1922 if (pending_second_half_open_
&&
1923 pending_second_half_open_
->connection() == connection
) {
1924 pending_second_half_open_
->callbacks()->OnError(
1925 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError
,
1926 "The connection was closed."));
1927 pending_second_half_open_
.reset();
1930 ProcessPendingCalls();
1932 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1933 if (!ConnectionCount() && !pending_open_calls_
.size() &&
1934 !pending_delete_calls_
.size()) {
1935 DCHECK(transactions_
.empty());
1937 const GURL origin_url
= backing_store_
->origin_url();
1938 backing_store_
= NULL
;
1940 factory_
->ReleaseDatabase(identifier_
, forced
);
1944 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1945 int64 object_store_id
,
1946 IndexedDBTransaction
* transaction
) {
1947 DCHECK(!transaction
);
1948 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
1949 RemoveObjectStore(object_store_id
);
1952 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1953 const IndexedDBObjectStoreMetadata
& object_store_metadata
,
1954 IndexedDBTransaction
* transaction
) {
1955 DCHECK(!transaction
);
1956 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation");
1957 AddObjectStore(object_store_metadata
,
1958 IndexedDBObjectStoreMetadata::kInvalidId
);
1961 void IndexedDBDatabase::VersionChangeAbortOperation(
1962 const base::string16
& previous_version
,
1963 int64 previous_int_version
,
1964 IndexedDBTransaction
* transaction
) {
1965 DCHECK(!transaction
);
1966 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
1967 metadata_
.version
= previous_version
;
1968 metadata_
.int_version
= previous_int_version
;
1971 } // namespace content