indexed_db_database.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
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. 4 5#include "content/browser/indexed_db/indexed_db_database.h" 6 7#include <math.h> 8#include <set> 9 10#include "base/auto_reset.h" 11#include "base/logging.h" 12#include "base/memory/scoped_ptr.h" 13#include "base/stl_util.h" 14#include "base/strings/string_number_conversions.h" 15#include "base/strings/utf_string_conversions.h" 16#include "content/browser/indexed_db/indexed_db_connection.h" 17#include "content/browser/indexed_db/indexed_db_context_impl.h" 18#include "content/browser/indexed_db/indexed_db_cursor.h" 19#include "content/browser/indexed_db/indexed_db_factory.h" 20#include "content/browser/indexed_db/indexed_db_index_writer.h" 21#include "content/browser/indexed_db/indexed_db_pending_connection.h" 22#include "content/browser/indexed_db/indexed_db_tracing.h" 23#include "content/browser/indexed_db/indexed_db_transaction.h" 24#include "content/browser/indexed_db/indexed_db_value.h" 25#include "content/common/indexed_db/indexed_db_key_path.h" 26#include "content/common/indexed_db/indexed_db_key_range.h" 27#include "third_party/WebKit/public/platform/WebIDBDatabaseException.h" 28 29using base::ASCIIToUTF16; 30using base::Int64ToString16; 31using blink::WebIDBKeyTypeNumber; 32 33namespace content { 34 35// PendingUpgradeCall has a scoped_ptr<IndexedDBConnection> because it owns the 36// in-progress connection. 37class IndexedDBDatabase::PendingUpgradeCall { 38 public: 39 PendingUpgradeCall(scoped_refptr<IndexedDBCallbacks> callbacks, 40 scoped_ptr<IndexedDBConnection> connection, 41 int64 transaction_id, 42 int64 version) 43 : callbacks_(callbacks), 44 connection_(connection.Pass()), 45 version_(version), 46 transaction_id_(transaction_id) {} 47 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; } 48 // Takes ownership of the connection object. 49 scoped_ptr<IndexedDBConnection> ReleaseConnection() WARN_UNUSED_RESULT { 50 return connection_.Pass(); 51 } 52 int64 version() const { return version_; } 53 int64 transaction_id() const { return transaction_id_; } 54 55 private: 56 scoped_refptr<IndexedDBCallbacks> callbacks_; 57 scoped_ptr<IndexedDBConnection> connection_; 58 int64 version_; 59 const int64 transaction_id_; 60}; 61 62// PendingSuccessCall has a IndexedDBConnection* because the connection is now 63// owned elsewhere, but we need to cancel the success call if that connection 64// closes before it is sent. 65class IndexedDBDatabase::PendingSuccessCall { 66 public: 67 PendingSuccessCall(scoped_refptr<IndexedDBCallbacks> callbacks, 68 IndexedDBConnection* connection, 69 int64 version) 70 : callbacks_(callbacks), connection_(connection), version_(version) {} 71 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; } 72 IndexedDBConnection* connection() const { return connection_; } 73 int64 version() const { return version_; } 74 75 private: 76 scoped_refptr<IndexedDBCallbacks> callbacks_; 77 IndexedDBConnection* connection_; 78 int64 version_; 79}; 80 81class IndexedDBDatabase::PendingDeleteCall { 82 public: 83 explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks) 84 : callbacks_(callbacks) {} 85 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; } 86 87 private: 88 scoped_refptr<IndexedDBCallbacks> callbacks_; 89}; 90 91scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create( 92 const base::string16& name, 93 IndexedDBBackingStore* backing_store, 94 IndexedDBFactory* factory, 95 const Identifier& unique_identifier) { 96 scoped_refptr<IndexedDBDatabase> database = 97 new IndexedDBDatabase(name, backing_store, factory, unique_identifier); 98 if (!database->OpenInternal().ok()) 99 return 0; 100 return database; 101} 102 103namespace { 104const base::string16::value_type kNoStringVersion[] = {0}; 105} 106 107IndexedDBDatabase::IndexedDBDatabase(const base::string16& name, 108 IndexedDBBackingStore* backing_store, 109 IndexedDBFactory* factory, 110 const Identifier& unique_identifier) 111 : backing_store_(backing_store), 112 metadata_(name, 113 kInvalidId, 114 kNoStringVersion, 115 IndexedDBDatabaseMetadata::NO_INT_VERSION, 116 kInvalidId), 117 identifier_(unique_identifier), 118 factory_(factory) { 119} 120 121void IndexedDBDatabase::AddObjectStore( 122 const IndexedDBObjectStoreMetadata& object_store, 123 int64 new_max_object_store_id) { 124 DCHECK(metadata_.object_stores.find(object_store.id) == 125 metadata_.object_stores.end()); 126 if (new_max_object_store_id != IndexedDBObjectStoreMetadata::kInvalidId) { 127 DCHECK_LT(metadata_.max_object_store_id, new_max_object_store_id); 128 metadata_.max_object_store_id = new_max_object_store_id; 129 } 130 metadata_.object_stores[object_store.id] = object_store; 131} 132 133void IndexedDBDatabase::RemoveObjectStore(int64 object_store_id) { 134 DCHECK(metadata_.object_stores.find(object_store_id) != 135 metadata_.object_stores.end()); 136 metadata_.object_stores.erase(object_store_id); 137} 138 139void IndexedDBDatabase::AddIndex(int64 object_store_id, 140 const IndexedDBIndexMetadata& index, 141 int64 new_max_index_id) { 142 DCHECK(metadata_.object_stores.find(object_store_id) != 143 metadata_.object_stores.end()); 144 IndexedDBObjectStoreMetadata object_store = 145 metadata_.object_stores[object_store_id]; 146 147 DCHECK(object_store.indexes.find(index.id) == object_store.indexes.end()); 148 object_store.indexes[index.id] = index; 149 if (new_max_index_id != IndexedDBIndexMetadata::kInvalidId) { 150 DCHECK_LT(object_store.max_index_id, new_max_index_id); 151 object_store.max_index_id = new_max_index_id; 152 } 153 metadata_.object_stores[object_store_id] = object_store; 154} 155 156void IndexedDBDatabase::RemoveIndex(int64 object_store_id, int64 index_id) { 157 DCHECK(metadata_.object_stores.find(object_store_id) != 158 metadata_.object_stores.end()); 159 IndexedDBObjectStoreMetadata object_store = 160 metadata_.object_stores[object_store_id]; 161 162 DCHECK(object_store.indexes.find(index_id) != object_store.indexes.end()); 163 object_store.indexes.erase(index_id); 164 metadata_.object_stores[object_store_id] = object_store; 165} 166 167leveldb::Status IndexedDBDatabase::OpenInternal() { 168 bool success = false; 169 leveldb::Status s = backing_store_->GetIDBDatabaseMetaData( 170 metadata_.name, &metadata_, &success); 171 DCHECK(success == (metadata_.id != kInvalidId)) << "success = " << success 172 << " id = " << metadata_.id; 173 if (!s.ok()) 174 return s; 175 if (success) 176 return backing_store_->GetObjectStores(metadata_.id, 177 &metadata_.object_stores); 178 179 return backing_store_->CreateIDBDatabaseMetaData( 180 metadata_.name, metadata_.version, metadata_.int_version, &metadata_.id); 181} 182 183IndexedDBDatabase::~IndexedDBDatabase() { 184 DCHECK(transactions_.empty()); 185 DCHECK(pending_open_calls_.empty()); 186 DCHECK(pending_delete_calls_.empty()); 187} 188 189scoped_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection( 190 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks, 191 int child_process_id) { 192 scoped_ptr<IndexedDBConnection> connection( 193 new IndexedDBConnection(this, database_callbacks)); 194 connections_.insert(connection.get()); 195 /* TODO(ericu): Grant child process permissions here so that the connection 196 * can create Blobs. 197 */ 198 return connection.Pass(); 199} 200 201IndexedDBTransaction* IndexedDBDatabase::GetTransaction( 202 int64 transaction_id) const { 203 TransactionMap::const_iterator trans_iterator = 204 transactions_.find(transaction_id); 205 if (trans_iterator == transactions_.end()) 206 return NULL; 207 return trans_iterator->second; 208} 209 210bool IndexedDBDatabase::ValidateObjectStoreId(int64 object_store_id) const { 211 if (!ContainsKey(metadata_.object_stores, object_store_id)) { 212 DLOG(ERROR) << "Invalid object_store_id"; 213 return false; 214 } 215 return true; 216} 217 218bool IndexedDBDatabase::ValidateObjectStoreIdAndIndexId(int64 object_store_id, 219 int64 index_id) const { 220 if (!ValidateObjectStoreId(object_store_id)) 221 return false; 222 const IndexedDBObjectStoreMetadata& object_store_metadata = 223 metadata_.object_stores.find(object_store_id)->second; 224 if (!ContainsKey(object_store_metadata.indexes, index_id)) { 225 DLOG(ERROR) << "Invalid index_id"; 226 return false; 227 } 228 return true; 229} 230 231bool IndexedDBDatabase::ValidateObjectStoreIdAndOptionalIndexId( 232 int64 object_store_id, 233 int64 index_id) const { 234 if (!ValidateObjectStoreId(object_store_id)) 235 return false; 236 const IndexedDBObjectStoreMetadata& object_store_metadata = 237 metadata_.object_stores.find(object_store_id)->second; 238 if (index_id != IndexedDBIndexMetadata::kInvalidId && 239 !ContainsKey(object_store_metadata.indexes, index_id)) { 240 DLOG(ERROR) << "Invalid index_id"; 241 return false; 242 } 243 return true; 244} 245 246bool IndexedDBDatabase::ValidateObjectStoreIdAndNewIndexId( 247 int64 object_store_id, 248 int64 index_id) const { 249 if (!ValidateObjectStoreId(object_store_id)) 250 return false; 251 const IndexedDBObjectStoreMetadata& object_store_metadata = 252 metadata_.object_stores.find(object_store_id)->second; 253 if (ContainsKey(object_store_metadata.indexes, index_id)) { 254 DLOG(ERROR) << "Invalid index_id"; 255 return false; 256 } 257 return true; 258} 259 260void IndexedDBDatabase::CreateObjectStore(int64 transaction_id, 261 int64 object_store_id, 262 const base::string16& name, 263 const IndexedDBKeyPath& key_path, 264 bool auto_increment) { 265 IDB_TRACE("IndexedDBDatabase::CreateObjectStore"); 266 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 267 if (!transaction) 268 return; 269 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); 270 271 if (ContainsKey(metadata_.object_stores, object_store_id)) { 272 DLOG(ERROR) << "Invalid object_store_id"; 273 return; 274 } 275 276 IndexedDBObjectStoreMetadata object_store_metadata( 277 name, 278 object_store_id, 279 key_path, 280 auto_increment, 281 IndexedDBDatabase::kMinimumIndexId); 282 283 transaction->ScheduleTask( 284 base::Bind(&IndexedDBDatabase::CreateObjectStoreOperation, 285 this, 286 object_store_metadata), 287 base::Bind(&IndexedDBDatabase::CreateObjectStoreAbortOperation, 288 this, 289 object_store_id)); 290 291 AddObjectStore(object_store_metadata, object_store_id); 292} 293 294void IndexedDBDatabase::CreateObjectStoreOperation( 295 const IndexedDBObjectStoreMetadata& object_store_metadata, 296 IndexedDBTransaction* transaction) { 297 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreOperation"); 298 leveldb::Status s = 299 backing_store_->CreateObjectStore(transaction->BackingStoreTransaction(), 300 transaction->database()->id(), 301 object_store_metadata.id, 302 object_store_metadata.name, 303 object_store_metadata.key_path, 304 object_store_metadata.auto_increment); 305 if (!s.ok()) { 306 IndexedDBDatabaseError error( 307 blink::WebIDBDatabaseExceptionUnknownError, 308 ASCIIToUTF16("Internal error creating object store '") + 309 object_store_metadata.name + ASCIIToUTF16("'.")); 310 transaction->Abort(error); 311 if (s.IsCorruption()) 312 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 313 error); 314 return; 315 } 316} 317 318void IndexedDBDatabase::DeleteObjectStore(int64 transaction_id, 319 int64 object_store_id) { 320 IDB_TRACE("IndexedDBDatabase::DeleteObjectStore"); 321 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 322 if (!transaction) 323 return; 324 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); 325 326 if (!ValidateObjectStoreId(object_store_id)) 327 return; 328 329 const IndexedDBObjectStoreMetadata& object_store_metadata = 330 metadata_.object_stores[object_store_id]; 331 332 transaction->ScheduleTask( 333 base::Bind(&IndexedDBDatabase::DeleteObjectStoreOperation, 334 this, 335 object_store_metadata), 336 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation, 337 this, 338 object_store_metadata)); 339 RemoveObjectStore(object_store_id); 340} 341 342void IndexedDBDatabase::CreateIndex(int64 transaction_id, 343 int64 object_store_id, 344 int64 index_id, 345 const base::string16& name, 346 const IndexedDBKeyPath& key_path, 347 bool unique, 348 bool multi_entry) { 349 IDB_TRACE("IndexedDBDatabase::CreateIndex"); 350 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 351 if (!transaction) 352 return; 353 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); 354 355 if (!ValidateObjectStoreIdAndNewIndexId(object_store_id, index_id)) 356 return; 357 const IndexedDBIndexMetadata index_metadata( 358 name, index_id, key_path, unique, multi_entry); 359 360 transaction->ScheduleTask( 361 base::Bind(&IndexedDBDatabase::CreateIndexOperation, 362 this, 363 object_store_id, 364 index_metadata), 365 base::Bind(&IndexedDBDatabase::CreateIndexAbortOperation, 366 this, 367 object_store_id, 368 index_id)); 369 370 AddIndex(object_store_id, index_metadata, index_id); 371} 372 373void IndexedDBDatabase::CreateIndexOperation( 374 int64 object_store_id, 375 const IndexedDBIndexMetadata& index_metadata, 376 IndexedDBTransaction* transaction) { 377 IDB_TRACE("IndexedDBDatabase::CreateIndexOperation"); 378 if (!backing_store_->CreateIndex(transaction->BackingStoreTransaction(), 379 transaction->database()->id(), 380 object_store_id, 381 index_metadata.id, 382 index_metadata.name, 383 index_metadata.key_path, 384 index_metadata.unique, 385 index_metadata.multi_entry).ok()) { 386 base::string16 error_string = 387 ASCIIToUTF16("Internal error creating index '") + 388 index_metadata.name + ASCIIToUTF16("'."); 389 transaction->Abort(IndexedDBDatabaseError( 390 blink::WebIDBDatabaseExceptionUnknownError, error_string)); 391 return; 392 } 393} 394 395void IndexedDBDatabase::CreateIndexAbortOperation( 396 int64 object_store_id, 397 int64 index_id, 398 IndexedDBTransaction* transaction) { 399 IDB_TRACE("IndexedDBDatabase::CreateIndexAbortOperation"); 400 DCHECK(!transaction); 401 RemoveIndex(object_store_id, index_id); 402} 403 404void IndexedDBDatabase::DeleteIndex(int64 transaction_id, 405 int64 object_store_id, 406 int64 index_id) { 407 IDB_TRACE("IndexedDBDatabase::DeleteIndex"); 408 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 409 if (!transaction) 410 return; 411 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); 412 413 if (!ValidateObjectStoreIdAndIndexId(object_store_id, index_id)) 414 return; 415 const IndexedDBIndexMetadata& index_metadata = 416 metadata_.object_stores[object_store_id].indexes[index_id]; 417 418 transaction->ScheduleTask( 419 base::Bind(&IndexedDBDatabase::DeleteIndexOperation, 420 this, 421 object_store_id, 422 index_metadata), 423 base::Bind(&IndexedDBDatabase::DeleteIndexAbortOperation, 424 this, 425 object_store_id, 426 index_metadata)); 427 428 RemoveIndex(object_store_id, index_id); 429} 430 431void IndexedDBDatabase::DeleteIndexOperation( 432 int64 object_store_id, 433 const IndexedDBIndexMetadata& index_metadata, 434 IndexedDBTransaction* transaction) { 435 IDB_TRACE("IndexedDBDatabase::DeleteIndexOperation"); 436 leveldb::Status s = 437 backing_store_->DeleteIndex(transaction->BackingStoreTransaction(), 438 transaction->database()->id(), 439 object_store_id, 440 index_metadata.id); 441 if (!s.ok()) { 442 base::string16 error_string = 443 ASCIIToUTF16("Internal error deleting index '") + 444 index_metadata.name + ASCIIToUTF16("'."); 445 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 446 error_string); 447 transaction->Abort(error); 448 if (s.IsCorruption()) 449 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 450 error); 451 } 452} 453 454void IndexedDBDatabase::DeleteIndexAbortOperation( 455 int64 object_store_id, 456 const IndexedDBIndexMetadata& index_metadata, 457 IndexedDBTransaction* transaction) { 458 IDB_TRACE("IndexedDBDatabase::DeleteIndexAbortOperation"); 459 DCHECK(!transaction); 460 AddIndex(object_store_id, index_metadata, IndexedDBIndexMetadata::kInvalidId); 461} 462 463void IndexedDBDatabase::Commit(int64 transaction_id) { 464 // The frontend suggests that we commit, but we may have previously initiated 465 // an abort, and so have disposed of the transaction. on_abort has already 466 // been dispatched to the frontend, so it will find out about that 467 // asynchronously. 468 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 469 if (transaction) 470 transaction->Commit(); 471} 472 473void IndexedDBDatabase::Abort(int64 transaction_id) { 474 // If the transaction is unknown, then it has already been aborted by the 475 // backend before this call so it is safe to ignore it. 476 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 477 if (transaction) 478 transaction->Abort(); 479} 480 481void IndexedDBDatabase::Abort(int64 transaction_id, 482 const IndexedDBDatabaseError& error) { 483 // If the transaction is unknown, then it has already been aborted by the 484 // backend before this call so it is safe to ignore it. 485 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 486 if (transaction) 487 transaction->Abort(error); 488} 489 490void IndexedDBDatabase::Get(int64 transaction_id, 491 int64 object_store_id, 492 int64 index_id, 493 scoped_ptr<IndexedDBKeyRange> key_range, 494 bool key_only, 495 scoped_refptr<IndexedDBCallbacks> callbacks) { 496 IDB_TRACE("IndexedDBDatabase::Get"); 497 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 498 if (!transaction) 499 return; 500 501 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id)) 502 return; 503 504 transaction->ScheduleTask(base::Bind( 505 &IndexedDBDatabase::GetOperation, 506 this, 507 object_store_id, 508 index_id, 509 Passed(&key_range), 510 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE, 511 callbacks)); 512} 513 514void IndexedDBDatabase::GetOperation( 515 int64 object_store_id, 516 int64 index_id, 517 scoped_ptr<IndexedDBKeyRange> key_range, 518 indexed_db::CursorType cursor_type, 519 scoped_refptr<IndexedDBCallbacks> callbacks, 520 IndexedDBTransaction* transaction) { 521 IDB_TRACE("IndexedDBDatabase::GetOperation"); 522 523 DCHECK(metadata_.object_stores.find(object_store_id) != 524 metadata_.object_stores.end()); 525 const IndexedDBObjectStoreMetadata& object_store_metadata = 526 metadata_.object_stores[object_store_id]; 527 528 const IndexedDBKey* key; 529 530 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; 531 if (key_range->IsOnlyKey()) { 532 key = &key_range->lower(); 533 } else { 534 if (index_id == IndexedDBIndexMetadata::kInvalidId) { 535 DCHECK_NE(cursor_type, indexed_db::CURSOR_KEY_ONLY); 536 // ObjectStore Retrieval Operation 537 backing_store_cursor = backing_store_->OpenObjectStoreCursor( 538 transaction->BackingStoreTransaction(), 539 id(), 540 object_store_id, 541 *key_range, 542 indexed_db::CURSOR_NEXT); 543 } else if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { 544 // Index Value Retrieval Operation 545 backing_store_cursor = backing_store_->OpenIndexKeyCursor( 546 transaction->BackingStoreTransaction(), 547 id(), 548 object_store_id, 549 index_id, 550 *key_range, 551 indexed_db::CURSOR_NEXT); 552 } else { 553 // Index Referenced Value Retrieval Operation 554 backing_store_cursor = backing_store_->OpenIndexCursor( 555 transaction->BackingStoreTransaction(), 556 id(), 557 object_store_id, 558 index_id, 559 *key_range, 560 indexed_db::CURSOR_NEXT); 561 } 562 563 if (!backing_store_cursor) { 564 callbacks->OnSuccess(); 565 return; 566 } 567 568 key = &backing_store_cursor->key(); 569 } 570 571 scoped_ptr<IndexedDBKey> primary_key; 572 leveldb::Status s; 573 if (index_id == IndexedDBIndexMetadata::kInvalidId) { 574 // Object Store Retrieval Operation 575 IndexedDBValue value; 576 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(), 577 id(), 578 object_store_id, 579 *key, 580 &value); 581 if (!s.ok()) { 582 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 583 "Internal error in GetRecord."); 584 callbacks->OnError(error); 585 586 if (s.IsCorruption()) 587 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 588 error); 589 return; 590 } 591 592 if (value.empty()) { 593 callbacks->OnSuccess(); 594 return; 595 } 596 597 if (object_store_metadata.auto_increment && 598 !object_store_metadata.key_path.IsNull()) { 599 callbacks->OnSuccess(&value, *key, object_store_metadata.key_path); 600 return; 601 } 602 603 callbacks->OnSuccess(&value); 604 return; 605 } 606 607 // From here we are dealing only with indexes. 608 s = backing_store_->GetPrimaryKeyViaIndex( 609 transaction->BackingStoreTransaction(), 610 id(), 611 object_store_id, 612 index_id, 613 *key, 614 &primary_key); 615 if (!s.ok()) { 616 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 617 "Internal error in GetPrimaryKeyViaIndex."); 618 callbacks->OnError(error); 619 if (s.IsCorruption()) 620 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 621 error); 622 return; 623 } 624 if (!primary_key) { 625 callbacks->OnSuccess(); 626 return; 627 } 628 if (cursor_type == indexed_db::CURSOR_KEY_ONLY) { 629 // Index Value Retrieval Operation 630 callbacks->OnSuccess(*primary_key); 631 return; 632 } 633 634 // Index Referenced Value Retrieval Operation 635 IndexedDBValue value; 636 s = backing_store_->GetRecord(transaction->BackingStoreTransaction(), 637 id(), 638 object_store_id, 639 *primary_key, 640 &value); 641 if (!s.ok()) { 642 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 643 "Internal error in GetRecord."); 644 callbacks->OnError(error); 645 if (s.IsCorruption()) 646 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 647 error); 648 return; 649 } 650 651 if (value.empty()) { 652 callbacks->OnSuccess(); 653 return; 654 } 655 if (object_store_metadata.auto_increment && 656 !object_store_metadata.key_path.IsNull()) { 657 callbacks->OnSuccess(&value, *primary_key, object_store_metadata.key_path); 658 return; 659 } 660 callbacks->OnSuccess(&value); 661} 662 663static scoped_ptr<IndexedDBKey> GenerateKey( 664 IndexedDBBackingStore* backing_store, 665 IndexedDBTransaction* transaction, 666 int64 database_id, 667 int64 object_store_id) { 668 const int64 max_generator_value = 669 9007199254740992LL; // Maximum integer storable as ECMAScript number. 670 int64 current_number; 671 leveldb::Status s = backing_store->GetKeyGeneratorCurrentNumber( 672 transaction->BackingStoreTransaction(), 673 database_id, 674 object_store_id, 675 ¤t_number); 676 if (!s.ok()) { 677 LOG(ERROR) << "Failed to GetKeyGeneratorCurrentNumber"; 678 return make_scoped_ptr(new IndexedDBKey()); 679 } 680 if (current_number < 0 || current_number > max_generator_value) 681 return make_scoped_ptr(new IndexedDBKey()); 682 683 return make_scoped_ptr(new IndexedDBKey(current_number, WebIDBKeyTypeNumber)); 684} 685 686static leveldb::Status UpdateKeyGenerator(IndexedDBBackingStore* backing_store, 687 IndexedDBTransaction* transaction, 688 int64 database_id, 689 int64 object_store_id, 690 const IndexedDBKey& key, 691 bool check_current) { 692 DCHECK_EQ(WebIDBKeyTypeNumber, key.type()); 693 return backing_store->MaybeUpdateKeyGeneratorCurrentNumber( 694 transaction->BackingStoreTransaction(), 695 database_id, 696 object_store_id, 697 static_cast<int64>(floor(key.number())) + 1, 698 check_current); 699} 700 701struct IndexedDBDatabase::PutOperationParams { 702 PutOperationParams() {} 703 int64 object_store_id; 704 IndexedDBValue value; 705 scoped_ptr<IndexedDBKey> key; 706 IndexedDBDatabase::PutMode put_mode; 707 scoped_refptr<IndexedDBCallbacks> callbacks; 708 std::vector<IndexKeys> index_keys; 709 710 private: 711 DISALLOW_COPY_AND_ASSIGN(PutOperationParams); 712}; 713 714void IndexedDBDatabase::Put(int64 transaction_id, 715 int64 object_store_id, 716 IndexedDBValue* value, 717 scoped_ptr<IndexedDBKey> key, 718 PutMode put_mode, 719 scoped_refptr<IndexedDBCallbacks> callbacks, 720 const std::vector<IndexKeys>& index_keys) { 721 IDB_TRACE("IndexedDBDatabase::Put"); 722 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 723 if (!transaction) 724 return; 725 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY); 726 727 if (!ValidateObjectStoreId(object_store_id)) 728 return; 729 730 DCHECK(key); 731 scoped_ptr<PutOperationParams> params(new PutOperationParams()); 732 params->object_store_id = object_store_id; 733 params->value.swap(*value); 734 params->key = key.Pass(); 735 params->put_mode = put_mode; 736 params->callbacks = callbacks; 737 params->index_keys = index_keys; 738 transaction->ScheduleTask(base::Bind( 739 &IndexedDBDatabase::PutOperation, this, base::Passed(¶ms))); 740} 741 742void IndexedDBDatabase::PutOperation(scoped_ptr<PutOperationParams> params, 743 IndexedDBTransaction* transaction) { 744 IDB_TRACE("IndexedDBDatabase::PutOperation"); 745 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY); 746 bool key_was_generated = false; 747 748 DCHECK(metadata_.object_stores.find(params->object_store_id) != 749 metadata_.object_stores.end()); 750 const IndexedDBObjectStoreMetadata& object_store = 751 metadata_.object_stores[params->object_store_id]; 752 DCHECK(object_store.auto_increment || params->key->IsValid()); 753 754 scoped_ptr<IndexedDBKey> key; 755 if (params->put_mode != IndexedDBDatabase::CURSOR_UPDATE && 756 object_store.auto_increment && !params->key->IsValid()) { 757 scoped_ptr<IndexedDBKey> auto_inc_key = GenerateKey( 758 backing_store_.get(), transaction, id(), params->object_store_id); 759 key_was_generated = true; 760 if (!auto_inc_key->IsValid()) { 761 params->callbacks->OnError( 762 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError, 763 "Maximum key generator value reached.")); 764 return; 765 } 766 key = auto_inc_key.Pass(); 767 } else { 768 key = params->key.Pass(); 769 } 770 771 DCHECK(key->IsValid()); 772 773 IndexedDBBackingStore::RecordIdentifier record_identifier; 774 if (params->put_mode == IndexedDBDatabase::ADD_ONLY) { 775 bool found = false; 776 leveldb::Status s = backing_store_->KeyExistsInObjectStore( 777 transaction->BackingStoreTransaction(), 778 id(), 779 params->object_store_id, 780 *key, 781 &record_identifier, 782 &found); 783 if (!s.ok()) { 784 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 785 "Internal error checking key existence."); 786 params->callbacks->OnError(error); 787 if (s.IsCorruption()) 788 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 789 error); 790 return; 791 } 792 if (found) { 793 params->callbacks->OnError( 794 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionConstraintError, 795 "Key already exists in the object store.")); 796 return; 797 } 798 } 799 800 ScopedVector<IndexWriter> index_writers; 801 base::string16 error_message; 802 bool obeys_constraints = false; 803 bool backing_store_success = MakeIndexWriters(transaction, 804 backing_store_.get(), 805 id(), 806 object_store, 807 *key, 808 key_was_generated, 809 params->index_keys, 810 &index_writers, 811 &error_message, 812 &obeys_constraints); 813 if (!backing_store_success) { 814 params->callbacks->OnError(IndexedDBDatabaseError( 815 blink::WebIDBDatabaseExceptionUnknownError, 816 "Internal error: backing store error updating index keys.")); 817 return; 818 } 819 if (!obeys_constraints) { 820 params->callbacks->OnError(IndexedDBDatabaseError( 821 blink::WebIDBDatabaseExceptionConstraintError, error_message)); 822 return; 823 } 824 825 // Before this point, don't do any mutation. After this point, rollback the 826 // transaction in case of error. 827 leveldb::Status s = 828 backing_store_->PutRecord(transaction->BackingStoreTransaction(), 829 id(), 830 params->object_store_id, 831 *key, 832 params->value, 833 &record_identifier); 834 if (!s.ok()) { 835 IndexedDBDatabaseError error( 836 blink::WebIDBDatabaseExceptionUnknownError, 837 "Internal error: backing store error performing put/add."); 838 params->callbacks->OnError(error); 839 if (s.IsCorruption()) 840 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 841 error); 842 return; 843 } 844 845 for (size_t i = 0; i < index_writers.size(); ++i) { 846 IndexWriter* index_writer = index_writers[i]; 847 index_writer->WriteIndexKeys(record_identifier, 848 backing_store_.get(), 849 transaction->BackingStoreTransaction(), 850 id(), 851 params->object_store_id); 852 } 853 854 if (object_store.auto_increment && 855 params->put_mode != IndexedDBDatabase::CURSOR_UPDATE && 856 key->type() == WebIDBKeyTypeNumber) { 857 leveldb::Status s = UpdateKeyGenerator(backing_store_.get(), 858 transaction, 859 id(), 860 params->object_store_id, 861 *key, 862 !key_was_generated); 863 if (!s.ok()) { 864 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 865 "Internal error updating key generator."); 866 params->callbacks->OnError(error); 867 if (s.IsCorruption()) 868 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 869 error); 870 return; 871 } 872 } 873 874 params->callbacks->OnSuccess(*key); 875} 876 877void IndexedDBDatabase::SetIndexKeys(int64 transaction_id, 878 int64 object_store_id, 879 scoped_ptr<IndexedDBKey> primary_key, 880 const std::vector<IndexKeys>& index_keys) { 881 IDB_TRACE("IndexedDBDatabase::SetIndexKeys"); 882 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 883 if (!transaction) 884 return; 885 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); 886 887 // TODO(alecflett): This method could be asynchronous, but we need to 888 // evaluate if it's worth the extra complexity. 889 IndexedDBBackingStore::RecordIdentifier record_identifier; 890 bool found = false; 891 leveldb::Status s = backing_store_->KeyExistsInObjectStore( 892 transaction->BackingStoreTransaction(), 893 metadata_.id, 894 object_store_id, 895 *primary_key, 896 &record_identifier, 897 &found); 898 if (!s.ok()) { 899 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 900 "Internal error setting index keys."); 901 transaction->Abort(error); 902 if (s.IsCorruption()) 903 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 904 error); 905 return; 906 } 907 if (!found) { 908 transaction->Abort(IndexedDBDatabaseError( 909 blink::WebIDBDatabaseExceptionUnknownError, 910 "Internal error setting index keys for object store.")); 911 return; 912 } 913 914 ScopedVector<IndexWriter> index_writers; 915 base::string16 error_message; 916 bool obeys_constraints = false; 917 DCHECK(metadata_.object_stores.find(object_store_id) != 918 metadata_.object_stores.end()); 919 const IndexedDBObjectStoreMetadata& object_store_metadata = 920 metadata_.object_stores[object_store_id]; 921 bool backing_store_success = MakeIndexWriters(transaction, 922 backing_store_, 923 id(), 924 object_store_metadata, 925 *primary_key, 926 false, 927 index_keys, 928 &index_writers, 929 &error_message, 930 &obeys_constraints); 931 if (!backing_store_success) { 932 transaction->Abort(IndexedDBDatabaseError( 933 blink::WebIDBDatabaseExceptionUnknownError, 934 "Internal error: backing store error updating index keys.")); 935 return; 936 } 937 if (!obeys_constraints) { 938 transaction->Abort(IndexedDBDatabaseError( 939 blink::WebIDBDatabaseExceptionConstraintError, error_message)); 940 return; 941 } 942 943 for (size_t i = 0; i < index_writers.size(); ++i) { 944 IndexWriter* index_writer = index_writers[i]; 945 index_writer->WriteIndexKeys(record_identifier, 946 backing_store_, 947 transaction->BackingStoreTransaction(), 948 id(), 949 object_store_id); 950 } 951} 952 953void IndexedDBDatabase::SetIndexesReady(int64 transaction_id, 954 int64, 955 const std::vector<int64>& index_ids) { 956 IDB_TRACE("IndexedDBDatabase::SetIndexesReady"); 957 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 958 if (!transaction) 959 return; 960 DCHECK_EQ(transaction->mode(), indexed_db::TRANSACTION_VERSION_CHANGE); 961 962 transaction->ScheduleTask( 963 IndexedDBDatabase::PREEMPTIVE_TASK, 964 base::Bind(&IndexedDBDatabase::SetIndexesReadyOperation, 965 this, 966 index_ids.size())); 967} 968 969void IndexedDBDatabase::SetIndexesReadyOperation( 970 size_t index_count, 971 IndexedDBTransaction* transaction) { 972 IDB_TRACE("IndexedDBDatabase::SetIndexesReadyOperation"); 973 for (size_t i = 0; i < index_count; ++i) 974 transaction->DidCompletePreemptiveEvent(); 975} 976 977struct IndexedDBDatabase::OpenCursorOperationParams { 978 OpenCursorOperationParams() {} 979 int64 object_store_id; 980 int64 index_id; 981 scoped_ptr<IndexedDBKeyRange> key_range; 982 indexed_db::CursorDirection direction; 983 indexed_db::CursorType cursor_type; 984 IndexedDBDatabase::TaskType task_type; 985 scoped_refptr<IndexedDBCallbacks> callbacks; 986 987 private: 988 DISALLOW_COPY_AND_ASSIGN(OpenCursorOperationParams); 989}; 990 991void IndexedDBDatabase::OpenCursor( 992 int64 transaction_id, 993 int64 object_store_id, 994 int64 index_id, 995 scoped_ptr<IndexedDBKeyRange> key_range, 996 indexed_db::CursorDirection direction, 997 bool key_only, 998 TaskType task_type, 999 scoped_refptr<IndexedDBCallbacks> callbacks) { 1000 IDB_TRACE("IndexedDBDatabase::OpenCursor"); 1001 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 1002 if (!transaction) 1003 return; 1004 1005 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id)) 1006 return; 1007 1008 scoped_ptr<OpenCursorOperationParams> params(new OpenCursorOperationParams()); 1009 params->object_store_id = object_store_id; 1010 params->index_id = index_id; 1011 params->key_range = key_range.Pass(); 1012 params->direction = direction; 1013 params->cursor_type = 1014 key_only ? indexed_db::CURSOR_KEY_ONLY : indexed_db::CURSOR_KEY_AND_VALUE; 1015 params->task_type = task_type; 1016 params->callbacks = callbacks; 1017 transaction->ScheduleTask(base::Bind( 1018 &IndexedDBDatabase::OpenCursorOperation, this, base::Passed(¶ms))); 1019} 1020 1021void IndexedDBDatabase::OpenCursorOperation( 1022 scoped_ptr<OpenCursorOperationParams> params, 1023 IndexedDBTransaction* transaction) { 1024 IDB_TRACE("IndexedDBDatabase::OpenCursorOperation"); 1025 1026 // The frontend has begun indexing, so this pauses the transaction 1027 // until the indexing is complete. This can't happen any earlier 1028 // because we don't want to switch to early mode in case multiple 1029 // indexes are being created in a row, with Put()'s in between. 1030 if (params->task_type == IndexedDBDatabase::PREEMPTIVE_TASK) 1031 transaction->AddPreemptiveEvent(); 1032 1033 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; 1034 if (params->index_id == IndexedDBIndexMetadata::kInvalidId) { 1035 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) { 1036 DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK); 1037 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor( 1038 transaction->BackingStoreTransaction(), 1039 id(), 1040 params->object_store_id, 1041 *params->key_range, 1042 params->direction); 1043 } else { 1044 backing_store_cursor = backing_store_->OpenObjectStoreCursor( 1045 transaction->BackingStoreTransaction(), 1046 id(), 1047 params->object_store_id, 1048 *params->key_range, 1049 params->direction); 1050 } 1051 } else { 1052 DCHECK_EQ(params->task_type, IndexedDBDatabase::NORMAL_TASK); 1053 if (params->cursor_type == indexed_db::CURSOR_KEY_ONLY) { 1054 backing_store_cursor = backing_store_->OpenIndexKeyCursor( 1055 transaction->BackingStoreTransaction(), 1056 id(), 1057 params->object_store_id, 1058 params->index_id, 1059 *params->key_range, 1060 params->direction); 1061 } else { 1062 backing_store_cursor = backing_store_->OpenIndexCursor( 1063 transaction->BackingStoreTransaction(), 1064 id(), 1065 params->object_store_id, 1066 params->index_id, 1067 *params->key_range, 1068 params->direction); 1069 } 1070 } 1071 1072 if (!backing_store_cursor) { 1073 params->callbacks->OnSuccess(static_cast<IndexedDBValue*>(NULL)); 1074 return; 1075 } 1076 1077 scoped_refptr<IndexedDBCursor> cursor = 1078 new IndexedDBCursor(backing_store_cursor.Pass(), 1079 params->cursor_type, 1080 params->task_type, 1081 transaction); 1082 params->callbacks->OnSuccess( 1083 cursor, cursor->key(), cursor->primary_key(), cursor->Value()); 1084} 1085 1086void IndexedDBDatabase::Count(int64 transaction_id, 1087 int64 object_store_id, 1088 int64 index_id, 1089 scoped_ptr<IndexedDBKeyRange> key_range, 1090 scoped_refptr<IndexedDBCallbacks> callbacks) { 1091 IDB_TRACE("IndexedDBDatabase::Count"); 1092 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 1093 if (!transaction) 1094 return; 1095 1096 if (!ValidateObjectStoreIdAndOptionalIndexId(object_store_id, index_id)) 1097 return; 1098 1099 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::CountOperation, 1100 this, 1101 object_store_id, 1102 index_id, 1103 base::Passed(&key_range), 1104 callbacks)); 1105} 1106 1107void IndexedDBDatabase::CountOperation( 1108 int64 object_store_id, 1109 int64 index_id, 1110 scoped_ptr<IndexedDBKeyRange> key_range, 1111 scoped_refptr<IndexedDBCallbacks> callbacks, 1112 IndexedDBTransaction* transaction) { 1113 IDB_TRACE("IndexedDBDatabase::CountOperation"); 1114 uint32 count = 0; 1115 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor; 1116 1117 if (index_id == IndexedDBIndexMetadata::kInvalidId) { 1118 backing_store_cursor = backing_store_->OpenObjectStoreKeyCursor( 1119 transaction->BackingStoreTransaction(), 1120 id(), 1121 object_store_id, 1122 *key_range, 1123 indexed_db::CURSOR_NEXT); 1124 } else { 1125 backing_store_cursor = backing_store_->OpenIndexKeyCursor( 1126 transaction->BackingStoreTransaction(), 1127 id(), 1128 object_store_id, 1129 index_id, 1130 *key_range, 1131 indexed_db::CURSOR_NEXT); 1132 } 1133 if (!backing_store_cursor) { 1134 callbacks->OnSuccess(count); 1135 return; 1136 } 1137 1138 do { 1139 ++count; 1140 } while (backing_store_cursor->Continue()); 1141 1142 callbacks->OnSuccess(count); 1143} 1144 1145void IndexedDBDatabase::DeleteRange( 1146 int64 transaction_id, 1147 int64 object_store_id, 1148 scoped_ptr<IndexedDBKeyRange> key_range, 1149 scoped_refptr<IndexedDBCallbacks> callbacks) { 1150 IDB_TRACE("IndexedDBDatabase::DeleteRange"); 1151 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 1152 if (!transaction) 1153 return; 1154 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY); 1155 1156 if (!ValidateObjectStoreId(object_store_id)) 1157 return; 1158 1159 transaction->ScheduleTask(base::Bind(&IndexedDBDatabase::DeleteRangeOperation, 1160 this, 1161 object_store_id, 1162 base::Passed(&key_range), 1163 callbacks)); 1164} 1165 1166void IndexedDBDatabase::DeleteRangeOperation( 1167 int64 object_store_id, 1168 scoped_ptr<IndexedDBKeyRange> key_range, 1169 scoped_refptr<IndexedDBCallbacks> callbacks, 1170 IndexedDBTransaction* transaction) { 1171 IDB_TRACE("IndexedDBDatabase::DeleteRangeOperation"); 1172 scoped_ptr<IndexedDBBackingStore::Cursor> backing_store_cursor = 1173 backing_store_->OpenObjectStoreCursor( 1174 transaction->BackingStoreTransaction(), 1175 id(), 1176 object_store_id, 1177 *key_range, 1178 indexed_db::CURSOR_NEXT); 1179 if (backing_store_cursor) { 1180 do { 1181 if (!backing_store_->DeleteRecord( 1182 transaction->BackingStoreTransaction(), 1183 id(), 1184 object_store_id, 1185 backing_store_cursor->record_identifier()) 1186 .ok()) { 1187 callbacks->OnError( 1188 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, 1189 "Internal error deleting data in range")); 1190 return; 1191 } 1192 } while (backing_store_cursor->Continue()); 1193 } 1194 1195 callbacks->OnSuccess(); 1196} 1197 1198void IndexedDBDatabase::Clear(int64 transaction_id, 1199 int64 object_store_id, 1200 scoped_refptr<IndexedDBCallbacks> callbacks) { 1201 IDB_TRACE("IndexedDBDatabase::Clear"); 1202 IndexedDBTransaction* transaction = GetTransaction(transaction_id); 1203 if (!transaction) 1204 return; 1205 DCHECK_NE(transaction->mode(), indexed_db::TRANSACTION_READ_ONLY); 1206 1207 if (!ValidateObjectStoreId(object_store_id)) 1208 return; 1209 1210 transaction->ScheduleTask(base::Bind( 1211 &IndexedDBDatabase::ClearOperation, this, object_store_id, callbacks)); 1212} 1213 1214void IndexedDBDatabase::ClearOperation( 1215 int64 object_store_id, 1216 scoped_refptr<IndexedDBCallbacks> callbacks, 1217 IndexedDBTransaction* transaction) { 1218 IDB_TRACE("IndexedDBDatabase::ObjectStoreClearOperation"); 1219 if (!backing_store_->ClearObjectStore(transaction->BackingStoreTransaction(), 1220 id(), 1221 object_store_id).ok()) { 1222 callbacks->OnError( 1223 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, 1224 "Internal error clearing object store")); 1225 return; 1226 } 1227 callbacks->OnSuccess(); 1228} 1229 1230void IndexedDBDatabase::DeleteObjectStoreOperation( 1231 const IndexedDBObjectStoreMetadata& object_store_metadata, 1232 IndexedDBTransaction* transaction) { 1233 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreOperation"); 1234 leveldb::Status s = 1235 backing_store_->DeleteObjectStore(transaction->BackingStoreTransaction(), 1236 transaction->database()->id(), 1237 object_store_metadata.id); 1238 if (!s.ok()) { 1239 base::string16 error_string = 1240 ASCIIToUTF16("Internal error deleting object store '") + 1241 object_store_metadata.name + ASCIIToUTF16("'."); 1242 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 1243 error_string); 1244 transaction->Abort(error); 1245 if (s.IsCorruption()) 1246 factory_->HandleBackingStoreCorruption(backing_store_->origin_url(), 1247 error); 1248 } 1249} 1250 1251void IndexedDBDatabase::VersionChangeOperation( 1252 int64 version, 1253 scoped_refptr<IndexedDBCallbacks> callbacks, 1254 scoped_ptr<IndexedDBConnection> connection, 1255 IndexedDBTransaction* transaction) { 1256 IDB_TRACE("IndexedDBDatabase::VersionChangeOperation"); 1257 int64 old_version = metadata_.int_version; 1258 DCHECK_GT(version, old_version); 1259 metadata_.int_version = version; 1260 if (!backing_store_->UpdateIDBDatabaseIntVersion( 1261 transaction->BackingStoreTransaction(), 1262 id(), 1263 metadata_.int_version)) { 1264 IndexedDBDatabaseError error( 1265 blink::WebIDBDatabaseExceptionUnknownError, 1266 ASCIIToUTF16( 1267 "Internal error writing data to stable storage when " 1268 "updating version.")); 1269 callbacks->OnError(error); 1270 transaction->Abort(error); 1271 return; 1272 } 1273 DCHECK(!pending_second_half_open_); 1274 pending_second_half_open_.reset( 1275 new PendingSuccessCall(callbacks, connection.get(), version)); 1276 callbacks->OnUpgradeNeeded(old_version, connection.Pass(), metadata()); 1277} 1278 1279void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction, 1280 bool committed) { 1281 DCHECK(transactions_.find(transaction->id()) != transactions_.end()); 1282 DCHECK_EQ(transactions_[transaction->id()], transaction); 1283 transactions_.erase(transaction->id()); 1284 1285 if (transaction->mode() == indexed_db::TRANSACTION_VERSION_CHANGE) { 1286 if (pending_second_half_open_) { 1287 if (committed) { 1288 DCHECK_EQ(pending_second_half_open_->version(), metadata_.int_version); 1289 DCHECK(metadata_.id != kInvalidId); 1290 1291 // Connection was already minted for OnUpgradeNeeded callback. 1292 scoped_ptr<IndexedDBConnection> connection; 1293 pending_second_half_open_->callbacks()->OnSuccess(connection.Pass(), 1294 this->metadata()); 1295 } else { 1296 pending_second_half_open_->callbacks()->OnError( 1297 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError, 1298 "Version change transaction was aborted in " 1299 "upgradeneeded event handler.")); 1300 } 1301 pending_second_half_open_.reset(); 1302 } 1303 1304 // Connection queue is now unblocked. 1305 ProcessPendingCalls(); 1306 } 1307} 1308 1309void IndexedDBDatabase::TransactionCommitFailed() { 1310 // Factory may be null in unit tests. 1311 if (!factory_) 1312 return; 1313 factory_->HandleBackingStoreFailure(backing_store_->origin_url()); 1314} 1315 1316size_t IndexedDBDatabase::ConnectionCount() const { 1317 // This does not include pending open calls, as those should not block version 1318 // changes and deletes. 1319 return connections_.size(); 1320} 1321 1322size_t IndexedDBDatabase::PendingOpenCount() const { 1323 return pending_open_calls_.size(); 1324} 1325 1326size_t IndexedDBDatabase::PendingUpgradeCount() const { 1327 return pending_run_version_change_transaction_call_ ? 1 : 0; 1328} 1329 1330size_t IndexedDBDatabase::RunningUpgradeCount() const { 1331 return pending_second_half_open_ ? 1 : 0; 1332} 1333 1334size_t IndexedDBDatabase::PendingDeleteCount() const { 1335 return pending_delete_calls_.size(); 1336} 1337 1338void IndexedDBDatabase::ProcessPendingCalls() { 1339 if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) { 1340 DCHECK(pending_run_version_change_transaction_call_->version() > 1341 metadata_.int_version); 1342 scoped_ptr<PendingUpgradeCall> pending_call = 1343 pending_run_version_change_transaction_call_.Pass(); 1344 RunVersionChangeTransactionFinal(pending_call->callbacks(), 1345 pending_call->ReleaseConnection(), 1346 pending_call->transaction_id(), 1347 pending_call->version()); 1348 DCHECK_EQ(1u, ConnectionCount()); 1349 // Fall through would be a no-op, since transaction must complete 1350 // asynchronously. 1351 DCHECK(IsDeleteDatabaseBlocked()); 1352 DCHECK(IsOpenConnectionBlocked()); 1353 return; 1354 } 1355 1356 if (!IsDeleteDatabaseBlocked()) { 1357 PendingDeleteCallList pending_delete_calls; 1358 pending_delete_calls_.swap(pending_delete_calls); 1359 while (!pending_delete_calls.empty()) { 1360 // Only the first delete call will delete the database, but each must fire 1361 // callbacks. 1362 scoped_ptr<PendingDeleteCall> pending_delete_call( 1363 pending_delete_calls.front()); 1364 pending_delete_calls.pop_front(); 1365 DeleteDatabaseFinal(pending_delete_call->callbacks()); 1366 } 1367 // delete_database_final should never re-queue calls. 1368 DCHECK(pending_delete_calls_.empty()); 1369 // Fall through when complete, as pending opens may be unblocked. 1370 } 1371 1372 if (!IsOpenConnectionBlocked()) { 1373 PendingOpenCallList pending_open_calls; 1374 pending_open_calls_.swap(pending_open_calls); 1375 while (!pending_open_calls.empty()) { 1376 OpenConnection(pending_open_calls.front()); 1377 pending_open_calls.pop_front(); 1378 } 1379 } 1380} 1381 1382void IndexedDBDatabase::CreateTransaction( 1383 int64 transaction_id, 1384 IndexedDBConnection* connection, 1385 const std::vector<int64>& object_store_ids, 1386 uint16 mode) { 1387 1388 DCHECK(connections_.count(connection)); 1389 DCHECK(transactions_.find(transaction_id) == transactions_.end()); 1390 if (transactions_.find(transaction_id) != transactions_.end()) 1391 return; 1392 1393 // The transaction will add itself to this database's coordinator, which 1394 // manages the lifetime of the object. 1395 TransactionCreated(new IndexedDBTransaction( 1396 transaction_id, 1397 connection->callbacks(), 1398 std::set<int64>(object_store_ids.begin(), object_store_ids.end()), 1399 static_cast<indexed_db::TransactionMode>(mode), 1400 this, 1401 new IndexedDBBackingStore::Transaction(backing_store_))); 1402} 1403 1404void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) { 1405 transactions_[transaction->id()] = transaction; 1406} 1407 1408bool IndexedDBDatabase::IsOpenConnectionBlocked() const { 1409 return !pending_delete_calls_.empty() || 1410 transaction_coordinator_.IsRunningVersionChangeTransaction() || 1411 pending_run_version_change_transaction_call_; 1412} 1413 1414void IndexedDBDatabase::OpenConnection( 1415 const IndexedDBPendingConnection& connection) { 1416 DCHECK(backing_store_); 1417 1418 // TODO(jsbell): Should have a priority queue so that higher version 1419 // requests are processed first. http://crbug.com/225850 1420 if (IsOpenConnectionBlocked()) { 1421 // The backing store only detects data loss when it is first opened. The 1422 // presence of existing connections means we didn't even check for data loss 1423 // so there'd better not be any. 1424 DCHECK_NE(blink::WebIDBDataLossTotal, connection.callbacks->data_loss()); 1425 pending_open_calls_.push_back(connection); 1426 return; 1427 } 1428 1429 if (metadata_.id == kInvalidId) { 1430 // The database was deleted then immediately re-opened; OpenInternal() 1431 // recreates it in the backing store. 1432 if (OpenInternal().ok()) { 1433 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_INT_VERSION, 1434 metadata_.int_version); 1435 } else { 1436 base::string16 message; 1437 if (connection.version == IndexedDBDatabaseMetadata::NO_INT_VERSION) { 1438 message = ASCIIToUTF16( 1439 "Internal error opening database with no version specified."); 1440 } else { 1441 message = 1442 ASCIIToUTF16("Internal error opening database with version ") + 1443 Int64ToString16(connection.version); 1444 } 1445 connection.callbacks->OnError(IndexedDBDatabaseError( 1446 blink::WebIDBDatabaseExceptionUnknownError, message)); 1447 return; 1448 } 1449 } 1450 1451 // We infer that the database didn't exist from its lack of either type of 1452 // version. 1453 bool is_new_database = 1454 metadata_.version == kNoStringVersion && 1455 metadata_.int_version == IndexedDBDatabaseMetadata::NO_INT_VERSION; 1456 1457 if (connection.version == IndexedDBDatabaseMetadata::DEFAULT_INT_VERSION) { 1458 // For unit tests only - skip upgrade steps. Calling from script with 1459 // DEFAULT_INT_VERSION throws exception. 1460 // TODO(jsbell): DCHECK that not in unit tests. 1461 DCHECK(is_new_database); 1462 connection.callbacks->OnSuccess( 1463 CreateConnection(connection.database_callbacks, 1464 connection.child_process_id), 1465 this->metadata()); 1466 return; 1467 } 1468 1469 // We may need to change the version. 1470 int64 local_version = connection.version; 1471 if (local_version == IndexedDBDatabaseMetadata::NO_INT_VERSION) { 1472 if (!is_new_database) { 1473 connection.callbacks->OnSuccess( 1474 CreateConnection(connection.database_callbacks, 1475 connection.child_process_id), 1476 this->metadata()); 1477 return; 1478 } 1479 // Spec says: If no version is specified and no database exists, set 1480 // database version to 1. 1481 local_version = 1; 1482 } 1483 1484 if (local_version > metadata_.int_version) { 1485 RunVersionChangeTransaction(connection.callbacks, 1486 CreateConnection(connection.database_callbacks, 1487 connection.child_process_id), 1488 connection.transaction_id, 1489 local_version); 1490 return; 1491 } 1492 if (local_version < metadata_.int_version) { 1493 connection.callbacks->OnError(IndexedDBDatabaseError( 1494 blink::WebIDBDatabaseExceptionVersionError, 1495 ASCIIToUTF16("The requested version (") + 1496 Int64ToString16(local_version) + 1497 ASCIIToUTF16(") is less than the existing version (") + 1498 Int64ToString16(metadata_.int_version) + ASCIIToUTF16(")."))); 1499 return; 1500 } 1501 DCHECK_EQ(local_version, metadata_.int_version); 1502 connection.callbacks->OnSuccess( 1503 CreateConnection(connection.database_callbacks, 1504 connection.child_process_id), 1505 this->metadata()); 1506} 1507 1508void IndexedDBDatabase::RunVersionChangeTransaction( 1509 scoped_refptr<IndexedDBCallbacks> callbacks, 1510 scoped_ptr<IndexedDBConnection> connection, 1511 int64 transaction_id, 1512 int64 requested_version) { 1513 1514 DCHECK(callbacks); 1515 DCHECK(connections_.count(connection.get())); 1516 if (ConnectionCount() > 1) { 1517 DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss()); 1518 // Front end ensures the event is not fired at connections that have 1519 // close_pending set. 1520 for (ConnectionSet::const_iterator it = connections_.begin(); 1521 it != connections_.end(); 1522 ++it) { 1523 if (*it != connection.get()) { 1524 (*it)->callbacks()->OnVersionChange(metadata_.int_version, 1525 requested_version); 1526 } 1527 } 1528 // TODO(jsbell): Remove the call to OnBlocked and instead wait 1529 // until the frontend tells us that all the "versionchange" events 1530 // have been delivered. http://crbug.com/100123 1531 callbacks->OnBlocked(metadata_.int_version); 1532 1533 DCHECK(!pending_run_version_change_transaction_call_); 1534 pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall( 1535 callbacks, connection.Pass(), transaction_id, requested_version)); 1536 return; 1537 } 1538 RunVersionChangeTransactionFinal( 1539 callbacks, connection.Pass(), transaction_id, requested_version); 1540} 1541 1542void IndexedDBDatabase::RunVersionChangeTransactionFinal( 1543 scoped_refptr<IndexedDBCallbacks> callbacks, 1544 scoped_ptr<IndexedDBConnection> connection, 1545 int64 transaction_id, 1546 int64 requested_version) { 1547 1548 std::vector<int64> object_store_ids; 1549 CreateTransaction(transaction_id, 1550 connection.get(), 1551 object_store_ids, 1552 indexed_db::TRANSACTION_VERSION_CHANGE); 1553 1554 transactions_[transaction_id] 1555 ->ScheduleTask(base::Bind(&IndexedDBDatabase::VersionChangeOperation, 1556 this, 1557 requested_version, 1558 callbacks, 1559 base::Passed(&connection)), 1560 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation, 1561 this, 1562 metadata_.version, 1563 metadata_.int_version)); 1564 1565 DCHECK(!pending_second_half_open_); 1566} 1567 1568void IndexedDBDatabase::DeleteDatabase( 1569 scoped_refptr<IndexedDBCallbacks> callbacks) { 1570 1571 if (IsDeleteDatabaseBlocked()) { 1572 for (ConnectionSet::const_iterator it = connections_.begin(); 1573 it != connections_.end(); 1574 ++it) { 1575 // Front end ensures the event is not fired at connections that have 1576 // close_pending set. 1577 (*it)->callbacks()->OnVersionChange( 1578 metadata_.int_version, IndexedDBDatabaseMetadata::NO_INT_VERSION); 1579 } 1580 // TODO(jsbell): Only fire OnBlocked if there are open 1581 // connections after the VersionChangeEvents are received, not 1582 // just set up to fire. http://crbug.com/100123 1583 callbacks->OnBlocked(metadata_.int_version); 1584 pending_delete_calls_.push_back(new PendingDeleteCall(callbacks)); 1585 return; 1586 } 1587 DeleteDatabaseFinal(callbacks); 1588} 1589 1590bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const { 1591 return !!ConnectionCount(); 1592} 1593 1594void IndexedDBDatabase::DeleteDatabaseFinal( 1595 scoped_refptr<IndexedDBCallbacks> callbacks) { 1596 DCHECK(!IsDeleteDatabaseBlocked()); 1597 DCHECK(backing_store_); 1598 if (!backing_store_->DeleteDatabase(metadata_.name).ok()) { 1599 callbacks->OnError( 1600 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, 1601 "Internal error deleting database.")); 1602 return; 1603 } 1604 metadata_.version = kNoStringVersion; 1605 metadata_.id = kInvalidId; 1606 metadata_.int_version = IndexedDBDatabaseMetadata::NO_INT_VERSION; 1607 metadata_.object_stores.clear(); 1608 callbacks->OnSuccess(); 1609 if (factory_) 1610 factory_->DatabaseDeleted(identifier_); 1611} 1612 1613void IndexedDBDatabase::ForceClose() { 1614 // IndexedDBConnection::ForceClose() may delete this database, so hold ref. 1615 scoped_refptr<IndexedDBDatabase> protect(this); 1616 ConnectionSet::const_iterator it = connections_.begin(); 1617 while (it != connections_.end()) { 1618 IndexedDBConnection* connection = *it++; 1619 connection->ForceClose(); 1620 } 1621 DCHECK(connections_.empty()); 1622} 1623 1624void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) { 1625 DCHECK(connections_.count(connection)); 1626 DCHECK(connection->IsConnected()); 1627 DCHECK(connection->database() == this); 1628 1629 // Abort outstanding transactions from the closing connection. This 1630 // can not happen if the close is requested by the connection itself 1631 // as the front-end defers the close until all transactions are 1632 // complete, but can occur on process termination or forced close. 1633 { 1634 TransactionMap transactions(transactions_); 1635 for (TransactionMap::const_iterator it = transactions.begin(), 1636 end = transactions.end(); 1637 it != end; 1638 ++it) { 1639 if (it->second->connection() == connection->callbacks()) 1640 it->second->Abort( 1641 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, 1642 "Connection is closing.")); 1643 } 1644 } 1645 1646 connections_.erase(connection); 1647 if (pending_second_half_open_ && 1648 pending_second_half_open_->connection() == connection) { 1649 pending_second_half_open_->callbacks()->OnError( 1650 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError, 1651 "The connection was closed.")); 1652 pending_second_half_open_.reset(); 1653 } 1654 1655 ProcessPendingCalls(); 1656 1657 // TODO(jsbell): Add a test for the pending_open_calls_ cases below. 1658 if (!ConnectionCount() && !pending_open_calls_.size() && 1659 !pending_delete_calls_.size()) { 1660 DCHECK(transactions_.empty()); 1661 1662 const GURL origin_url = backing_store_->origin_url(); 1663 backing_store_ = NULL; 1664 1665 // factory_ should only be null in unit tests. 1666 // TODO(jsbell): DCHECK(factory_ || !in_unit_tests) - somehow. 1667 if (factory_) { 1668 factory_->ReleaseDatabase(identifier_, forced); 1669 factory_ = NULL; 1670 } 1671 } 1672} 1673 1674void IndexedDBDatabase::CreateObjectStoreAbortOperation( 1675 int64 object_store_id, 1676 IndexedDBTransaction* transaction) { 1677 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation"); 1678 DCHECK(!transaction); 1679 RemoveObjectStore(object_store_id); 1680} 1681 1682void IndexedDBDatabase::DeleteObjectStoreAbortOperation( 1683 const IndexedDBObjectStoreMetadata& object_store_metadata, 1684 IndexedDBTransaction* transaction) { 1685 IDB_TRACE("IndexedDBDatabase::DeleteObjectStoreAbortOperation"); 1686 DCHECK(!transaction); 1687 AddObjectStore(object_store_metadata, 1688 IndexedDBObjectStoreMetadata::kInvalidId); 1689} 1690 1691void IndexedDBDatabase::VersionChangeAbortOperation( 1692 const base::string16& previous_version, 1693 int64 previous_int_version, 1694 IndexedDBTransaction* transaction) { 1695 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation"); 1696 DCHECK(!transaction); 1697 metadata_.version = previous_version; 1698 metadata_.int_version = previous_int_version; 1699} 1700 1701} // namespace content 1702