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