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