1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/indexed_db/indexed_db_database.h"
10 #include "base/auto_reset.h"
11 #include "base/logging.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/memory/scoped_vector.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "content/browser/indexed_db/indexed_db_blob_info.h"
19 #include "content/browser/indexed_db/indexed_db_connection.h"
20 #include "content/browser/indexed_db/indexed_db_context_impl.h"
21 #include "content/browser/indexed_db/indexed_db_cursor.h"
22 #include "content/browser/indexed_db/indexed_db_factory.h"
23 #include "content/browser/indexed_db/indexed_db_index_writer.h"
24 #include "content/browser/indexed_db/indexed_db_pending_connection.h"
25 #include "content/browser/indexed_db/indexed_db_return_value.h"
26 #include "content/browser/indexed_db/indexed_db_tracing.h"
27 #include "content/browser/indexed_db/indexed_db_transaction.h"
28 #include "content/browser/indexed_db/indexed_db_value.h"
29 #include "content/common/indexed_db/indexed_db_key_path.h"
30 #include "content/common/indexed_db/indexed_db_key_range.h"
31 #include "storage/browser/blob/blob_data_handle.h"
32 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
33 #include "third_party/leveldatabase/env_chromium.h"
35 using base::ASCIIToUTF16
;
36 using base::Int64ToString16
;
37 using blink::WebIDBKeyTypeNumber
;
43 // Used for WebCore.IndexedDB.Schema.ObjectStore.KeyPathType and
44 // WebCore.IndexedDB.Schema.Index.KeyPathType histograms. Do not
45 // modify (delete, re-order, renumber) these values other than
47 enum HistogramIDBKeyPathType
{
48 KEY_PATH_TYPE_NONE
= 0,
49 KEY_PATH_TYPE_STRING
= 1,
50 KEY_PATH_TYPE_ARRAY
= 2,
51 KEY_PATH_TYPE_MAX
= 3, // Keep as last/max entry, for histogram range.
54 HistogramIDBKeyPathType
HistogramKeyPathType(const IndexedDBKeyPath
& key_path
) {
55 switch (key_path
.type()) {
56 case blink::WebIDBKeyPathTypeNull
:
57 return KEY_PATH_TYPE_NONE
;
58 case blink::WebIDBKeyPathTypeString
:
59 return KEY_PATH_TYPE_STRING
;
60 case blink::WebIDBKeyPathTypeArray
:
61 return KEY_PATH_TYPE_ARRAY
;
64 return KEY_PATH_TYPE_NONE
;
69 // PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the
70 // in-progress connection.
71 class IndexedDBDatabase::PendingUpgradeCall
{
73 PendingUpgradeCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
,
74 scoped_ptr
<IndexedDBConnection
> connection
,
77 : callbacks_(callbacks
),
78 connection_(connection
.Pass()),
80 transaction_id_(transaction_id
) {}
81 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
82 // Takes ownership of the connection object.
83 scoped_ptr
<IndexedDBConnection
> ReleaseConnection() WARN_UNUSED_RESULT
{
84 return connection_
.Pass();
86 int64
version() const { return version_
; }
87 int64
transaction_id() const { return transaction_id_
; }
90 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
91 scoped_ptr
<IndexedDBConnection
> connection_
;
93 const int64 transaction_id_
;
96 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
97 // owned elsewhere, but we need to cancel the success call if that connection
98 // closes before it is sent.
99 class IndexedDBDatabase::PendingSuccessCall
{
101 PendingSuccessCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
,
102 IndexedDBConnection
* connection
,
104 : callbacks_(callbacks
), connection_(connection
), version_(version
) {}
105 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
106 IndexedDBConnection
* connection() const { return connection_
; }
107 int64
version() const { return version_
; }
110 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
111 IndexedDBConnection
* connection_
;
115 class IndexedDBDatabase::PendingDeleteCall
{
117 explicit PendingDeleteCall(scoped_refptr
<IndexedDBCallbacks
> callbacks
)
118 : callbacks_(callbacks
) {}
119 scoped_refptr
<IndexedDBCallbacks
> callbacks() const { return callbacks_
; }
122 scoped_refptr
<IndexedDBCallbacks
> callbacks_
;
125 scoped_refptr
<IndexedDBDatabase
> IndexedDBDatabase::Create(
126 const base::string16
& name
,
127 IndexedDBBackingStore
* backing_store
,
128 IndexedDBFactory
* factory
,
129 const Identifier
& unique_identifier
,
130 leveldb::Status
* s
) {
131 scoped_refptr
<IndexedDBDatabase
> database
=
132 new IndexedDBDatabase(name
, backing_store
, factory
, unique_identifier
);
133 *s
= database
->OpenInternal();
141 const base::string16::value_type kNoStringVersion
[] = {0};
144 IndexedDBDatabase::IndexedDBDatabase(const base::string16
& name
,
145 IndexedDBBackingStore
* backing_store
,
146 IndexedDBFactory
* factory
,
147 const Identifier
& unique_identifier
)
148 : backing_store_(backing_store
),
152 IndexedDBDatabaseMetadata::NO_INT_VERSION
,
154 identifier_(unique_identifier
),
156 DCHECK(factory
!= NULL
);
159 void IndexedDBDatabase::AddObjectStore(
160 const IndexedDBObjectStoreMetadata
& object_store
,
161 int64 new_max_object_store_id
) {
162 DCHECK(metadata_
.object_stores
.find(object_store
.id
) ==
163 metadata_
.object_stores
.end());
164 if (new_max_object_store_id
!= IndexedDBObjectStoreMetadata::kInvalidId
) {
165 DCHECK_LT(metadata_
.max_object_store_id
, new_max_object_store_id
);
166 metadata_
.max_object_store_id
= new_max_object_store_id
;
168 metadata_
.object_stores
[object_store
.id
] = object_store
;
171 void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id
) {
172 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
173 metadata_
.object_stores
.end());
174 metadata_
.object_stores
.erase(object_store_id
);
177 void IndexedDBDatabase::AddIndex(int64 object_store_id
,
178 const IndexedDBIndexMetadata
& index
,
179 int64 new_max_index_id
) {
180 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
181 metadata_
.object_stores
.end());
182 IndexedDBObjectStoreMetadata object_store
=
183 metadata_
.object_stores
[object_store_id
];
185 DCHECK(object_store
.indexes
.find(index
.id
) == object_store
.indexes
.end());
186 object_store
.indexes
[index
.id
] = index
;
187 if (new_max_index_id
!= IndexedDBIndexMetadata::kInvalidId
) {
188 DCHECK_LT(object_store
.max_index_id
, new_max_index_id
);
189 object_store
.max_index_id
= new_max_index_id
;
191 metadata_
.object_stores
[object_store_id
] = object_store
;
194 void IndexedDBDatabase::RemoveIndex(int64 object_store_id
, int64 index_id
) {
195 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
196 metadata_
.object_stores
.end());
197 IndexedDBObjectStoreMetadata object_store
=
198 metadata_
.object_stores
[object_store_id
];
200 DCHECK(object_store
.indexes
.find(index_id
) != object_store
.indexes
.end());
201 object_store
.indexes
.erase(index_id
);
202 metadata_
.object_stores
[object_store_id
] = object_store
;
205 leveldb::Status
IndexedDBDatabase::OpenInternal() {
206 bool success
= false;
207 leveldb::Status s
= backing_store_
->GetIDBDatabaseMetaData(
208 metadata_
.name
, &metadata_
, &success
);
209 DCHECK(success
== (metadata_
.id
!= kInvalidId
)) << "success = " << success
210 << " id = " << metadata_
.id
;
214 return backing_store_
->GetObjectStores(metadata_
.id
,
215 &metadata_
.object_stores
);
217 return backing_store_
->CreateIDBDatabaseMetaData(
218 metadata_
.name
, metadata_
.version
, metadata_
.int_version
, &metadata_
.id
);
221 IndexedDBDatabase::~IndexedDBDatabase() {
222 DCHECK(transactions_
.empty());
223 DCHECK(pending_open_calls_
.empty());
224 DCHECK(pending_delete_calls_
.empty());
227 scoped_ptr
<IndexedDBConnection
> IndexedDBDatabase::CreateConnection(
228 scoped_refptr
<IndexedDBDatabaseCallbacks
> database_callbacks
,
229 int child_process_id
) {
230 scoped_ptr
<IndexedDBConnection
> connection(
231 new IndexedDBConnection(this, database_callbacks
));
232 connections_
.insert(connection
.get());
233 backing_store_
->GrantChildProcessPermissions(child_process_id
);
234 return connection
.Pass();
237 IndexedDBTransaction
* IndexedDBDatabase::GetTransaction(
238 int64 transaction_id
) const {
239 TransactionMap::const_iterator trans_iterator
=
240 transactions_
.find(transaction_id
);
241 if (trans_iterator
== transactions_
.end())
243 return trans_iterator
->second
;
246 bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id
) const {
247 if (!ContainsKey(metadata_
.object_stores
, object_store_id
)) {
248 DLOG(ERROR
) << "Invalid object_store_id";
254 bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id
,
255 int64 index_id
) const {
256 if (!ValidateObjectStoreId(object_store_id
))
258 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
259 metadata_
.object_stores
.find(object_store_id
)->second
;
260 if (!ContainsKey(object_store_metadata
.indexes
, index_id
)) {
261 DLOG(ERROR
) << "Invalid index_id";
267 bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId(
268 int64 object_store_id
,
269 int64 index_id
) const {
270 if (!ValidateObjectStoreId(object_store_id
))
272 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
273 metadata_
.object_stores
.find(object_store_id
)->second
;
274 if (index_id
!= IndexedDBIndexMetadata::kInvalidId
&&
275 !ContainsKey(object_store_metadata
.indexes
, index_id
)) {
276 DLOG(ERROR
) << "Invalid index_id";
282 bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId(
283 int64 object_store_id
,
284 int64 index_id
) const {
285 if (!ValidateObjectStoreId(object_store_id
))
287 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
288 metadata_
.object_stores
.find(object_store_id
)->second
;
289 if (ContainsKey(object_store_metadata
.indexes
, index_id
)) {
290 DLOG(ERROR
) << "Invalid index_id";
296 void IndexedDBDatabase::CreateObjectStore(int64 transaction_id
,
297 int64 object_store_id
,
298 const base::string16
& name
,
299 const IndexedDBKeyPath
& key_path
,
300 bool auto_increment
) {
301 IDB_TRACE1("IndexedDBDatabase::CreateObjectStore", "txn.id", transaction_id
);
302 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
305 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
307 if (ContainsKey(metadata_
.object_stores
, object_store_id
)) {
308 DLOG(ERROR
) << "Invalid object_store_id";
312 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.ObjectStore.KeyPathType",
313 HistogramKeyPathType(key_path
), KEY_PATH_TYPE_MAX
);
314 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.ObjectStore.AutoIncrement",
317 // Store creation is done synchronously, as it may be followed by
318 // index creation (also sync) since preemptive OpenCursor/SetIndexKeys
320 IndexedDBObjectStoreMetadata
object_store_metadata(
325 IndexedDBDatabase::kMinimumIndexId
);
328 backing_store_
->CreateObjectStore(transaction
->BackingStoreTransaction(),
329 transaction
->database()->id(),
330 object_store_metadata
.id
,
331 object_store_metadata
.name
,
332 object_store_metadata
.key_path
,
333 object_store_metadata
.auto_increment
);
335 IndexedDBDatabaseError
error(
336 blink::WebIDBDatabaseExceptionUnknownError
,
337 ASCIIToUTF16("Internal error creating object store '") +
338 object_store_metadata
.name
+ ASCIIToUTF16("'."));
339 transaction
->Abort(error
);
340 if (s
.IsCorruption())
341 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
346 AddObjectStore(object_store_metadata
, object_store_id
);
347 transaction
->ScheduleAbortTask(
348 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation
,
353 void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id
,
354 int64 object_store_id
) {
355 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStore", "txn.id", transaction_id
);
356 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
359 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
361 if (!ValidateObjectStoreId(object_store_id
))
364 transaction
->ScheduleTask(
365 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation
,
370 void IndexedDBDatabase::CreateIndex(int64 transaction_id
,
371 int64 object_store_id
,
373 const base::string16
& name
,
374 const IndexedDBKeyPath
& key_path
,
377 IDB_TRACE1("IndexedDBDatabase::CreateIndex", "txn.id", transaction_id
);
378 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
381 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
383 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id
, index_id
))
386 UMA_HISTOGRAM_ENUMERATION("WebCore.IndexedDB.Schema.Index.KeyPathType",
387 HistogramKeyPathType(key_path
), KEY_PATH_TYPE_MAX
);
388 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.Unique", unique
);
389 UMA_HISTOGRAM_BOOLEAN("WebCore.IndexedDB.Schema.Index.MultiEntry",
392 // Index creation is done synchronously since preemptive
393 // OpenCursor/SetIndexKeys may follow.
394 const IndexedDBIndexMetadata
index_metadata(
395 name
, index_id
, key_path
, unique
, multi_entry
);
397 if (!backing_store_
->CreateIndex(transaction
->BackingStoreTransaction(),
398 transaction
->database()->id(),
402 index_metadata
.key_path
,
403 index_metadata
.unique
,
404 index_metadata
.multi_entry
).ok()) {
405 base::string16 error_string
=
406 ASCIIToUTF16("Internal error creating index '") +
407 index_metadata
.name
+ ASCIIToUTF16("'.");
408 transaction
->Abort(IndexedDBDatabaseError(
409 blink::WebIDBDatabaseExceptionUnknownError
, error_string
));
413 AddIndex(object_store_id
, index_metadata
, index_id
);
414 transaction
->ScheduleAbortTask(
415 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation
,
421 void IndexedDBDatabase::CreateIndexAbortOperation(
422 int64 object_store_id
,
424 IndexedDBTransaction
* transaction
) {
425 IDB_TRACE1("IndexedDBDatabase::CreateIndexAbortOperation",
428 DCHECK(!transaction
);
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_TRACE1("IndexedDBDatabase::DeleteIndexAbortOperation",
495 AddIndex(object_store_id
, index_metadata
, IndexedDBIndexMetadata::kInvalidId
);
498 void IndexedDBDatabase::Commit(int64 transaction_id
) {
499 // The frontend suggests that we commit, but we may have previously initiated
500 // an abort, and so have disposed of the transaction. on_abort has already
501 // been dispatched to the frontend, so it will find out about that
503 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
505 scoped_refptr
<IndexedDBFactory
> factory
= factory_
;
506 leveldb::Status s
= transaction
->Commit();
507 if (s
.IsCorruption()) {
508 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
509 "Internal error committing transaction.");
510 factory
->HandleBackingStoreCorruption(identifier_
.first
, error
);
515 void IndexedDBDatabase::Abort(int64 transaction_id
) {
516 // If the transaction is unknown, then it has already been aborted by the
517 // backend before this call so it is safe to ignore it.
518 IDB_TRACE1("IndexedDBDatabase::Abort", "txn.id", transaction_id
);
519 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
521 transaction
->Abort();
524 void IndexedDBDatabase::Abort(int64 transaction_id
,
525 const IndexedDBDatabaseError
& error
) {
526 IDB_TRACE1("IndexedDBDatabase::Abort(error)", "txn.id", transaction_id
);
527 // If the transaction is unknown, then it has already been aborted by the
528 // backend before this call so it is safe to ignore it.
529 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
531 transaction
->Abort(error
);
534 void IndexedDBDatabase::Get(int64 transaction_id
,
535 int64 object_store_id
,
537 scoped_ptr
<IndexedDBKeyRange
> key_range
,
539 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
540 IDB_TRACE1("IndexedDBDatabase::Get", "txn.id", transaction_id
);
541 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
545 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
548 transaction
->ScheduleTask(base::Bind(
549 &IndexedDBDatabase::GetOperation
,
554 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
,
558 void IndexedDBDatabase::GetOperation(
559 int64 object_store_id
,
561 scoped_ptr
<IndexedDBKeyRange
> key_range
,
562 indexed_db::CursorType cursor_type
,
563 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
564 IndexedDBTransaction
* transaction
) {
565 IDB_TRACE1("IndexedDBDatabase::GetOperation", "txn.id", transaction
->id());
567 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
568 metadata_
.object_stores
.end());
569 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
570 metadata_
.object_stores
[object_store_id
];
572 const IndexedDBKey
* key
;
575 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
576 if (key_range
->IsOnlyKey()) {
577 key
= &key_range
->lower();
579 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
580 DCHECK_NE(cursor_type
, indexed_db::CURSOR_KEY_ONLY
);
581 // ObjectStore Retrieval Operation
582 backing_store_cursor
= backing_store_
->OpenObjectStoreCursor(
583 transaction
->BackingStoreTransaction(),
587 blink::WebIDBCursorDirectionNext
,
589 } else if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
590 // Index Value Retrieval Operation
591 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
592 transaction
->BackingStoreTransaction(),
597 blink::WebIDBCursorDirectionNext
,
600 // Index Referenced Value Retrieval Operation
601 backing_store_cursor
= backing_store_
->OpenIndexCursor(
602 transaction
->BackingStoreTransaction(),
607 blink::WebIDBCursorDirectionNext
,
612 DLOG(ERROR
) << "Unable to open cursor operation: " << s
.ToString();
613 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
614 "Internal error deleting data in range");
615 if (s
.IsCorruption()) {
616 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
621 if (!backing_store_cursor
) {
622 callbacks
->OnSuccess();
626 key
= &backing_store_cursor
->key();
629 scoped_ptr
<IndexedDBKey
> primary_key
;
630 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
631 // Object Store Retrieval Operation
632 IndexedDBReturnValue value
;
633 s
= backing_store_
->GetRecord(transaction
->BackingStoreTransaction(),
639 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
640 "Internal error in GetRecord.");
641 callbacks
->OnError(error
);
643 if (s
.IsCorruption())
644 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
650 callbacks
->OnSuccess();
654 if (object_store_metadata
.auto_increment
&&
655 !object_store_metadata
.key_path
.IsNull()) {
656 value
.primary_key
= *key
;
657 value
.key_path
= object_store_metadata
.key_path
;
660 callbacks
->OnSuccess(&value
);
664 // From here we are dealing only with indexes.
665 s
= backing_store_
->GetPrimaryKeyViaIndex(
666 transaction
->BackingStoreTransaction(),
673 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
674 "Internal error in GetPrimaryKeyViaIndex.");
675 callbacks
->OnError(error
);
676 if (s
.IsCorruption())
677 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
682 callbacks
->OnSuccess();
685 if (cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
686 // Index Value Retrieval Operation
687 callbacks
->OnSuccess(*primary_key
);
691 // Index Referenced Value Retrieval Operation
692 IndexedDBReturnValue value
;
693 s
= backing_store_
->GetRecord(transaction
->BackingStoreTransaction(),
699 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
700 "Internal error in GetRecord.");
701 callbacks
->OnError(error
);
702 if (s
.IsCorruption())
703 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
709 callbacks
->OnSuccess();
712 if (object_store_metadata
.auto_increment
&&
713 !object_store_metadata
.key_path
.IsNull()) {
714 value
.primary_key
= *primary_key
;
715 value
.key_path
= object_store_metadata
.key_path
;
717 callbacks
->OnSuccess(&value
);
720 static scoped_ptr
<IndexedDBKey
> GenerateKey(
721 IndexedDBBackingStore
* backing_store
,
722 IndexedDBTransaction
* transaction
,
724 int64 object_store_id
) {
725 const int64 max_generator_value
=
726 9007199254740992LL; // Maximum integer storable as ECMAScript number.
727 int64 current_number
;
728 leveldb::Status s
= backing_store
->GetKeyGeneratorCurrentNumber(
729 transaction
->BackingStoreTransaction(),
734 LOG(ERROR
) << "Failed to GetKeyGeneratorCurrentNumber";
735 return make_scoped_ptr(new IndexedDBKey());
737 if (current_number
< 0 || current_number
> max_generator_value
)
738 return make_scoped_ptr(new IndexedDBKey());
740 return make_scoped_ptr(new IndexedDBKey(current_number
, WebIDBKeyTypeNumber
));
743 static leveldb::Status
UpdateKeyGenerator(IndexedDBBackingStore
* backing_store
,
744 IndexedDBTransaction
* transaction
,
746 int64 object_store_id
,
747 const IndexedDBKey
& key
,
748 bool check_current
) {
749 DCHECK_EQ(WebIDBKeyTypeNumber
, key
.type());
750 return backing_store
->MaybeUpdateKeyGeneratorCurrentNumber(
751 transaction
->BackingStoreTransaction(),
754 static_cast<int64
>(floor(key
.number())) + 1,
758 struct IndexedDBDatabase::PutOperationParams
{
759 PutOperationParams() {}
760 int64 object_store_id
;
761 IndexedDBValue value
;
762 ScopedVector
<storage::BlobDataHandle
> handles
;
763 scoped_ptr
<IndexedDBKey
> key
;
764 blink::WebIDBPutMode put_mode
;
765 scoped_refptr
<IndexedDBCallbacks
> callbacks
;
766 std::vector
<IndexKeys
> index_keys
;
769 DISALLOW_COPY_AND_ASSIGN(PutOperationParams
);
772 void IndexedDBDatabase::Put(int64 transaction_id
,
773 int64 object_store_id
,
774 IndexedDBValue
* value
,
775 ScopedVector
<storage::BlobDataHandle
>* handles
,
776 scoped_ptr
<IndexedDBKey
> key
,
777 blink::WebIDBPutMode put_mode
,
778 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
779 const std::vector
<IndexKeys
>& index_keys
) {
780 IDB_TRACE1("IndexedDBDatabase::Put", "txn.id", transaction_id
);
781 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
784 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
786 if (!ValidateObjectStoreId(object_store_id
))
791 scoped_ptr
<PutOperationParams
> params(new PutOperationParams());
792 params
->object_store_id
= object_store_id
;
793 params
->value
.swap(*value
);
794 params
->handles
.swap(*handles
);
795 params
->key
= key
.Pass();
796 params
->put_mode
= put_mode
;
797 params
->callbacks
= callbacks
;
798 params
->index_keys
= index_keys
;
799 transaction
->ScheduleTask(base::Bind(
800 &IndexedDBDatabase::PutOperation
, this, base::Passed(¶ms
)));
803 void IndexedDBDatabase::PutOperation(scoped_ptr
<PutOperationParams
> params
,
804 IndexedDBTransaction
* transaction
) {
805 IDB_TRACE1("IndexedDBDatabase::PutOperation", "txn.id", transaction
->id());
806 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
807 bool key_was_generated
= false;
809 DCHECK(metadata_
.object_stores
.find(params
->object_store_id
) !=
810 metadata_
.object_stores
.end());
811 const IndexedDBObjectStoreMetadata
& object_store
=
812 metadata_
.object_stores
[params
->object_store_id
];
813 DCHECK(object_store
.auto_increment
|| params
->key
->IsValid());
815 scoped_ptr
<IndexedDBKey
> key
;
816 if (params
->put_mode
!= blink::WebIDBPutModeCursorUpdate
&&
817 object_store
.auto_increment
&& !params
->key
->IsValid()) {
818 scoped_ptr
<IndexedDBKey
> auto_inc_key
= GenerateKey(
819 backing_store_
.get(), transaction
, id(), params
->object_store_id
);
820 key_was_generated
= true;
821 if (!auto_inc_key
->IsValid()) {
822 params
->callbacks
->OnError(
823 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError
,
824 "Maximum key generator value reached."));
827 key
= auto_inc_key
.Pass();
829 key
= params
->key
.Pass();
832 DCHECK(key
->IsValid());
834 IndexedDBBackingStore::RecordIdentifier record_identifier
;
835 if (params
->put_mode
== blink::WebIDBPutModeAddOnly
) {
837 leveldb::Status s
= backing_store_
->KeyExistsInObjectStore(
838 transaction
->BackingStoreTransaction(),
840 params
->object_store_id
,
845 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
846 "Internal error checking key existence.");
847 params
->callbacks
->OnError(error
);
848 if (s
.IsCorruption())
849 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
854 params
->callbacks
->OnError(
855 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError
,
856 "Key already exists in the object store."));
861 ScopedVector
<IndexWriter
> index_writers
;
862 base::string16 error_message
;
863 bool obeys_constraints
= false;
864 bool backing_store_success
= MakeIndexWriters(transaction
,
865 backing_store_
.get(),
874 if (!backing_store_success
) {
875 params
->callbacks
->OnError(IndexedDBDatabaseError(
876 blink::WebIDBDatabaseExceptionUnknownError
,
877 "Internal error: backing store error updating index keys."));
880 if (!obeys_constraints
) {
881 params
->callbacks
->OnError(IndexedDBDatabaseError(
882 blink::WebIDBDatabaseExceptionConstraintError
, error_message
));
886 // Before this point, don't do any mutation. After this point, rollback the
887 // transaction in case of error.
889 backing_store_
->PutRecord(transaction
->BackingStoreTransaction(),
891 params
->object_store_id
,
897 IndexedDBDatabaseError
error(
898 blink::WebIDBDatabaseExceptionUnknownError
,
899 "Internal error: backing store error performing put/add.");
900 params
->callbacks
->OnError(error
);
901 if (s
.IsCorruption())
902 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
907 for (size_t i
= 0; i
< index_writers
.size(); ++i
) {
908 IndexWriter
* index_writer
= index_writers
[i
];
909 index_writer
->WriteIndexKeys(record_identifier
,
910 backing_store_
.get(),
911 transaction
->BackingStoreTransaction(),
913 params
->object_store_id
);
916 if (object_store
.auto_increment
&&
917 params
->put_mode
!= blink::WebIDBPutModeCursorUpdate
&&
918 key
->type() == WebIDBKeyTypeNumber
) {
919 leveldb::Status s
= UpdateKeyGenerator(backing_store_
.get(),
922 params
->object_store_id
,
926 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
927 "Internal error updating key generator.");
928 params
->callbacks
->OnError(error
);
929 if (s
.IsCorruption())
930 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
936 params
->callbacks
->OnSuccess(*key
);
939 void IndexedDBDatabase::SetIndexKeys(int64 transaction_id
,
940 int64 object_store_id
,
941 scoped_ptr
<IndexedDBKey
> primary_key
,
942 const std::vector
<IndexKeys
>& index_keys
) {
943 IDB_TRACE1("IndexedDBDatabase::SetIndexKeys", "txn.id", transaction_id
);
944 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
947 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
949 // TODO(alecflett): This method could be asynchronous, but we need to
950 // evaluate if it's worth the extra complexity.
951 IndexedDBBackingStore::RecordIdentifier record_identifier
;
953 leveldb::Status s
= backing_store_
->KeyExistsInObjectStore(
954 transaction
->BackingStoreTransaction(),
961 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
962 "Internal error setting index keys.");
963 transaction
->Abort(error
);
964 if (s
.IsCorruption())
965 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
970 transaction
->Abort(IndexedDBDatabaseError(
971 blink::WebIDBDatabaseExceptionUnknownError
,
972 "Internal error setting index keys for object store."));
976 ScopedVector
<IndexWriter
> index_writers
;
977 base::string16 error_message
;
978 bool obeys_constraints
= false;
979 DCHECK(metadata_
.object_stores
.find(object_store_id
) !=
980 metadata_
.object_stores
.end());
981 const IndexedDBObjectStoreMetadata
& object_store_metadata
=
982 metadata_
.object_stores
[object_store_id
];
983 bool backing_store_success
= MakeIndexWriters(transaction
,
984 backing_store_
.get(),
986 object_store_metadata
,
993 if (!backing_store_success
) {
994 transaction
->Abort(IndexedDBDatabaseError(
995 blink::WebIDBDatabaseExceptionUnknownError
,
996 "Internal error: backing store error updating index keys."));
999 if (!obeys_constraints
) {
1000 transaction
->Abort(IndexedDBDatabaseError(
1001 blink::WebIDBDatabaseExceptionConstraintError
, error_message
));
1005 for (size_t i
= 0; i
< index_writers
.size(); ++i
) {
1006 IndexWriter
* index_writer
= index_writers
[i
];
1007 index_writer
->WriteIndexKeys(record_identifier
,
1008 backing_store_
.get(),
1009 transaction
->BackingStoreTransaction(),
1015 void IndexedDBDatabase::SetIndexesReady(int64 transaction_id
,
1017 const std::vector
<int64
>& index_ids
) {
1018 IDB_TRACE1("IndexedDBDatabase::SetIndexesReady", "txn.id", transaction_id
);
1019 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1022 DCHECK_EQ(transaction
->mode(), blink::WebIDBTransactionModeVersionChange
);
1024 transaction
->ScheduleTask(
1025 blink::WebIDBTaskTypePreemptive
,
1026 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation
,
1031 void IndexedDBDatabase::SetIndexesReadyOperation(
1033 IndexedDBTransaction
* transaction
) {
1034 IDB_TRACE1("IndexedDBDatabase::SetIndexesReadyOperation",
1037 for (size_t i
= 0; i
< index_count
; ++i
)
1038 transaction
->DidCompletePreemptiveEvent();
1041 struct IndexedDBDatabase::OpenCursorOperationParams
{
1042 OpenCursorOperationParams() {}
1043 int64 object_store_id
;
1045 scoped_ptr
<IndexedDBKeyRange
> key_range
;
1046 blink::WebIDBCursorDirection direction
;
1047 indexed_db::CursorType cursor_type
;
1048 blink::WebIDBTaskType task_type
;
1049 scoped_refptr
<IndexedDBCallbacks
> callbacks
;
1052 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams
);
1055 void IndexedDBDatabase::OpenCursor(
1056 int64 transaction_id
,
1057 int64 object_store_id
,
1059 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1060 blink::WebIDBCursorDirection direction
,
1062 blink::WebIDBTaskType task_type
,
1063 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1064 IDB_TRACE1("IndexedDBDatabase::OpenCursor", "txn.id", transaction_id
);
1065 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1069 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
1072 scoped_ptr
<OpenCursorOperationParams
> params(new OpenCursorOperationParams());
1073 params
->object_store_id
= object_store_id
;
1074 params
->index_id
= index_id
;
1075 params
->key_range
= key_range
.Pass();
1076 params
->direction
= direction
;
1077 params
->cursor_type
=
1078 key_only
? indexed_db::CURSOR_KEY_ONLY
: indexed_db::CURSOR_KEY_AND_VALUE
;
1079 params
->task_type
= task_type
;
1080 params
->callbacks
= callbacks
;
1081 transaction
->ScheduleTask(base::Bind(
1082 &IndexedDBDatabase::OpenCursorOperation
, this, base::Passed(¶ms
)));
1085 void IndexedDBDatabase::OpenCursorOperation(
1086 scoped_ptr
<OpenCursorOperationParams
> params
,
1087 IndexedDBTransaction
* transaction
) {
1089 "IndexedDBDatabase::OpenCursorOperation", "txn.id", transaction
->id());
1091 // The frontend has begun indexing, so this pauses the transaction
1092 // until the indexing is complete. This can't happen any earlier
1093 // because we don't want to switch to early mode in case multiple
1094 // indexes are being created in a row, with Put()'s in between.
1095 if (params
->task_type
== blink::WebIDBTaskTypePreemptive
)
1096 transaction
->AddPreemptiveEvent();
1099 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
1100 if (params
->index_id
== IndexedDBIndexMetadata::kInvalidId
) {
1101 if (params
->cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
1102 DCHECK_EQ(params
->task_type
, blink::WebIDBTaskTypeNormal
);
1103 backing_store_cursor
= backing_store_
->OpenObjectStoreKeyCursor(
1104 transaction
->BackingStoreTransaction(),
1106 params
->object_store_id
,
1111 backing_store_cursor
= backing_store_
->OpenObjectStoreCursor(
1112 transaction
->BackingStoreTransaction(),
1114 params
->object_store_id
,
1120 DCHECK_EQ(params
->task_type
, blink::WebIDBTaskTypeNormal
);
1121 if (params
->cursor_type
== indexed_db::CURSOR_KEY_ONLY
) {
1122 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
1123 transaction
->BackingStoreTransaction(),
1125 params
->object_store_id
,
1131 backing_store_cursor
= backing_store_
->OpenIndexCursor(
1132 transaction
->BackingStoreTransaction(),
1134 params
->object_store_id
,
1143 DLOG(ERROR
) << "Unable to open cursor operation: " << s
.ToString();
1144 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1145 "Internal error opening cursor operation");
1146 if (s
.IsCorruption()) {
1147 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1152 if (!backing_store_cursor
) {
1153 // Why is Success being called?
1154 params
->callbacks
->OnSuccess(nullptr);
1158 scoped_refptr
<IndexedDBCursor
> cursor
=
1159 new IndexedDBCursor(backing_store_cursor
.Pass(),
1160 params
->cursor_type
,
1163 params
->callbacks
->OnSuccess(
1164 cursor
, cursor
->key(), cursor
->primary_key(), cursor
->Value());
1167 void IndexedDBDatabase::Count(int64 transaction_id
,
1168 int64 object_store_id
,
1170 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1171 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1172 IDB_TRACE1("IndexedDBDatabase::Count", "txn.id", transaction_id
);
1173 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1177 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id
, index_id
))
1180 transaction
->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation
,
1184 base::Passed(&key_range
),
1188 void IndexedDBDatabase::CountOperation(
1189 int64 object_store_id
,
1191 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1192 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1193 IndexedDBTransaction
* transaction
) {
1194 IDB_TRACE1("IndexedDBDatabase::CountOperation", "txn.id", transaction
->id());
1196 scoped_ptr
<IndexedDBBackingStore::Cursor
> backing_store_cursor
;
1199 if (index_id
== IndexedDBIndexMetadata::kInvalidId
) {
1200 backing_store_cursor
= backing_store_
->OpenObjectStoreKeyCursor(
1201 transaction
->BackingStoreTransaction(),
1205 blink::WebIDBCursorDirectionNext
,
1208 backing_store_cursor
= backing_store_
->OpenIndexKeyCursor(
1209 transaction
->BackingStoreTransaction(),
1214 blink::WebIDBCursorDirectionNext
,
1218 DLOG(ERROR
) << "Unable perform count operation: " << s
.ToString();
1219 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1220 "Internal error performing count operation");
1221 if (s
.IsCorruption()) {
1222 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1226 if (!backing_store_cursor
) {
1227 callbacks
->OnSuccess(count
);
1233 } while (backing_store_cursor
->Continue(&s
));
1235 // TODO(cmumford): Check for database corruption.
1237 callbacks
->OnSuccess(count
);
1240 void IndexedDBDatabase::DeleteRange(
1241 int64 transaction_id
,
1242 int64 object_store_id
,
1243 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1244 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1245 IDB_TRACE1("IndexedDBDatabase::DeleteRange", "txn.id", transaction_id
);
1246 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1249 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
1251 if (!ValidateObjectStoreId(object_store_id
))
1254 transaction
->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation
,
1257 base::Passed(&key_range
),
1261 void IndexedDBDatabase::DeleteRangeOperation(
1262 int64 object_store_id
,
1263 scoped_ptr
<IndexedDBKeyRange
> key_range
,
1264 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1265 IndexedDBTransaction
* transaction
) {
1267 "IndexedDBDatabase::DeleteRangeOperation", "txn.id", transaction
->id());
1269 backing_store_
->DeleteRange(transaction
->BackingStoreTransaction(),
1274 base::string16 error_string
=
1275 ASCIIToUTF16("Internal error deleting data in range");
1276 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1278 transaction
->Abort(error
);
1279 if (s
.IsCorruption()) {
1280 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1285 callbacks
->OnSuccess();
1288 void IndexedDBDatabase::Clear(int64 transaction_id
,
1289 int64 object_store_id
,
1290 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1291 IDB_TRACE1("IndexedDBDatabase::Clear", "txn.id", transaction_id
);
1292 IndexedDBTransaction
* transaction
= GetTransaction(transaction_id
);
1295 DCHECK_NE(transaction
->mode(), blink::WebIDBTransactionModeReadOnly
);
1297 if (!ValidateObjectStoreId(object_store_id
))
1300 transaction
->ScheduleTask(base::Bind(
1301 &IndexedDBDatabase::ClearOperation
, this, object_store_id
, callbacks
));
1304 void IndexedDBDatabase::ClearOperation(
1305 int64 object_store_id
,
1306 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1307 IndexedDBTransaction
* transaction
) {
1308 IDB_TRACE1("IndexedDBDatabase::ClearOperation", "txn.id", transaction
->id());
1309 leveldb::Status s
= backing_store_
->ClearObjectStore(
1310 transaction
->BackingStoreTransaction(), id(), object_store_id
);
1312 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1313 "Internal error clearing object store");
1314 callbacks
->OnError(error
);
1315 if (s
.IsCorruption()) {
1316 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1321 callbacks
->OnSuccess();
1324 void IndexedDBDatabase::DeleteObjectStoreOperation(
1325 int64 object_store_id
,
1326 IndexedDBTransaction
* transaction
) {
1327 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreOperation",
1331 const IndexedDBObjectStoreMetadata object_store_metadata
=
1332 metadata_
.object_stores
[object_store_id
];
1334 backing_store_
->DeleteObjectStore(transaction
->BackingStoreTransaction(),
1335 transaction
->database()->id(),
1338 base::string16 error_string
=
1339 ASCIIToUTF16("Internal error deleting object store '") +
1340 object_store_metadata
.name
+ ASCIIToUTF16("'.");
1341 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1343 transaction
->Abort(error
);
1344 if (s
.IsCorruption())
1345 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(),
1350 RemoveObjectStore(object_store_id
);
1351 transaction
->ScheduleAbortTask(
1352 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation
,
1354 object_store_metadata
));
1357 void IndexedDBDatabase::VersionChangeOperation(
1359 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1360 scoped_ptr
<IndexedDBConnection
> connection
,
1361 IndexedDBTransaction
* transaction
) {
1363 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction
->id());
1364 int64 old_version
= metadata_
.int_version
;
1365 DCHECK_GT(version
, old_version
);
1367 if (!backing_store_
->UpdateIDBDatabaseIntVersion(
1368 transaction
->BackingStoreTransaction(), id(), version
)) {
1369 IndexedDBDatabaseError
error(
1370 blink::WebIDBDatabaseExceptionUnknownError
,
1372 "Internal error writing data to stable storage when "
1373 "updating version."));
1374 callbacks
->OnError(error
);
1375 transaction
->Abort(error
);
1379 transaction
->ScheduleAbortTask(
1380 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation
,
1383 metadata_
.int_version
));
1384 metadata_
.int_version
= version
;
1385 metadata_
.version
= kNoStringVersion
;
1387 DCHECK(!pending_second_half_open_
);
1388 pending_second_half_open_
.reset(
1389 new PendingSuccessCall(callbacks
, connection
.get(), version
));
1390 callbacks
->OnUpgradeNeeded(old_version
, connection
.Pass(), metadata());
1393 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction
* transaction
,
1395 DCHECK(transactions_
.find(transaction
->id()) != transactions_
.end());
1396 DCHECK_EQ(transactions_
[transaction
->id()], transaction
);
1397 transactions_
.erase(transaction
->id());
1399 if (transaction
->mode() == blink::WebIDBTransactionModeVersionChange
) {
1400 if (pending_second_half_open_
) {
1402 DCHECK_EQ(pending_second_half_open_
->version(), metadata_
.int_version
);
1403 DCHECK(metadata_
.id
!= kInvalidId
);
1405 // Connection was already minted for OnUpgradeNeeded callback.
1406 scoped_ptr
<IndexedDBConnection
> connection
;
1407 pending_second_half_open_
->callbacks()->OnSuccess(connection
.Pass(),
1410 pending_second_half_open_
->callbacks()->OnError(
1411 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError
,
1412 "Version change transaction was aborted in "
1413 "upgradeneeded event handler."));
1415 pending_second_half_open_
.reset();
1418 // Connection queue is now unblocked.
1419 ProcessPendingCalls();
1423 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status
& status
) {
1424 if (status
.IsCorruption()) {
1425 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1426 "Error committing transaction");
1427 factory_
->HandleBackingStoreCorruption(backing_store_
->origin_url(), error
);
1429 factory_
->HandleBackingStoreFailure(backing_store_
->origin_url());
1433 size_t IndexedDBDatabase::ConnectionCount() const {
1434 // This does not include pending open calls, as those should not block version
1435 // changes and deletes.
1436 return connections_
.size();
1439 size_t IndexedDBDatabase::PendingOpenCount() const {
1440 return pending_open_calls_
.size();
1443 size_t IndexedDBDatabase::PendingUpgradeCount() const {
1444 return pending_run_version_change_transaction_call_
? 1 : 0;
1447 size_t IndexedDBDatabase::RunningUpgradeCount() const {
1448 return pending_second_half_open_
? 1 : 0;
1451 size_t IndexedDBDatabase::PendingDeleteCount() const {
1452 return pending_delete_calls_
.size();
1455 void IndexedDBDatabase::ProcessPendingCalls() {
1456 if (pending_run_version_change_transaction_call_
&& ConnectionCount() == 1) {
1457 DCHECK(pending_run_version_change_transaction_call_
->version() >
1458 metadata_
.int_version
);
1459 scoped_ptr
<PendingUpgradeCall
> pending_call
=
1460 pending_run_version_change_transaction_call_
.Pass();
1461 RunVersionChangeTransactionFinal(pending_call
->callbacks(),
1462 pending_call
->ReleaseConnection(),
1463 pending_call
->transaction_id(),
1464 pending_call
->version());
1465 DCHECK_EQ(1u, ConnectionCount());
1466 // Fall through would be a no-op, since transaction must complete
1468 DCHECK(IsDeleteDatabaseBlocked());
1469 DCHECK(IsOpenConnectionBlocked());
1473 if (!IsDeleteDatabaseBlocked()) {
1474 PendingDeleteCallList pending_delete_calls
;
1475 pending_delete_calls_
.swap(pending_delete_calls
);
1476 while (!pending_delete_calls
.empty()) {
1477 // Only the first delete call will delete the database, but each must fire
1479 scoped_ptr
<PendingDeleteCall
> pending_delete_call(
1480 pending_delete_calls
.front());
1481 pending_delete_calls
.pop_front();
1482 DeleteDatabaseFinal(pending_delete_call
->callbacks());
1484 // delete_database_final should never re-queue calls.
1485 DCHECK(pending_delete_calls_
.empty());
1486 // Fall through when complete, as pending opens may be unblocked.
1489 if (!IsOpenConnectionBlocked()) {
1490 PendingOpenCallList pending_open_calls
;
1491 pending_open_calls_
.swap(pending_open_calls
);
1492 while (!pending_open_calls
.empty()) {
1493 OpenConnection(pending_open_calls
.front());
1494 pending_open_calls
.pop_front();
1499 void IndexedDBDatabase::CreateTransaction(
1500 int64 transaction_id
,
1501 IndexedDBConnection
* connection
,
1502 const std::vector
<int64
>& object_store_ids
,
1503 blink::WebIDBTransactionMode mode
) {
1504 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id
);
1505 DCHECK(connections_
.count(connection
));
1506 DCHECK(transactions_
.find(transaction_id
) == transactions_
.end());
1507 if (transactions_
.find(transaction_id
) != transactions_
.end())
1510 // The transaction will add itself to this database's coordinator, which
1511 // manages the lifetime of the object.
1512 TransactionCreated(new IndexedDBTransaction(
1514 connection
->callbacks(),
1515 std::set
<int64
>(object_store_ids
.begin(), object_store_ids
.end()),
1518 new IndexedDBBackingStore::Transaction(backing_store_
.get())));
1521 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction
* transaction
) {
1522 transactions_
[transaction
->id()] = transaction
;
1525 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1526 return !pending_delete_calls_
.empty() ||
1527 transaction_coordinator_
.IsRunningVersionChangeTransaction() ||
1528 pending_run_version_change_transaction_call_
;
1531 void IndexedDBDatabase::OpenConnection(
1532 const IndexedDBPendingConnection
& connection
) {
1533 DCHECK(backing_store_
.get());
1535 // TODO(jsbell): Should have a priority queue so that higher version
1536 // requests are processed first. http://crbug.com/225850
1537 if (IsOpenConnectionBlocked()) {
1538 // The backing store only detects data loss when it is first opened. The
1539 // presence of existing connections means we didn't even check for data loss
1540 // so there'd better not be any.
1541 DCHECK_NE(blink::WebIDBDataLossTotal
, connection
.callbacks
->data_loss());
1542 pending_open_calls_
.push_back(connection
);
1546 if (metadata_
.id
== kInvalidId
) {
1547 // The database was deleted then immediately re-opened; OpenInternal()
1548 // recreates it in the backing store.
1549 if (OpenInternal().ok()) {
1550 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION
,
1551 metadata_
.int_version
);
1553 base::string16 message
;
1554 if (connection
.version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
) {
1555 message
= ASCIIToUTF16(
1556 "Internal error opening database with no version specified.");
1559 ASCIIToUTF16("Internal error opening database with version ") +
1560 Int64ToString16(connection
.version
);
1562 connection
.callbacks
->OnError(IndexedDBDatabaseError(
1563 blink::WebIDBDatabaseExceptionUnknownError
, message
));
1568 // We infer that the database didn't exist from its lack of either type of
1570 bool is_new_database
=
1571 metadata_
.version
== kNoStringVersion
&&
1572 metadata_
.int_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1574 if (connection
.version
== IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION
) {
1575 // For unit tests only - skip upgrade steps. Calling from script with
1576 // DEFAULT_INT_VERSION throws exception.
1577 // TODO(jsbell): DCHECK that not in unit tests.
1578 DCHECK(is_new_database
);
1579 connection
.callbacks
->OnSuccess(
1580 CreateConnection(connection
.database_callbacks
,
1581 connection
.child_process_id
),
1586 // We may need to change the version.
1587 int64 local_version
= connection
.version
;
1588 if (local_version
== IndexedDBDatabaseMetadata::NO_INT_VERSION
) {
1589 if (!is_new_database
) {
1590 connection
.callbacks
->OnSuccess(
1591 CreateConnection(connection
.database_callbacks
,
1592 connection
.child_process_id
),
1596 // Spec says: If no version is specified and no database exists, set
1597 // database version to 1.
1601 if (local_version
> metadata_
.int_version
) {
1602 RunVersionChangeTransaction(connection
.callbacks
,
1603 CreateConnection(connection
.database_callbacks
,
1604 connection
.child_process_id
),
1605 connection
.transaction_id
,
1609 if (local_version
< metadata_
.int_version
) {
1610 connection
.callbacks
->OnError(IndexedDBDatabaseError(
1611 blink::WebIDBDatabaseExceptionVersionError
,
1612 ASCIIToUTF16("The requested version (") +
1613 Int64ToString16(local_version
) +
1614 ASCIIToUTF16(") is less than the existing version (") +
1615 Int64ToString16(metadata_
.int_version
) + ASCIIToUTF16(").")));
1618 DCHECK_EQ(local_version
, metadata_
.int_version
);
1619 connection
.callbacks
->OnSuccess(
1620 CreateConnection(connection
.database_callbacks
,
1621 connection
.child_process_id
),
1625 void IndexedDBDatabase::RunVersionChangeTransaction(
1626 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1627 scoped_ptr
<IndexedDBConnection
> connection
,
1628 int64 transaction_id
,
1629 int64 requested_version
) {
1630 DCHECK(callbacks
.get());
1631 DCHECK(connections_
.count(connection
.get()));
1632 if (ConnectionCount() > 1) {
1633 DCHECK_NE(blink::WebIDBDataLossTotal
, callbacks
->data_loss());
1634 // Front end ensures the event is not fired at connections that have
1635 // close_pending set.
1636 for (const auto* iter
: connections_
) {
1637 if (iter
!= connection
.get()) {
1638 iter
->callbacks()->OnVersionChange(metadata_
.int_version
,
1642 // OnBlocked will be fired at the request when one of the other
1643 // connections acks that the OnVersionChange was ignored.
1645 DCHECK(!pending_run_version_change_transaction_call_
);
1646 pending_run_version_change_transaction_call_
.reset(new PendingUpgradeCall(
1647 callbacks
, connection
.Pass(), transaction_id
, requested_version
));
1650 RunVersionChangeTransactionFinal(
1651 callbacks
, connection
.Pass(), transaction_id
, requested_version
);
1654 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1655 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
1656 scoped_ptr
<IndexedDBConnection
> connection
,
1657 int64 transaction_id
,
1658 int64 requested_version
) {
1660 std::vector
<int64
> object_store_ids
;
1661 CreateTransaction(transaction_id
,
1664 blink::WebIDBTransactionModeVersionChange
);
1666 transactions_
[transaction_id
]->ScheduleTask(
1667 base::Bind(&IndexedDBDatabase::VersionChangeOperation
,
1671 base::Passed(&connection
)));
1672 DCHECK(!pending_second_half_open_
);
1675 void IndexedDBDatabase::DeleteDatabase(
1676 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1678 if (IsDeleteDatabaseBlocked()) {
1679 for (const auto* connection
: connections_
) {
1680 // Front end ensures the event is not fired at connections that have
1681 // close_pending set.
1682 connection
->callbacks()->OnVersionChange(
1683 metadata_
.int_version
, IndexedDBDatabaseMetadata::NO_INT_VERSION
);
1685 // OnBlocked will be fired at the request when one of the other
1686 // connections acks that the OnVersionChange was ignored.
1688 pending_delete_calls_
.push_back(new PendingDeleteCall(callbacks
));
1691 DeleteDatabaseFinal(callbacks
);
1694 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1695 return !!ConnectionCount();
1698 void IndexedDBDatabase::DeleteDatabaseFinal(
1699 scoped_refptr
<IndexedDBCallbacks
> callbacks
) {
1700 DCHECK(!IsDeleteDatabaseBlocked());
1701 DCHECK(backing_store_
.get());
1702 leveldb::Status s
= backing_store_
->DeleteDatabase(metadata_
.name
);
1704 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
1705 "Internal error deleting database.");
1706 callbacks
->OnError(error
);
1707 if (s
.IsCorruption()) {
1708 GURL origin_url
= backing_store_
->origin_url();
1709 backing_store_
= NULL
;
1710 factory_
->HandleBackingStoreCorruption(origin_url
, error
);
1714 int64 old_version
= metadata_
.int_version
;
1715 metadata_
.version
= kNoStringVersion
;
1716 metadata_
.id
= kInvalidId
;
1717 metadata_
.int_version
= IndexedDBDatabaseMetadata::NO_INT_VERSION
;
1718 metadata_
.object_stores
.clear();
1719 callbacks
->OnSuccess(old_version
);
1720 factory_
->DatabaseDeleted(identifier_
);
1723 void IndexedDBDatabase::ForceClose() {
1724 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1725 scoped_refptr
<IndexedDBDatabase
> protect(this);
1726 ConnectionSet::const_iterator it
= connections_
.begin();
1727 while (it
!= connections_
.end()) {
1728 IndexedDBConnection
* connection
= *it
++;
1729 connection
->ForceClose();
1731 DCHECK(connections_
.empty());
1734 void IndexedDBDatabase::VersionChangeIgnored() {
1735 if (pending_run_version_change_transaction_call_
)
1736 pending_run_version_change_transaction_call_
->callbacks()->OnBlocked(
1737 metadata_
.int_version
);
1739 for (const auto& pending_delete_call
: pending_delete_calls_
)
1740 pending_delete_call
->callbacks()->OnBlocked(metadata_
.int_version
);
1744 void IndexedDBDatabase::Close(IndexedDBConnection
* connection
, bool forced
) {
1745 DCHECK(connections_
.count(connection
));
1746 DCHECK(connection
->IsConnected());
1747 DCHECK(connection
->database() == this);
1749 IDB_TRACE("IndexedDBDatabase::Close");
1751 connections_
.erase(connection
);
1753 // Abort outstanding transactions from the closing connection. This
1754 // can not happen if the close is requested by the connection itself
1755 // as the front-end defers the close until all transactions are
1756 // complete, but can occur on process termination or forced close.
1758 TransactionMap
transactions(transactions_
);
1759 for (const auto& it
: transactions
) {
1760 if (it
.second
->connection() == connection
->callbacks())
1762 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
,
1763 "Connection is closing."));
1767 if (pending_second_half_open_
&&
1768 pending_second_half_open_
->connection() == connection
) {
1769 pending_second_half_open_
->callbacks()->OnError(
1770 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError
,
1771 "The connection was closed."));
1772 pending_second_half_open_
.reset();
1775 ProcessPendingCalls();
1777 // TODO(jsbell): Add a test for the pending_open_calls_ cases below.
1778 if (!ConnectionCount() && !pending_open_calls_
.size() &&
1779 !pending_delete_calls_
.size()) {
1780 DCHECK(transactions_
.empty());
1782 const GURL origin_url
= backing_store_
->origin_url();
1783 backing_store_
= NULL
;
1785 factory_
->ReleaseDatabase(identifier_
, forced
);
1789 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1790 int64 object_store_id
,
1791 IndexedDBTransaction
* transaction
) {
1792 DCHECK(!transaction
);
1793 IDB_TRACE1("IndexedDBDatabase::CreateObjectStoreAbortOperation",
1796 RemoveObjectStore(object_store_id
);
1799 void IndexedDBDatabase::DeleteObjectStoreAbortOperation(
1800 const IndexedDBObjectStoreMetadata
& object_store_metadata
,
1801 IndexedDBTransaction
* transaction
) {
1802 DCHECK(!transaction
);
1803 IDB_TRACE1("IndexedDBDatabase::DeleteObjectStoreAbortOperation",
1806 AddObjectStore(object_store_metadata
,
1807 IndexedDBObjectStoreMetadata::kInvalidId
);
1810 void IndexedDBDatabase::VersionChangeAbortOperation(
1811 const base::string16
& previous_version
,
1812 int64 previous_int_version
,
1813 IndexedDBTransaction
* transaction
) {
1814 DCHECK(!transaction
);
1815 IDB_TRACE1("IndexedDBDatabase::VersionChangeAbortOperation",
1818 metadata_
.version
= previous_version
;
1819 metadata_
.int_version
= previous_int_version
;
1822 } // namespace content