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