1 // Copyright (c) 2012 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 "webkit/browser/appcache/appcache_database.h" 6 7 #include "base/auto_reset.h" 8 #include "base/command_line.h" 9 #include "base/file_util.h" 10 #include "base/logging.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "sql/connection.h" 13 #include "sql/meta_table.h" 14 #include "sql/statement.h" 15 #include "sql/transaction.h" 16 #include "webkit/browser/appcache/appcache_entry.h" 17 #include "webkit/browser/appcache/appcache_histograms.h" 18 19 namespace appcache { 20 21 // Schema ------------------------------------------------------------------- 22 namespace { 23 24 #if defined(APPCACHE_USE_SIMPLE_CACHE) 25 const int kCurrentVersion = 6; 26 const int kCompatibleVersion = 6; 27 #else 28 const int kCurrentVersion = 5; 29 const int kCompatibleVersion = 5; 30 #endif 31 32 // A mechanism to run experiments that may affect in data being persisted 33 // in different ways such that when the experiment is toggled on/off via 34 // cmd line flags, the database gets reset. The active flags are stored at 35 // the time of database creation and compared when reopening. If different 36 // the database is reset. 37 const char kExperimentFlagsKey[] = "ExperimentFlags"; 38 39 const char kGroupsTable[] = "Groups"; 40 const char kCachesTable[] = "Caches"; 41 const char kEntriesTable[] = "Entries"; 42 const char kNamespacesTable[] = "Namespaces"; 43 const char kOnlineWhiteListsTable[] = "OnlineWhiteLists"; 44 const char kDeletableResponseIdsTable[] = "DeletableResponseIds"; 45 46 struct TableInfo { 47 const char* table_name; 48 const char* columns; 49 }; 50 51 struct IndexInfo { 52 const char* index_name; 53 const char* table_name; 54 const char* columns; 55 bool unique; 56 }; 57 58 const TableInfo kTables[] = { 59 { kGroupsTable, 60 "(group_id INTEGER PRIMARY KEY," 61 " origin TEXT," 62 " manifest_url TEXT," 63 " creation_time INTEGER," 64 " last_access_time INTEGER)" }, 65 66 { kCachesTable, 67 "(cache_id INTEGER PRIMARY KEY," 68 " group_id INTEGER," 69 " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1))," 70 " update_time INTEGER," 71 " cache_size INTEGER)" }, // intentionally not normalized 72 73 { kEntriesTable, 74 "(cache_id INTEGER," 75 " url TEXT," 76 " flags INTEGER," 77 " response_id INTEGER," 78 " response_size INTEGER)" }, 79 80 { kNamespacesTable, 81 "(cache_id INTEGER," 82 " origin TEXT," // intentionally not normalized 83 " type INTEGER," 84 " namespace_url TEXT," 85 " target_url TEXT," 86 " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))" }, 87 88 { kOnlineWhiteListsTable, 89 "(cache_id INTEGER," 90 " namespace_url TEXT," 91 " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))" }, 92 93 { kDeletableResponseIdsTable, 94 "(response_id INTEGER NOT NULL)" }, 95 }; 96 97 const IndexInfo kIndexes[] = { 98 { "GroupsOriginIndex", 99 kGroupsTable, 100 "(origin)", 101 false }, 102 103 { "GroupsManifestIndex", 104 kGroupsTable, 105 "(manifest_url)", 106 true }, 107 108 { "CachesGroupIndex", 109 kCachesTable, 110 "(group_id)", 111 false }, 112 113 { "EntriesCacheIndex", 114 kEntriesTable, 115 "(cache_id)", 116 false }, 117 118 { "EntriesCacheAndUrlIndex", 119 kEntriesTable, 120 "(cache_id, url)", 121 true }, 122 123 { "EntriesResponseIdIndex", 124 kEntriesTable, 125 "(response_id)", 126 true }, 127 128 { "NamespacesCacheIndex", 129 kNamespacesTable, 130 "(cache_id)", 131 false }, 132 133 { "NamespacesOriginIndex", 134 kNamespacesTable, 135 "(origin)", 136 false }, 137 138 { "NamespacesCacheAndUrlIndex", 139 kNamespacesTable, 140 "(cache_id, namespace_url)", 141 true }, 142 143 { "OnlineWhiteListCacheIndex", 144 kOnlineWhiteListsTable, 145 "(cache_id)", 146 false }, 147 148 { "DeletableResponsesIdIndex", 149 kDeletableResponseIdsTable, 150 "(response_id)", 151 true }, 152 }; 153 154 const int kTableCount = ARRAYSIZE_UNSAFE(kTables); 155 const int kIndexCount = ARRAYSIZE_UNSAFE(kIndexes); 156 157 bool CreateTable(sql::Connection* db, const TableInfo& info) { 158 std::string sql("CREATE TABLE "); 159 sql += info.table_name; 160 sql += info.columns; 161 return db->Execute(sql.c_str()); 162 } 163 164 bool CreateIndex(sql::Connection* db, const IndexInfo& info) { 165 std::string sql; 166 if (info.unique) 167 sql += "CREATE UNIQUE INDEX "; 168 else 169 sql += "CREATE INDEX "; 170 sql += info.index_name; 171 sql += " ON "; 172 sql += info.table_name; 173 sql += info.columns; 174 return db->Execute(sql.c_str()); 175 } 176 177 std::string GetActiveExperimentFlags() { 178 if (CommandLine::ForCurrentProcess()->HasSwitch(kEnableExecutableHandlers)) 179 return std::string("executableHandlersEnabled"); 180 return std::string(); 181 } 182 183 } // anon namespace 184 185 // AppCacheDatabase ---------------------------------------------------------- 186 187 AppCacheDatabase::GroupRecord::GroupRecord() 188 : group_id(0) { 189 } 190 191 AppCacheDatabase::GroupRecord::~GroupRecord() { 192 } 193 194 AppCacheDatabase::NamespaceRecord::NamespaceRecord() 195 : cache_id(0) { 196 } 197 198 AppCacheDatabase::NamespaceRecord::~NamespaceRecord() { 199 } 200 201 202 AppCacheDatabase::AppCacheDatabase(const base::FilePath& path) 203 : db_file_path_(path), is_disabled_(false), is_recreating_(false) { 204 } 205 206 AppCacheDatabase::~AppCacheDatabase() { 207 } 208 209 void AppCacheDatabase::CloseConnection() { 210 // We can't close the connection for an in-memory database w/o 211 // losing all of the data, so we don't do that. 212 if (!db_file_path_.empty()) 213 ResetConnectionAndTables(); 214 } 215 216 void AppCacheDatabase::Disable() { 217 VLOG(1) << "Disabling appcache database."; 218 is_disabled_ = true; 219 ResetConnectionAndTables(); 220 } 221 222 int64 AppCacheDatabase::GetOriginUsage(const GURL& origin) { 223 std::vector<CacheRecord> records; 224 if (!FindCachesForOrigin(origin, &records)) 225 return 0; 226 227 int64 origin_usage = 0; 228 std::vector<CacheRecord>::const_iterator iter = records.begin(); 229 while (iter != records.end()) { 230 origin_usage += iter->cache_size; 231 ++iter; 232 } 233 return origin_usage; 234 } 235 236 bool AppCacheDatabase::GetAllOriginUsage(std::map<GURL, int64>* usage_map) { 237 std::set<GURL> origins; 238 if (!FindOriginsWithGroups(&origins)) 239 return false; 240 for (std::set<GURL>::const_iterator origin = origins.begin(); 241 origin != origins.end(); ++origin) { 242 (*usage_map)[*origin] = GetOriginUsage(*origin); 243 } 244 return true; 245 } 246 247 bool AppCacheDatabase::FindOriginsWithGroups(std::set<GURL>* origins) { 248 DCHECK(origins && origins->empty()); 249 if (!LazyOpen(false)) 250 return false; 251 252 const char* kSql = 253 "SELECT DISTINCT(origin) FROM Groups"; 254 255 sql::Statement statement(db_->GetUniqueStatement(kSql)); 256 257 while (statement.Step()) 258 origins->insert(GURL(statement.ColumnString(0))); 259 260 return statement.Succeeded(); 261 } 262 263 bool AppCacheDatabase::FindLastStorageIds( 264 int64* last_group_id, int64* last_cache_id, int64* last_response_id, 265 int64* last_deletable_response_rowid) { 266 DCHECK(last_group_id && last_cache_id && last_response_id && 267 last_deletable_response_rowid); 268 269 *last_group_id = 0; 270 *last_cache_id = 0; 271 *last_response_id = 0; 272 *last_deletable_response_rowid = 0; 273 274 if (!LazyOpen(false)) 275 return false; 276 277 const char* kMaxGroupIdSql = "SELECT MAX(group_id) FROM Groups"; 278 const char* kMaxCacheIdSql = "SELECT MAX(cache_id) FROM Caches"; 279 const char* kMaxResponseIdFromEntriesSql = 280 "SELECT MAX(response_id) FROM Entries"; 281 const char* kMaxResponseIdFromDeletablesSql = 282 "SELECT MAX(response_id) FROM DeletableResponseIds"; 283 const char* kMaxDeletableResponseRowIdSql = 284 "SELECT MAX(rowid) FROM DeletableResponseIds"; 285 int64 max_group_id; 286 int64 max_cache_id; 287 int64 max_response_id_from_entries; 288 int64 max_response_id_from_deletables; 289 int64 max_deletable_response_rowid; 290 if (!RunUniqueStatementWithInt64Result(kMaxGroupIdSql, &max_group_id) || 291 !RunUniqueStatementWithInt64Result(kMaxCacheIdSql, &max_cache_id) || 292 !RunUniqueStatementWithInt64Result(kMaxResponseIdFromEntriesSql, 293 &max_response_id_from_entries) || 294 !RunUniqueStatementWithInt64Result(kMaxResponseIdFromDeletablesSql, 295 &max_response_id_from_deletables) || 296 !RunUniqueStatementWithInt64Result(kMaxDeletableResponseRowIdSql, 297 &max_deletable_response_rowid)) { 298 return false; 299 } 300 301 *last_group_id = max_group_id; 302 *last_cache_id = max_cache_id; 303 *last_response_id = std::max(max_response_id_from_entries, 304 max_response_id_from_deletables); 305 *last_deletable_response_rowid = max_deletable_response_rowid; 306 return true; 307 } 308 309 bool AppCacheDatabase::FindGroup(int64 group_id, GroupRecord* record) { 310 DCHECK(record); 311 if (!LazyOpen(false)) 312 return false; 313 314 const char* kSql = 315 "SELECT group_id, origin, manifest_url," 316 " creation_time, last_access_time" 317 " FROM Groups WHERE group_id = ?"; 318 319 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 320 321 statement.BindInt64(0, group_id); 322 if (!statement.Step()) 323 return false; 324 325 ReadGroupRecord(statement, record); 326 DCHECK(record->group_id == group_id); 327 return true; 328 } 329 330 bool AppCacheDatabase::FindGroupForManifestUrl( 331 const GURL& manifest_url, GroupRecord* record) { 332 DCHECK(record); 333 if (!LazyOpen(false)) 334 return false; 335 336 const char* kSql = 337 "SELECT group_id, origin, manifest_url," 338 " creation_time, last_access_time" 339 " FROM Groups WHERE manifest_url = ?"; 340 341 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 342 statement.BindString(0, manifest_url.spec()); 343 344 if (!statement.Step()) 345 return false; 346 347 ReadGroupRecord(statement, record); 348 DCHECK(record->manifest_url == manifest_url); 349 return true; 350 } 351 352 bool AppCacheDatabase::FindGroupsForOrigin( 353 const GURL& origin, std::vector<GroupRecord>* records) { 354 DCHECK(records && records->empty()); 355 if (!LazyOpen(false)) 356 return false; 357 358 const char* kSql = 359 "SELECT group_id, origin, manifest_url," 360 " creation_time, last_access_time" 361 " FROM Groups WHERE origin = ?"; 362 363 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 364 statement.BindString(0, origin.spec()); 365 366 while (statement.Step()) { 367 records->push_back(GroupRecord()); 368 ReadGroupRecord(statement, &records->back()); 369 DCHECK(records->back().origin == origin); 370 } 371 372 return statement.Succeeded(); 373 } 374 375 bool AppCacheDatabase::FindGroupForCache(int64 cache_id, GroupRecord* record) { 376 DCHECK(record); 377 if (!LazyOpen(false)) 378 return false; 379 380 const char* kSql = 381 "SELECT g.group_id, g.origin, g.manifest_url," 382 " g.creation_time, g.last_access_time" 383 " FROM Groups g, Caches c" 384 " WHERE c.cache_id = ? AND c.group_id = g.group_id"; 385 386 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 387 statement.BindInt64(0, cache_id); 388 389 if (!statement.Step()) 390 return false; 391 392 ReadGroupRecord(statement, record); 393 return true; 394 } 395 396 bool AppCacheDatabase::UpdateGroupLastAccessTime( 397 int64 group_id, base::Time time) { 398 if (!LazyOpen(true)) 399 return false; 400 401 const char* kSql = 402 "UPDATE Groups SET last_access_time = ? WHERE group_id = ?"; 403 404 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 405 statement.BindInt64(0, time.ToInternalValue()); 406 statement.BindInt64(1, group_id); 407 408 return statement.Run() && db_->GetLastChangeCount(); 409 } 410 411 bool AppCacheDatabase::InsertGroup(const GroupRecord* record) { 412 if (!LazyOpen(true)) 413 return false; 414 415 const char* kSql = 416 "INSERT INTO Groups" 417 " (group_id, origin, manifest_url, creation_time, last_access_time)" 418 " VALUES(?, ?, ?, ?, ?)"; 419 420 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 421 statement.BindInt64(0, record->group_id); 422 statement.BindString(1, record->origin.spec()); 423 statement.BindString(2, record->manifest_url.spec()); 424 statement.BindInt64(3, record->creation_time.ToInternalValue()); 425 statement.BindInt64(4, record->last_access_time.ToInternalValue()); 426 427 return statement.Run(); 428 } 429 430 bool AppCacheDatabase::DeleteGroup(int64 group_id) { 431 if (!LazyOpen(false)) 432 return false; 433 434 const char* kSql = 435 "DELETE FROM Groups WHERE group_id = ?"; 436 437 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 438 statement.BindInt64(0, group_id); 439 440 return statement.Run(); 441 } 442 443 bool AppCacheDatabase::FindCache(int64 cache_id, CacheRecord* record) { 444 DCHECK(record); 445 if (!LazyOpen(false)) 446 return false; 447 448 const char* kSql = 449 "SELECT cache_id, group_id, online_wildcard, update_time, cache_size" 450 " FROM Caches WHERE cache_id = ?"; 451 452 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 453 statement.BindInt64(0, cache_id); 454 455 if (!statement.Step()) 456 return false; 457 458 ReadCacheRecord(statement, record); 459 return true; 460 } 461 462 bool AppCacheDatabase::FindCacheForGroup(int64 group_id, CacheRecord* record) { 463 DCHECK(record); 464 if (!LazyOpen(false)) 465 return false; 466 467 const char* kSql = 468 "SELECT cache_id, group_id, online_wildcard, update_time, cache_size" 469 " FROM Caches WHERE group_id = ?"; 470 471 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 472 statement.BindInt64(0, group_id); 473 474 if (!statement.Step()) 475 return false; 476 477 ReadCacheRecord(statement, record); 478 return true; 479 } 480 481 bool AppCacheDatabase::FindCachesForOrigin( 482 const GURL& origin, std::vector<CacheRecord>* records) { 483 DCHECK(records); 484 std::vector<GroupRecord> group_records; 485 if (!FindGroupsForOrigin(origin, &group_records)) 486 return false; 487 488 CacheRecord cache_record; 489 std::vector<GroupRecord>::const_iterator iter = group_records.begin(); 490 while (iter != group_records.end()) { 491 if (FindCacheForGroup(iter->group_id, &cache_record)) 492 records->push_back(cache_record); 493 ++iter; 494 } 495 return true; 496 } 497 498 bool AppCacheDatabase::InsertCache(const CacheRecord* record) { 499 if (!LazyOpen(true)) 500 return false; 501 502 const char* kSql = 503 "INSERT INTO Caches (cache_id, group_id, online_wildcard," 504 " update_time, cache_size)" 505 " VALUES(?, ?, ?, ?, ?)"; 506 507 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 508 statement.BindInt64(0, record->cache_id); 509 statement.BindInt64(1, record->group_id); 510 statement.BindBool(2, record->online_wildcard); 511 statement.BindInt64(3, record->update_time.ToInternalValue()); 512 statement.BindInt64(4, record->cache_size); 513 514 return statement.Run(); 515 } 516 517 bool AppCacheDatabase::DeleteCache(int64 cache_id) { 518 if (!LazyOpen(false)) 519 return false; 520 521 const char* kSql = 522 "DELETE FROM Caches WHERE cache_id = ?"; 523 524 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 525 statement.BindInt64(0, cache_id); 526 527 return statement.Run(); 528 } 529 530 bool AppCacheDatabase::FindEntriesForCache( 531 int64 cache_id, std::vector<EntryRecord>* records) { 532 DCHECK(records && records->empty()); 533 if (!LazyOpen(false)) 534 return false; 535 536 const char* kSql = 537 "SELECT cache_id, url, flags, response_id, response_size FROM Entries" 538 " WHERE cache_id = ?"; 539 540 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 541 statement.BindInt64(0, cache_id); 542 543 while (statement.Step()) { 544 records->push_back(EntryRecord()); 545 ReadEntryRecord(statement, &records->back()); 546 DCHECK(records->back().cache_id == cache_id); 547 } 548 549 return statement.Succeeded(); 550 } 551 552 bool AppCacheDatabase::FindEntriesForUrl( 553 const GURL& url, std::vector<EntryRecord>* records) { 554 DCHECK(records && records->empty()); 555 if (!LazyOpen(false)) 556 return false; 557 558 const char* kSql = 559 "SELECT cache_id, url, flags, response_id, response_size FROM Entries" 560 " WHERE url = ?"; 561 562 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 563 statement.BindString(0, url.spec()); 564 565 while (statement.Step()) { 566 records->push_back(EntryRecord()); 567 ReadEntryRecord(statement, &records->back()); 568 DCHECK(records->back().url == url); 569 } 570 571 return statement.Succeeded(); 572 } 573 574 bool AppCacheDatabase::FindEntry( 575 int64 cache_id, const GURL& url, EntryRecord* record) { 576 DCHECK(record); 577 if (!LazyOpen(false)) 578 return false; 579 580 const char* kSql = 581 "SELECT cache_id, url, flags, response_id, response_size FROM Entries" 582 " WHERE cache_id = ? AND url = ?"; 583 584 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 585 statement.BindInt64(0, cache_id); 586 statement.BindString(1, url.spec()); 587 588 if (!statement.Step()) 589 return false; 590 591 ReadEntryRecord(statement, record); 592 DCHECK(record->cache_id == cache_id); 593 DCHECK(record->url == url); 594 return true; 595 } 596 597 bool AppCacheDatabase::InsertEntry(const EntryRecord* record) { 598 if (!LazyOpen(true)) 599 return false; 600 601 const char* kSql = 602 "INSERT INTO Entries (cache_id, url, flags, response_id, response_size)" 603 " VALUES(?, ?, ?, ?, ?)"; 604 605 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 606 statement.BindInt64(0, record->cache_id); 607 statement.BindString(1, record->url.spec()); 608 statement.BindInt(2, record->flags); 609 statement.BindInt64(3, record->response_id); 610 statement.BindInt64(4, record->response_size); 611 612 return statement.Run(); 613 } 614 615 bool AppCacheDatabase::InsertEntryRecords( 616 const std::vector<EntryRecord>& records) { 617 if (records.empty()) 618 return true; 619 sql::Transaction transaction(db_.get()); 620 if (!transaction.Begin()) 621 return false; 622 std::vector<EntryRecord>::const_iterator iter = records.begin(); 623 while (iter != records.end()) { 624 if (!InsertEntry(&(*iter))) 625 return false; 626 ++iter; 627 } 628 return transaction.Commit(); 629 } 630 631 bool AppCacheDatabase::DeleteEntriesForCache(int64 cache_id) { 632 if (!LazyOpen(false)) 633 return false; 634 635 const char* kSql = 636 "DELETE FROM Entries WHERE cache_id = ?"; 637 638 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 639 statement.BindInt64(0, cache_id); 640 641 return statement.Run(); 642 } 643 644 bool AppCacheDatabase::AddEntryFlags( 645 const GURL& entry_url, int64 cache_id, int additional_flags) { 646 if (!LazyOpen(false)) 647 return false; 648 649 const char* kSql = 650 "UPDATE Entries SET flags = flags | ? WHERE cache_id = ? AND url = ?"; 651 652 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 653 statement.BindInt(0, additional_flags); 654 statement.BindInt64(1, cache_id); 655 statement.BindString(2, entry_url.spec()); 656 657 return statement.Run() && db_->GetLastChangeCount(); 658 } 659 660 bool AppCacheDatabase::FindNamespacesForOrigin( 661 const GURL& origin, 662 std::vector<NamespaceRecord>* intercepts, 663 std::vector<NamespaceRecord>* fallbacks) { 664 DCHECK(intercepts && intercepts->empty()); 665 DCHECK(fallbacks && fallbacks->empty()); 666 if (!LazyOpen(false)) 667 return false; 668 669 const char* kSql = 670 "SELECT cache_id, origin, type, namespace_url, target_url, is_pattern" 671 " FROM Namespaces WHERE origin = ?"; 672 673 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 674 statement.BindString(0, origin.spec()); 675 676 ReadNamespaceRecords(&statement, intercepts, fallbacks); 677 678 return statement.Succeeded(); 679 } 680 681 bool AppCacheDatabase::FindNamespacesForCache( 682 int64 cache_id, 683 std::vector<NamespaceRecord>* intercepts, 684 std::vector<NamespaceRecord>* fallbacks) { 685 DCHECK(intercepts && intercepts->empty()); 686 DCHECK(fallbacks && fallbacks->empty()); 687 if (!LazyOpen(false)) 688 return false; 689 690 const char* kSql = 691 "SELECT cache_id, origin, type, namespace_url, target_url, is_pattern" 692 " FROM Namespaces WHERE cache_id = ?"; 693 694 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 695 statement.BindInt64(0, cache_id); 696 697 ReadNamespaceRecords(&statement, intercepts, fallbacks); 698 699 return statement.Succeeded(); 700 } 701 702 bool AppCacheDatabase::InsertNamespace( 703 const NamespaceRecord* record) { 704 if (!LazyOpen(true)) 705 return false; 706 707 const char* kSql = 708 "INSERT INTO Namespaces" 709 " (cache_id, origin, type, namespace_url, target_url, is_pattern)" 710 " VALUES (?, ?, ?, ?, ?, ?)"; 711 712 // Note: quick and dirty storage for the 'executable' bit w/o changing 713 // schemas, we use the high bit of 'type' field. 714 int type_with_executable_bit = record->namespace_.type; 715 if (record->namespace_.is_executable) { 716 type_with_executable_bit |= 0x8000000; 717 DCHECK(CommandLine::ForCurrentProcess()->HasSwitch( 718 kEnableExecutableHandlers)); 719 } 720 721 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 722 statement.BindInt64(0, record->cache_id); 723 statement.BindString(1, record->origin.spec()); 724 statement.BindInt(2, type_with_executable_bit); 725 statement.BindString(3, record->namespace_.namespace_url.spec()); 726 statement.BindString(4, record->namespace_.target_url.spec()); 727 statement.BindBool(5, record->namespace_.is_pattern); 728 return statement.Run(); 729 } 730 731 bool AppCacheDatabase::InsertNamespaceRecords( 732 const std::vector<NamespaceRecord>& records) { 733 if (records.empty()) 734 return true; 735 sql::Transaction transaction(db_.get()); 736 if (!transaction.Begin()) 737 return false; 738 std::vector<NamespaceRecord>::const_iterator iter = records.begin(); 739 while (iter != records.end()) { 740 if (!InsertNamespace(&(*iter))) 741 return false; 742 ++iter; 743 } 744 return transaction.Commit(); 745 } 746 747 bool AppCacheDatabase::DeleteNamespacesForCache(int64 cache_id) { 748 if (!LazyOpen(false)) 749 return false; 750 751 const char* kSql = 752 "DELETE FROM Namespaces WHERE cache_id = ?"; 753 754 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 755 statement.BindInt64(0, cache_id); 756 757 return statement.Run(); 758 } 759 760 bool AppCacheDatabase::FindOnlineWhiteListForCache( 761 int64 cache_id, std::vector<OnlineWhiteListRecord>* records) { 762 DCHECK(records && records->empty()); 763 if (!LazyOpen(false)) 764 return false; 765 766 const char* kSql = 767 "SELECT cache_id, namespace_url, is_pattern FROM OnlineWhiteLists" 768 " WHERE cache_id = ?"; 769 770 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 771 statement.BindInt64(0, cache_id); 772 773 while (statement.Step()) { 774 records->push_back(OnlineWhiteListRecord()); 775 this->ReadOnlineWhiteListRecord(statement, &records->back()); 776 DCHECK(records->back().cache_id == cache_id); 777 } 778 return statement.Succeeded(); 779 } 780 781 bool AppCacheDatabase::InsertOnlineWhiteList( 782 const OnlineWhiteListRecord* record) { 783 if (!LazyOpen(true)) 784 return false; 785 786 const char* kSql = 787 "INSERT INTO OnlineWhiteLists (cache_id, namespace_url, is_pattern)" 788 " VALUES (?, ?, ?)"; 789 790 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 791 statement.BindInt64(0, record->cache_id); 792 statement.BindString(1, record->namespace_url.spec()); 793 statement.BindBool(2, record->is_pattern); 794 795 return statement.Run(); 796 } 797 798 bool AppCacheDatabase::InsertOnlineWhiteListRecords( 799 const std::vector<OnlineWhiteListRecord>& records) { 800 if (records.empty()) 801 return true; 802 sql::Transaction transaction(db_.get()); 803 if (!transaction.Begin()) 804 return false; 805 std::vector<OnlineWhiteListRecord>::const_iterator iter = records.begin(); 806 while (iter != records.end()) { 807 if (!InsertOnlineWhiteList(&(*iter))) 808 return false; 809 ++iter; 810 } 811 return transaction.Commit(); 812 } 813 814 bool AppCacheDatabase::DeleteOnlineWhiteListForCache(int64 cache_id) { 815 if (!LazyOpen(false)) 816 return false; 817 818 const char* kSql = 819 "DELETE FROM OnlineWhiteLists WHERE cache_id = ?"; 820 821 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 822 statement.BindInt64(0, cache_id); 823 824 return statement.Run(); 825 } 826 827 bool AppCacheDatabase::GetDeletableResponseIds( 828 std::vector<int64>* response_ids, int64 max_rowid, int limit) { 829 if (!LazyOpen(false)) 830 return false; 831 832 const char* kSql = 833 "SELECT response_id FROM DeletableResponseIds " 834 " WHERE rowid <= ?" 835 " LIMIT ?"; 836 837 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 838 statement.BindInt64(0, max_rowid); 839 statement.BindInt64(1, limit); 840 841 while (statement.Step()) 842 response_ids->push_back(statement.ColumnInt64(0)); 843 return statement.Succeeded(); 844 } 845 846 bool AppCacheDatabase::InsertDeletableResponseIds( 847 const std::vector<int64>& response_ids) { 848 const char* kSql = 849 "INSERT INTO DeletableResponseIds (response_id) VALUES (?)"; 850 return RunCachedStatementWithIds(SQL_FROM_HERE, kSql, response_ids); 851 } 852 853 bool AppCacheDatabase::DeleteDeletableResponseIds( 854 const std::vector<int64>& response_ids) { 855 const char* kSql = 856 "DELETE FROM DeletableResponseIds WHERE response_id = ?"; 857 return RunCachedStatementWithIds(SQL_FROM_HERE, kSql, response_ids); 858 } 859 860 bool AppCacheDatabase::RunCachedStatementWithIds( 861 const sql::StatementID& statement_id, const char* sql, 862 const std::vector<int64>& ids) { 863 DCHECK(sql); 864 if (!LazyOpen(true)) 865 return false; 866 867 sql::Transaction transaction(db_.get()); 868 if (!transaction.Begin()) 869 return false; 870 871 sql::Statement statement(db_->GetCachedStatement(statement_id, sql)); 872 873 std::vector<int64>::const_iterator iter = ids.begin(); 874 while (iter != ids.end()) { 875 statement.BindInt64(0, *iter); 876 if (!statement.Run()) 877 return false; 878 statement.Reset(true); 879 ++iter; 880 } 881 882 return transaction.Commit(); 883 } 884 885 bool AppCacheDatabase::RunUniqueStatementWithInt64Result( 886 const char* sql, int64* result) { 887 DCHECK(sql); 888 sql::Statement statement(db_->GetUniqueStatement(sql)); 889 if (!statement.Step()) { 890 return false; 891 } 892 *result = statement.ColumnInt64(0); 893 return true; 894 } 895 896 bool AppCacheDatabase::FindResponseIdsForCacheHelper( 897 int64 cache_id, std::vector<int64>* ids_vector, 898 std::set<int64>* ids_set) { 899 DCHECK(ids_vector || ids_set); 900 DCHECK(!(ids_vector && ids_set)); 901 if (!LazyOpen(false)) 902 return false; 903 904 const char* kSql = 905 "SELECT response_id FROM Entries WHERE cache_id = ?"; 906 907 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql)); 908 909 statement.BindInt64(0, cache_id); 910 while (statement.Step()) { 911 int64 id = statement.ColumnInt64(0); 912 if (ids_set) 913 ids_set->insert(id); 914 else 915 ids_vector->push_back(id); 916 } 917 918 return statement.Succeeded(); 919 } 920 921 void AppCacheDatabase::ReadGroupRecord( 922 const sql::Statement& statement, GroupRecord* record) { 923 record->group_id = statement.ColumnInt64(0); 924 record->origin = GURL(statement.ColumnString(1)); 925 record->manifest_url = GURL(statement.ColumnString(2)); 926 record->creation_time = 927 base::Time::FromInternalValue(statement.ColumnInt64(3)); 928 record->last_access_time = 929 base::Time::FromInternalValue(statement.ColumnInt64(4)); 930 } 931 932 void AppCacheDatabase::ReadCacheRecord( 933 const sql::Statement& statement, CacheRecord* record) { 934 record->cache_id = statement.ColumnInt64(0); 935 record->group_id = statement.ColumnInt64(1); 936 record->online_wildcard = statement.ColumnBool(2); 937 record->update_time = 938 base::Time::FromInternalValue(statement.ColumnInt64(3)); 939 record->cache_size = statement.ColumnInt64(4); 940 } 941 942 void AppCacheDatabase::ReadEntryRecord( 943 const sql::Statement& statement, EntryRecord* record) { 944 record->cache_id = statement.ColumnInt64(0); 945 record->url = GURL(statement.ColumnString(1)); 946 record->flags = statement.ColumnInt(2); 947 record->response_id = statement.ColumnInt64(3); 948 record->response_size = statement.ColumnInt64(4); 949 } 950 951 void AppCacheDatabase::ReadNamespaceRecords( 952 sql::Statement* statement, 953 NamespaceRecordVector* intercepts, 954 NamespaceRecordVector* fallbacks) { 955 while (statement->Step()) { 956 NamespaceType type = static_cast<NamespaceType>(statement->ColumnInt(2)); 957 NamespaceRecordVector* records = 958 (type == FALLBACK_NAMESPACE) ? fallbacks : intercepts; 959 records->push_back(NamespaceRecord()); 960 ReadNamespaceRecord(statement, &records->back()); 961 } 962 } 963 964 void AppCacheDatabase::ReadNamespaceRecord( 965 const sql::Statement* statement, NamespaceRecord* record) { 966 record->cache_id = statement->ColumnInt64(0); 967 record->origin = GURL(statement->ColumnString(1)); 968 int type_with_executable_bit = statement->ColumnInt(2); 969 record->namespace_.namespace_url = GURL(statement->ColumnString(3)); 970 record->namespace_.target_url = GURL(statement->ColumnString(4)); 971 record->namespace_.is_pattern = statement->ColumnBool(5); 972 973 // Note: quick and dirty storage for the 'executable' bit w/o changing 974 // schemas, we use the high bit of 'type' field. 975 record->namespace_.type = static_cast<NamespaceType> 976 (type_with_executable_bit & 0x7ffffff); 977 record->namespace_.is_executable = 978 (type_with_executable_bit & 0x80000000) != 0; 979 DCHECK(!record->namespace_.is_executable || 980 CommandLine::ForCurrentProcess()->HasSwitch(kEnableExecutableHandlers)); 981 } 982 983 void AppCacheDatabase::ReadOnlineWhiteListRecord( 984 const sql::Statement& statement, OnlineWhiteListRecord* record) { 985 record->cache_id = statement.ColumnInt64(0); 986 record->namespace_url = GURL(statement.ColumnString(1)); 987 record->is_pattern = statement.ColumnBool(2); 988 } 989 990 bool AppCacheDatabase::LazyOpen(bool create_if_needed) { 991 if (db_) 992 return true; 993 994 // If we tried and failed once, don't try again in the same session 995 // to avoid creating an incoherent mess on disk. 996 if (is_disabled_) 997 return false; 998 999 // Avoid creating a database at all if we can. 1000 bool use_in_memory_db = db_file_path_.empty(); 1001 if (!create_if_needed && 1002 (use_in_memory_db || !base::PathExists(db_file_path_))) { 1003 return false; 1004 } 1005 1006 db_.reset(new sql::Connection); 1007 meta_table_.reset(new sql::MetaTable); 1008 1009 db_->set_histogram_tag("AppCache"); 1010 1011 bool opened = false; 1012 if (use_in_memory_db) { 1013 opened = db_->OpenInMemory(); 1014 } else if (!base::CreateDirectory(db_file_path_.DirName())) { 1015 LOG(ERROR) << "Failed to create appcache directory."; 1016 } else { 1017 opened = db_->Open(db_file_path_); 1018 if (opened) 1019 db_->Preload(); 1020 } 1021 1022 if (!opened || !db_->QuickIntegrityCheck() || !EnsureDatabaseVersion()) { 1023 LOG(ERROR) << "Failed to open the appcache database."; 1024 AppCacheHistograms::CountInitResult( 1025 AppCacheHistograms::SQL_DATABASE_ERROR); 1026 1027 // We're unable to open the database. This is a fatal error 1028 // which we can't recover from. We try to handle it by deleting 1029 // the existing appcache data and starting with a clean slate in 1030 // this browser session. 1031 if (!use_in_memory_db && DeleteExistingAndCreateNewDatabase()) 1032 return true; 1033 1034 Disable(); 1035 return false; 1036 } 1037 1038 AppCacheHistograms::CountInitResult(AppCacheHistograms::INIT_OK); 1039 return true; 1040 } 1041 1042 bool AppCacheDatabase::EnsureDatabaseVersion() { 1043 if (!sql::MetaTable::DoesTableExist(db_.get())) 1044 return CreateSchema(); 1045 1046 if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion)) 1047 return false; 1048 1049 if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) { 1050 LOG(WARNING) << "AppCache database is too new."; 1051 return false; 1052 } 1053 1054 std::string stored_flags; 1055 meta_table_->GetValue(kExperimentFlagsKey, &stored_flags); 1056 if (stored_flags != GetActiveExperimentFlags()) 1057 return false; 1058 1059 if (meta_table_->GetVersionNumber() < kCurrentVersion) 1060 return UpgradeSchema(); 1061 1062 #ifndef NDEBUG 1063 DCHECK(sql::MetaTable::DoesTableExist(db_.get())); 1064 for (int i = 0; i < kTableCount; ++i) { 1065 DCHECK(db_->DoesTableExist(kTables[i].table_name)); 1066 } 1067 for (int i = 0; i < kIndexCount; ++i) { 1068 DCHECK(db_->DoesIndexExist(kIndexes[i].index_name)); 1069 } 1070 #endif 1071 1072 return true; 1073 } 1074 1075 bool AppCacheDatabase::CreateSchema() { 1076 sql::Transaction transaction(db_.get()); 1077 if (!transaction.Begin()) 1078 return false; 1079 1080 if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion)) 1081 return false; 1082 1083 if (!meta_table_->SetValue(kExperimentFlagsKey, 1084 GetActiveExperimentFlags())) { 1085 return false; 1086 } 1087 1088 for (int i = 0; i < kTableCount; ++i) { 1089 if (!CreateTable(db_.get(), kTables[i])) 1090 return false; 1091 } 1092 1093 for (int i = 0; i < kIndexCount; ++i) { 1094 if (!CreateIndex(db_.get(), kIndexes[i])) 1095 return false; 1096 } 1097 1098 return transaction.Commit(); 1099 } 1100 1101 bool AppCacheDatabase::UpgradeSchema() { 1102 #if defined(APPCACHE_USE_SIMPLE_CACHE) 1103 return DeleteExistingAndCreateNewDatabase(); 1104 #else 1105 if (meta_table_->GetVersionNumber() == 3) { 1106 // version 3 was pre 12/17/2011 1107 DCHECK_EQ(strcmp(kNamespacesTable, kTables[3].table_name), 0); 1108 DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[6].table_name), 0); 1109 DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[7].table_name), 0); 1110 DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[8].table_name), 0); 1111 1112 const TableInfo kNamespaceTable_v4 = { 1113 kNamespacesTable, 1114 "(cache_id INTEGER," 1115 " origin TEXT," // intentionally not normalized 1116 " type INTEGER," 1117 " namespace_url TEXT," 1118 " target_url TEXT)" 1119 }; 1120 1121 // Migrate from the old FallbackNameSpaces to the newer Namespaces table, 1122 // but without the is_pattern column added in v5. 1123 sql::Transaction transaction(db_.get()); 1124 if (!transaction.Begin() || 1125 !CreateTable(db_.get(), kNamespaceTable_v4)) { 1126 return false; 1127 } 1128 1129 // Move data from the old table to the new table, setting the 1130 // 'type' for all current records to the value for FALLBACK_NAMESPACE. 1131 DCHECK_EQ(0, static_cast<int>(FALLBACK_NAMESPACE)); 1132 if (!db_->Execute( 1133 "INSERT INTO Namespaces" 1134 " SELECT cache_id, origin, 0, namespace_url, fallback_entry_url" 1135 " FROM FallbackNameSpaces")) { 1136 return false; 1137 } 1138 1139 // Drop the old table, indexes on that table are also removed by this. 1140 if (!db_->Execute("DROP TABLE FallbackNameSpaces")) 1141 return false; 1142 1143 // Create new indexes. 1144 if (!CreateIndex(db_.get(), kIndexes[6]) || 1145 !CreateIndex(db_.get(), kIndexes[7]) || 1146 !CreateIndex(db_.get(), kIndexes[8])) { 1147 return false; 1148 } 1149 1150 meta_table_->SetVersionNumber(4); 1151 meta_table_->SetCompatibleVersionNumber(4); 1152 if (!transaction.Commit()) 1153 return false; 1154 } 1155 1156 if (meta_table_->GetVersionNumber() == 4) { 1157 // version 4 pre 3/30/2013 1158 // Add the is_pattern column to the Namespaces and OnlineWhitelists tables. 1159 DCHECK_EQ(strcmp(kNamespacesTable, "Namespaces"), 0); 1160 sql::Transaction transaction(db_.get()); 1161 if (!transaction.Begin()) 1162 return false; 1163 if (!db_->Execute( 1164 "ALTER TABLE Namespaces ADD COLUMN" 1165 " is_pattern INTEGER CHECK(is_pattern IN (0, 1))")) { 1166 return false; 1167 } 1168 if (!db_->Execute( 1169 "ALTER TABLE OnlineWhitelists ADD COLUMN" 1170 " is_pattern INTEGER CHECK(is_pattern IN (0, 1))")) { 1171 return false; 1172 } 1173 meta_table_->SetVersionNumber(5); 1174 meta_table_->SetCompatibleVersionNumber(5); 1175 return transaction.Commit(); 1176 } 1177 1178 // If there is no upgrade path for the version on disk to the current 1179 // version, nuke everything and start over. 1180 return DeleteExistingAndCreateNewDatabase(); 1181 #endif 1182 } 1183 1184 void AppCacheDatabase::ResetConnectionAndTables() { 1185 meta_table_.reset(); 1186 db_.reset(); 1187 } 1188 1189 bool AppCacheDatabase::DeleteExistingAndCreateNewDatabase() { 1190 DCHECK(!db_file_path_.empty()); 1191 DCHECK(base::PathExists(db_file_path_)); 1192 VLOG(1) << "Deleting existing appcache data and starting over."; 1193 1194 ResetConnectionAndTables(); 1195 1196 // This also deletes the disk cache data. 1197 base::FilePath directory = db_file_path_.DirName(); 1198 if (!base::DeleteFile(directory, true) || 1199 !base::CreateDirectory(directory)) { 1200 return false; 1201 } 1202 1203 // Make sure the steps above actually deleted things. 1204 if (base::PathExists(db_file_path_)) 1205 return false; 1206 1207 // So we can't go recursive. 1208 if (is_recreating_) 1209 return false; 1210 1211 base::AutoReset<bool> auto_reset(&is_recreating_, true); 1212 return LazyOpen(true); 1213 } 1214 1215 } // namespace appcache 1216