1 // Copyright (c) 2011 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 "chrome/browser/history/thumbnail_database.h" 6 7 #include <algorithm> 8 #include <string> 9 10 #include "app/sql/statement.h" 11 #include "app/sql/transaction.h" 12 #include "base/command_line.h" 13 #include "base/file_util.h" 14 #include "base/memory/ref_counted_memory.h" 15 #include "base/time.h" 16 #include "base/string_util.h" 17 #include "base/utf_string_conversions.h" 18 #include "chrome/browser/diagnostics/sqlite_diagnostics.h" 19 #include "chrome/browser/history/history_publisher.h" 20 #include "chrome/browser/history/top_sites.h" 21 #include "chrome/browser/history/url_database.h" 22 #include "chrome/common/thumbnail_score.h" 23 #include "third_party/skia/include/core/SkBitmap.h" 24 #include "ui/gfx/codec/jpeg_codec.h" 25 26 #if defined(OS_MACOSX) 27 #include "base/mac/mac_util.h" 28 #endif 29 30 static void FillIconMapping(const sql::Statement& statement, 31 const GURL& page_url, 32 history::IconMapping* icon_mapping) { 33 icon_mapping->mapping_id = statement.ColumnInt64(0); 34 icon_mapping->icon_id = statement.ColumnInt64(1); 35 icon_mapping->icon_type = 36 static_cast<history::IconType>(statement.ColumnInt(2)); 37 icon_mapping->page_url = page_url; 38 } 39 40 namespace history { 41 42 // Version number of the database. 43 static const int kCurrentVersionNumber = 4; 44 static const int kCompatibleVersionNumber = 4; 45 46 ThumbnailDatabase::ThumbnailDatabase() 47 : history_publisher_(NULL), 48 use_top_sites_(false) { 49 } 50 51 ThumbnailDatabase::~ThumbnailDatabase() { 52 // The DBCloseScoper will delete the DB and the cache. 53 } 54 55 sql::InitStatus ThumbnailDatabase::Init( 56 const FilePath& db_name, 57 const HistoryPublisher* history_publisher, 58 URLDatabase* url_db) { 59 history_publisher_ = history_publisher; 60 sql::InitStatus status = OpenDatabase(&db_, db_name); 61 if (status != sql::INIT_OK) 62 return status; 63 64 // Scope initialization in a transaction so we can't be partially initialized. 65 sql::Transaction transaction(&db_); 66 transaction.Begin(); 67 68 #if defined(OS_MACOSX) 69 // Exclude the thumbnails file and its journal from backups. 70 base::mac::SetFileBackupExclusion(db_name, true); 71 FilePath::StringType db_name_string(db_name.value()); 72 db_name_string += "-journal"; 73 FilePath db_journal_name(db_name_string); 74 base::mac::SetFileBackupExclusion(db_journal_name, true); 75 #endif 76 77 // Create the tables. 78 if (!meta_table_.Init(&db_, kCurrentVersionNumber, 79 kCompatibleVersionNumber) || 80 !InitThumbnailTable() || 81 !InitFaviconsTable(&db_, false) || 82 !InitIconMappingTable(&db_, false)) { 83 db_.Close(); 84 return sql::INIT_FAILURE; 85 } 86 InitFaviconsIndex(); 87 InitIconMappingIndex(); 88 89 // Version check. We should not encounter a database too old for us to handle 90 // in the wild, so we try to continue in that case. 91 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { 92 LOG(WARNING) << "Thumbnail database is too new."; 93 return sql::INIT_TOO_NEW; 94 } 95 96 int cur_version = meta_table_.GetVersionNumber(); 97 if (cur_version == 2) { 98 if (!UpgradeToVersion3()) { 99 LOG(WARNING) << "Unable to update to thumbnail database to version 3."; 100 db_.Close(); 101 return sql::INIT_FAILURE; 102 } 103 ++cur_version; 104 } 105 106 if (cur_version == 3) { 107 if (!UpgradeToVersion4() || !MigrateIconMappingData(url_db)) { 108 LOG(WARNING) << "Unable to update to thumbnail database to version 4."; 109 db_.Close(); 110 return sql::INIT_FAILURE; 111 } 112 ++cur_version; 113 } 114 115 LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << 116 "Thumbnail database version " << cur_version << " is too old to handle."; 117 118 // Initialization is complete. 119 if (!transaction.Commit()) { 120 db_.Close(); 121 return sql::INIT_FAILURE; 122 } 123 124 return sql::INIT_OK; 125 } 126 127 sql::InitStatus ThumbnailDatabase::OpenDatabase(sql::Connection* db, 128 const FilePath& db_name) { 129 // Set the exceptional sqlite error handler. 130 db->set_error_delegate(GetErrorHandlerForThumbnailDb()); 131 132 // Thumbnails db now only stores favicons, so we don't need that big a page 133 // size or cache. 134 db->set_page_size(2048); 135 db->set_cache_size(32); 136 137 // Run the database in exclusive mode. Nobody else should be accessing the 138 // database while we're running, and this will give somewhat improved perf. 139 db->set_exclusive_locking(); 140 141 if (!db->Open(db_name)) 142 return sql::INIT_FAILURE; 143 144 return sql::INIT_OK; 145 } 146 147 bool ThumbnailDatabase::InitThumbnailTable() { 148 if (!db_.DoesTableExist("thumbnails")) { 149 use_top_sites_ = true; 150 } 151 return true; 152 } 153 154 bool ThumbnailDatabase::UpgradeToVersion3() { 155 if (use_top_sites_) { 156 meta_table_.SetVersionNumber(3); 157 meta_table_.SetCompatibleVersionNumber( 158 std::min(3, kCompatibleVersionNumber)); 159 return true; // Not needed after migration to TopSites. 160 } 161 162 // sqlite doesn't like the "ALTER TABLE xxx ADD (column_one, two, 163 // three)" syntax, so list out the commands we need to execute: 164 const char* alterations[] = { 165 "ALTER TABLE thumbnails ADD boring_score DOUBLE DEFAULT 1.0", 166 "ALTER TABLE thumbnails ADD good_clipping INTEGER DEFAULT 0", 167 "ALTER TABLE thumbnails ADD at_top INTEGER DEFAULT 0", 168 "ALTER TABLE thumbnails ADD last_updated INTEGER DEFAULT 0", 169 NULL 170 }; 171 172 for (int i = 0; alterations[i] != NULL; ++i) { 173 if (!db_.Execute(alterations[i])) { 174 NOTREACHED(); 175 return false; 176 } 177 } 178 179 meta_table_.SetVersionNumber(3); 180 meta_table_.SetCompatibleVersionNumber(std::min(3, kCompatibleVersionNumber)); 181 return true; 182 } 183 184 bool ThumbnailDatabase::RecreateThumbnailTable() { 185 if (use_top_sites_) 186 return true; // Not needed after migration to TopSites. 187 188 if (!db_.Execute("DROP TABLE thumbnails")) 189 return false; 190 return InitThumbnailTable(); 191 } 192 193 bool ThumbnailDatabase::InitFaviconsTable(sql::Connection* db, 194 bool is_temporary) { 195 // Note: if you update the schema, don't forget to update 196 // CopyToTemporaryFaviconTable as well. 197 const char* name = is_temporary ? "temp_favicons" : "favicons"; 198 if (!db->DoesTableExist(name)) { 199 std::string sql; 200 sql.append("CREATE TABLE "); 201 sql.append(name); 202 sql.append("(" 203 "id INTEGER PRIMARY KEY," 204 "url LONGVARCHAR NOT NULL," 205 "last_updated INTEGER DEFAULT 0," 206 "image_data BLOB," 207 "icon_type INTEGER DEFAULT 1)"); // Set the default as FAVICON 208 // to be consistent with table 209 // upgrade in 210 // UpgradeToVersion4(). 211 if (!db->Execute(sql.c_str())) 212 return false; 213 } 214 return true; 215 } 216 217 void ThumbnailDatabase::InitFaviconsIndex() { 218 // Add an index on the url column. We ignore errors. Since this is always 219 // called during startup, the index will normally already exist. 220 db_.Execute("CREATE INDEX favicons_url ON favicons(url)"); 221 } 222 223 void ThumbnailDatabase::BeginTransaction() { 224 db_.BeginTransaction(); 225 } 226 227 void ThumbnailDatabase::CommitTransaction() { 228 db_.CommitTransaction(); 229 } 230 231 void ThumbnailDatabase::Vacuum() { 232 DCHECK(db_.transaction_nesting() == 0) << 233 "Can not have a transaction when vacuuming."; 234 db_.Execute("VACUUM"); 235 } 236 237 void ThumbnailDatabase::SetPageThumbnail( 238 const GURL& url, 239 URLID id, 240 const SkBitmap& thumbnail, 241 const ThumbnailScore& score, 242 base::Time time) { 243 if (use_top_sites_) { 244 LOG(WARNING) << "Use TopSites instead."; 245 return; // Not possible after migration to TopSites. 246 } 247 248 if (!thumbnail.isNull()) { 249 bool add_thumbnail = true; 250 ThumbnailScore current_score; 251 if (ThumbnailScoreForId(id, ¤t_score)) { 252 add_thumbnail = ShouldReplaceThumbnailWith(current_score, score); 253 } 254 255 if (add_thumbnail) { 256 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 257 "INSERT OR REPLACE INTO thumbnails " 258 "(url_id, boring_score, good_clipping, at_top, last_updated, data) " 259 "VALUES (?,?,?,?,?,?)")); 260 if (!statement) 261 return; 262 263 // We use 90 quality (out of 100) which is pretty high, because 264 // we're very sensitive to artifacts for these small sized, 265 // highly detailed images. 266 std::vector<unsigned char> jpeg_data; 267 SkAutoLockPixels thumbnail_lock(thumbnail); 268 bool encoded = gfx::JPEGCodec::Encode( 269 reinterpret_cast<unsigned char*>(thumbnail.getAddr32(0, 0)), 270 gfx::JPEGCodec::FORMAT_SkBitmap, thumbnail.width(), 271 thumbnail.height(), 272 static_cast<int>(thumbnail.rowBytes()), 90, 273 &jpeg_data); 274 275 if (encoded) { 276 statement.BindInt64(0, id); 277 statement.BindDouble(1, score.boring_score); 278 statement.BindBool(2, score.good_clipping); 279 statement.BindBool(3, score.at_top); 280 statement.BindInt64(4, score.time_at_snapshot.ToTimeT()); 281 statement.BindBlob(5, &jpeg_data[0], 282 static_cast<int>(jpeg_data.size())); 283 if (!statement.Run()) 284 NOTREACHED() << db_.GetErrorMessage(); 285 } 286 287 // Publish the thumbnail to any indexers listening to us. 288 // The tests may send an invalid url. Hence avoid publishing those. 289 if (url.is_valid() && history_publisher_ != NULL) 290 history_publisher_->PublishPageThumbnail(jpeg_data, url, time); 291 } 292 } else { 293 if (!DeleteThumbnail(id) ) 294 DLOG(WARNING) << "Unable to delete thumbnail"; 295 } 296 } 297 298 bool ThumbnailDatabase::GetPageThumbnail(URLID id, 299 std::vector<unsigned char>* data) { 300 if (use_top_sites_) { 301 LOG(WARNING) << "Use TopSites instead."; 302 return false; // Not possible after migration to TopSites. 303 } 304 305 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 306 "SELECT data FROM thumbnails WHERE url_id=?")); 307 if (!statement) 308 return false; 309 310 statement.BindInt64(0, id); 311 if (!statement.Step()) 312 return false; // don't have a thumbnail for this ID 313 314 statement.ColumnBlobAsVector(0, data); 315 return true; 316 } 317 318 bool ThumbnailDatabase::DeleteThumbnail(URLID id) { 319 if (use_top_sites_) { 320 return true; // Not possible after migration to TopSites. 321 } 322 323 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 324 "DELETE FROM thumbnails WHERE url_id = ?")); 325 if (!statement) 326 return false; 327 328 statement.BindInt64(0, id); 329 return statement.Run(); 330 } 331 332 bool ThumbnailDatabase::ThumbnailScoreForId(URLID id, 333 ThumbnailScore* score) { 334 if (use_top_sites_) { 335 LOG(WARNING) << "Use TopSites instead."; 336 return false; // Not possible after migration to TopSites. 337 } 338 339 // Fetch the current thumbnail's information to make sure we 340 // aren't replacing a good thumbnail with one that's worse. 341 sql::Statement select_statement(db_.GetCachedStatement(SQL_FROM_HERE, 342 "SELECT boring_score, good_clipping, at_top, last_updated " 343 "FROM thumbnails WHERE url_id=?")); 344 if (!select_statement) { 345 NOTREACHED() << "Couldn't build select statement!"; 346 } else { 347 select_statement.BindInt64(0, id); 348 if (select_statement.Step()) { 349 double current_boring_score = select_statement.ColumnDouble(0); 350 bool current_clipping = select_statement.ColumnBool(1); 351 bool current_at_top = select_statement.ColumnBool(2); 352 base::Time last_updated = 353 base::Time::FromTimeT(select_statement.ColumnInt64(3)); 354 *score = ThumbnailScore(current_boring_score, current_clipping, 355 current_at_top, last_updated); 356 return true; 357 } 358 } 359 360 return false; 361 } 362 363 bool ThumbnailDatabase::SetFavicon(URLID icon_id, 364 scoped_refptr<RefCountedMemory> icon_data, 365 base::Time time) { 366 DCHECK(icon_id); 367 if (icon_data->size()) { 368 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 369 "UPDATE favicons SET image_data=?, last_updated=? WHERE id=?")); 370 if (!statement) 371 return 0; 372 373 statement.BindBlob(0, icon_data->front(), 374 static_cast<int>(icon_data->size())); 375 statement.BindInt64(1, time.ToTimeT()); 376 statement.BindInt64(2, icon_id); 377 return statement.Run(); 378 } else { 379 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 380 "UPDATE favicons SET image_data=NULL, last_updated=? WHERE id=?")); 381 if (!statement) 382 return 0; 383 384 statement.BindInt64(0, time.ToTimeT()); 385 statement.BindInt64(1, icon_id); 386 return statement.Run(); 387 } 388 } 389 390 bool ThumbnailDatabase::SetFaviconLastUpdateTime(FaviconID icon_id, 391 base::Time time) { 392 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 393 "UPDATE favicons SET last_updated=? WHERE id=?")); 394 if (!statement) 395 return 0; 396 397 statement.BindInt64(0, time.ToTimeT()); 398 statement.BindInt64(1, icon_id); 399 return statement.Run(); 400 } 401 402 FaviconID ThumbnailDatabase::GetFaviconIDForFaviconURL(const GURL& icon_url, 403 int required_icon_type, 404 IconType* icon_type) { 405 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 406 "SELECT id, icon_type FROM favicons WHERE url=? AND (icon_type & ? > 0) " 407 "ORDER BY icon_type DESC")); 408 if (!statement) 409 return 0; 410 411 statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url)); 412 statement.BindInt(1, required_icon_type); 413 if (!statement.Step()) 414 return 0; // not cached 415 416 if (icon_type) 417 *icon_type = static_cast<IconType>(statement.ColumnInt(1)); 418 return statement.ColumnInt64(0); 419 } 420 421 bool ThumbnailDatabase::GetFavicon( 422 FaviconID icon_id, 423 base::Time* last_updated, 424 std::vector<unsigned char>* png_icon_data, 425 GURL* icon_url) { 426 DCHECK(icon_id); 427 428 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 429 "SELECT last_updated, image_data, url FROM favicons WHERE id=?")); 430 if (!statement) 431 return 0; 432 433 statement.BindInt64(0, icon_id); 434 435 if (!statement.Step()) 436 return false; // No entry for the id. 437 438 *last_updated = base::Time::FromTimeT(statement.ColumnInt64(0)); 439 if (statement.ColumnByteLength(1) > 0) 440 statement.ColumnBlobAsVector(1, png_icon_data); 441 if (icon_url) 442 *icon_url = GURL(statement.ColumnString(2)); 443 444 return true; 445 } 446 447 FaviconID ThumbnailDatabase::AddFavicon(const GURL& icon_url, 448 IconType icon_type) { 449 450 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 451 "INSERT INTO favicons (url, icon_type) VALUES (?, ?)")); 452 if (!statement) 453 return 0; 454 455 statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url)); 456 statement.BindInt(1, icon_type); 457 if (!statement.Run()) 458 return 0; 459 return db_.GetLastInsertRowId(); 460 } 461 462 bool ThumbnailDatabase::DeleteFavicon(FaviconID id) { 463 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 464 "DELETE FROM favicons WHERE id = ?")); 465 if (!statement) 466 return false; 467 468 statement.BindInt64(0, id); 469 return statement.Run(); 470 } 471 472 bool ThumbnailDatabase::GetIconMappingForPageURL(const GURL& page_url, 473 IconType required_icon_type, 474 IconMapping* icon_mapping) { 475 std::vector<IconMapping> icon_mappings; 476 if (!GetIconMappingsForPageURL(page_url, &icon_mappings)) 477 return false; 478 479 for (std::vector<IconMapping>::iterator m = icon_mappings.begin(); 480 m != icon_mappings.end(); ++m) { 481 if (m->icon_type == required_icon_type) { 482 if (icon_mapping != NULL) 483 *icon_mapping = *m; 484 return true; 485 } 486 } 487 488 return false; 489 } 490 491 bool ThumbnailDatabase::GetIconMappingsForPageURL( 492 const GURL& page_url, 493 std::vector<IconMapping>* mapping_data) { 494 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 495 "SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type " 496 "FROM icon_mapping " 497 "INNER JOIN favicons " 498 "ON icon_mapping.icon_id = favicons.id " 499 "WHERE icon_mapping.page_url=? " 500 "ORDER BY favicons.icon_type DESC")); 501 if (!statement) 502 return false; 503 504 statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url)); 505 506 bool result = false; 507 while (statement.Step()) { 508 result = true; 509 if (!mapping_data) 510 return result; 511 512 IconMapping icon_mapping; 513 FillIconMapping(statement, page_url, &icon_mapping); 514 mapping_data->push_back(icon_mapping); 515 } 516 return result; 517 } 518 519 IconMappingID ThumbnailDatabase::AddIconMapping(const GURL& page_url, 520 FaviconID icon_id) { 521 return AddIconMapping(page_url, icon_id, false); 522 } 523 524 bool ThumbnailDatabase::UpdateIconMapping(IconMappingID mapping_id, 525 FaviconID icon_id) { 526 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 527 "UPDATE icon_mapping SET icon_id=? WHERE id=?")); 528 if (!statement) 529 return 0; 530 531 statement.BindInt64(0, icon_id); 532 statement.BindInt64(1, mapping_id); 533 return statement.Run(); 534 } 535 536 bool ThumbnailDatabase::DeleteIconMappings(const GURL& page_url) { 537 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 538 "DELETE FROM icon_mapping WHERE page_url = ?")); 539 if (!statement) 540 return false; 541 542 statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url)); 543 return statement.Run(); 544 } 545 546 bool ThumbnailDatabase::HasMappingFor(FaviconID id) { 547 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 548 "SELECT id FROM icon_mapping " 549 "WHERE icon_id=?")); 550 if (!statement) 551 return false; 552 553 statement.BindInt64(0, id); 554 return statement.Step(); 555 } 556 557 bool ThumbnailDatabase::MigrateIconMappingData(URLDatabase* url_db) { 558 URLDatabase::IconMappingEnumerator e; 559 if (!url_db->InitIconMappingEnumeratorForEverything(&e)) 560 return false; 561 562 IconMapping info; 563 while (e.GetNextIconMapping(&info)) { 564 // TODO: Using bulk insert to improve the performance. 565 if (!AddIconMapping(info.page_url, info.icon_id)) 566 return false; 567 } 568 return true; 569 } 570 571 IconMappingID ThumbnailDatabase::AddToTemporaryIconMappingTable( 572 const GURL& page_url, const FaviconID icon_id) { 573 return AddIconMapping(page_url, icon_id, true); 574 } 575 576 bool ThumbnailDatabase::CommitTemporaryIconMappingTable() { 577 // Delete the old icon_mapping table. 578 if (!db_.Execute("DROP TABLE icon_mapping")) 579 return false; 580 581 // Rename the temporary one. 582 if (!db_.Execute("ALTER TABLE temp_icon_mapping RENAME TO icon_mapping")) 583 return false; 584 585 // The renamed table needs the index (the temporary table doesn't have one). 586 InitIconMappingIndex(); 587 588 return true; 589 } 590 591 FaviconID ThumbnailDatabase::CopyToTemporaryFaviconTable(FaviconID source) { 592 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 593 "INSERT INTO temp_favicons (url, last_updated, image_data, icon_type)" 594 "SELECT url, last_updated, image_data, icon_type " 595 "FROM favicons WHERE id = ?")); 596 if (!statement) 597 return 0; 598 statement.BindInt64(0, source); 599 if (!statement.Run()) 600 return 0; 601 602 // We return the ID of the newly inserted favicon. 603 return db_.GetLastInsertRowId(); 604 } 605 606 bool ThumbnailDatabase::CommitTemporaryFaviconTable() { 607 // Delete the old favicons table. 608 if (!db_.Execute("DROP TABLE favicons")) 609 return false; 610 611 // Rename the temporary one. 612 if (!db_.Execute("ALTER TABLE temp_favicons RENAME TO favicons")) 613 return false; 614 615 // The renamed table needs the index (the temporary table doesn't have one). 616 InitFaviconsIndex(); 617 return true; 618 } 619 620 bool ThumbnailDatabase::NeedsMigrationToTopSites() { 621 return !use_top_sites_; 622 } 623 624 bool ThumbnailDatabase::RenameAndDropThumbnails(const FilePath& old_db_file, 625 const FilePath& new_db_file) { 626 // Init favicons table - same schema as the thumbnails. 627 sql::Connection favicons; 628 if (OpenDatabase(&favicons, new_db_file) != sql::INIT_OK) 629 return false; 630 631 if (!InitFaviconsTable(&favicons, false) || 632 !InitIconMappingTable(&favicons, false)) { 633 NOTREACHED() << "Couldn't init favicons and icon-mapping table."; 634 favicons.Close(); 635 return false; 636 } 637 favicons.Close(); 638 639 // Can't attach within a transaction. 640 if (transaction_nesting()) 641 CommitTransaction(); 642 643 // Attach new DB. 644 { 645 // This block is needed because otherwise the attach statement is 646 // never cleared from cache and we can't close the DB :P 647 sql::Statement attach(db_.GetUniqueStatement("ATTACH ? AS new_favicons")); 648 if (!attach) { 649 NOTREACHED() << "Unable to attach database."; 650 // Keep the transaction open, even though we failed. 651 BeginTransaction(); 652 return false; 653 } 654 655 #if defined(OS_POSIX) 656 attach.BindString(0, new_db_file.value()); 657 #else 658 attach.BindString(0, WideToUTF8(new_db_file.value())); 659 #endif 660 661 if (!attach.Run()) { 662 NOTREACHED() << db_.GetErrorMessage(); 663 BeginTransaction(); 664 return false; 665 } 666 } 667 668 // Move favicons to the new DB. 669 if (!db_.Execute("INSERT OR REPLACE INTO new_favicons.favicons " 670 "SELECT * FROM favicons")) { 671 NOTREACHED() << "Unable to copy favicons."; 672 BeginTransaction(); 673 return false; 674 } 675 676 if (!db_.Execute("DETACH new_favicons")) { 677 NOTREACHED() << "Unable to detach database."; 678 BeginTransaction(); 679 return false; 680 } 681 682 db_.Close(); 683 684 // Reset the DB to point to new file. 685 if (OpenDatabase(&db_, new_db_file) != sql::INIT_OK) 686 return false; 687 688 file_util::Delete(old_db_file, false); 689 690 InitFaviconsIndex(); 691 692 // Reopen the transaction. 693 BeginTransaction(); 694 use_top_sites_ = true; 695 return true; 696 } 697 698 bool ThumbnailDatabase::InitIconMappingTable(sql::Connection* db, 699 bool is_temporary) { 700 const char* name = is_temporary ? "temp_icon_mapping" : "icon_mapping"; 701 if (!db->DoesTableExist(name)) { 702 std::string sql; 703 sql.append("CREATE TABLE "); 704 sql.append(name); 705 sql.append("(" 706 "id INTEGER PRIMARY KEY," 707 "page_url LONGVARCHAR NOT NULL," 708 "icon_id INTEGER)"); 709 if (!db->Execute(sql.c_str())) 710 return false; 711 } 712 return true; 713 } 714 715 void ThumbnailDatabase::InitIconMappingIndex() { 716 // Add an index on the url column. We ignore errors. Since this is always 717 // called during startup, the index will normally already exist. 718 db_.Execute("CREATE INDEX icon_mapping_page_url_idx" 719 " ON icon_mapping(page_url)"); 720 db_.Execute("CREATE INDEX icon_mapping_icon_id_idx ON icon_mapping(icon_id)"); 721 } 722 723 IconMappingID ThumbnailDatabase::AddIconMapping(const GURL& page_url, 724 FaviconID icon_id, 725 bool is_temporary) { 726 const char* name = is_temporary ? "temp_icon_mapping" : "icon_mapping"; 727 const char* statement_name = 728 is_temporary ? "add_temp_icon_mapping" : "add_icon_mapping"; 729 730 std::string sql; 731 sql.append("INSERT INTO "); 732 sql.append(name); 733 sql.append("(page_url, icon_id) VALUES (?, ?)"); 734 735 sql::Statement statement( 736 db_.GetCachedStatement(sql::StatementID(statement_name), sql.c_str())); 737 if (!statement) 738 return 0; 739 740 statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url)); 741 statement.BindInt64(1, icon_id); 742 743 if (!statement.Run()) 744 return 0; 745 746 return db_.GetLastInsertRowId(); 747 } 748 749 bool ThumbnailDatabase::UpgradeToVersion4() { 750 // Set the default icon type as favicon, so the current data are set 751 // correctly. 752 if (!db_.Execute("ALTER TABLE favicons ADD icon_type INTEGER DEFAULT 1")) { 753 NOTREACHED(); 754 return false; 755 } 756 meta_table_.SetVersionNumber(4); 757 meta_table_.SetCompatibleVersionNumber(std::min(4, kCompatibleVersionNumber)); 758 return true; 759 } 760 761 } // namespace history 762