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