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 "chrome/browser/history/thumbnail_database.h" 6 7 #include <algorithm> 8 #include <string> 9 10 #include "base/command_line.h" 11 #include "base/debug/alias.h" 12 #include "base/file_util.h" 13 #include "base/format_macros.h" 14 #include "base/memory/ref_counted_memory.h" 15 #include "base/metrics/histogram.h" 16 #include "base/rand_util.h" 17 #include "base/strings/string_number_conversions.h" 18 #include "base/strings/string_tokenizer.h" 19 #include "base/strings/string_util.h" 20 #include "base/strings/stringprintf.h" 21 #include "base/strings/utf_string_conversions.h" 22 #include "base/time/time.h" 23 #include "chrome/browser/history/history_publisher.h" 24 #include "chrome/browser/history/top_sites.h" 25 #include "chrome/browser/history/url_database.h" 26 #include "chrome/common/chrome_version_info.h" 27 #include "chrome/common/dump_without_crashing.h" 28 #include "chrome/common/thumbnail_score.h" 29 #include "sql/statement.h" 30 #include "sql/transaction.h" 31 #include "third_party/sqlite/sqlite3.h" 32 #include "ui/gfx/image/image_util.h" 33 34 #if defined(OS_MACOSX) 35 #include "base/mac/mac_util.h" 36 #endif 37 38 // Description of database tables: 39 // 40 // icon_mapping 41 // id Unique ID. 42 // page_url Page URL which has one or more associated favicons. 43 // icon_id The ID of favicon that this mapping maps to. 44 // 45 // favicons This table associates a row to each favicon for a 46 // |page_url| in the |icon_mapping| table. This is the 47 // default favicon |page_url|/favicon.ico plus any favicons 48 // associated via <link rel="icon_type" href="url">. 49 // The |id| matches the |icon_id| field in the appropriate 50 // row in the icon_mapping table. 51 // 52 // id Unique ID. 53 // url The URL at which the favicon file is located. 54 // icon_type The type of the favicon specified in the rel attribute of 55 // the link tag. The FAVICON type is used for the default 56 // favicon.ico favicon. 57 // 58 // favicon_bitmaps This table contains the PNG encoded bitmap data of the 59 // favicons. There is a separate row for every size in a 60 // multi resolution bitmap. The bitmap data is associated 61 // to the favicon via the |icon_id| field which matches 62 // the |id| field in the appropriate row in the |favicons| 63 // table. 64 // 65 // id Unique ID. 66 // icon_id The ID of the favicon that the bitmap is associated to. 67 // last_updated The time at which this favicon was inserted into the 68 // table. This is used to determine if it needs to be 69 // redownloaded from the web. 70 // image_data PNG encoded data of the favicon. 71 // width Pixel width of |image_data|. 72 // height Pixel height of |image_data|. 73 74 namespace { 75 76 void FillIconMapping(const sql::Statement& statement, 77 const GURL& page_url, 78 history::IconMapping* icon_mapping) { 79 icon_mapping->mapping_id = statement.ColumnInt64(0); 80 icon_mapping->icon_id = statement.ColumnInt64(1); 81 icon_mapping->icon_type = 82 static_cast<chrome::IconType>(statement.ColumnInt(2)); 83 icon_mapping->icon_url = GURL(statement.ColumnString(3)); 84 icon_mapping->page_url = page_url; 85 } 86 87 enum InvalidStructureType { 88 // NOTE(shess): Intentionally skip bucket 0 to account for 89 // conversion from a boolean histogram. 90 STRUCTURE_EVENT_FAVICON = 1, 91 STRUCTURE_EVENT_VERSION4, 92 STRUCTURE_EVENT_VERSION5, 93 94 // Always keep this at the end. 95 STRUCTURE_EVENT_MAX, 96 }; 97 98 void RecordInvalidStructure(InvalidStructureType invalid_type) { 99 UMA_HISTOGRAM_ENUMERATION("History.InvalidFaviconsDBStructure", 100 invalid_type, STRUCTURE_EVENT_MAX); 101 } 102 103 // Attempt to pass 2000 bytes of |debug_info| into a crash dump. 104 void DumpWithoutCrashing2000(const std::string& debug_info) { 105 char debug_buf[2000]; 106 base::strlcpy(debug_buf, debug_info.c_str(), arraysize(debug_buf)); 107 base::debug::Alias(&debug_buf); 108 109 logging::DumpWithoutCrashing(); 110 } 111 112 void ReportCorrupt(sql::Connection* db, size_t startup_kb) { 113 // Buffer for accumulating debugging info about the error. Place 114 // more-relevant information earlier, in case things overflow the 115 // fixed-size buffer. 116 std::string debug_info; 117 118 base::StringAppendF(&debug_info, "SQLITE_CORRUPT, integrity_check:\n"); 119 120 // Check files up to 8M to keep things from blocking too long. 121 const size_t kMaxIntegrityCheckSize = 8192; 122 if (startup_kb > kMaxIntegrityCheckSize) { 123 base::StringAppendF(&debug_info, "too big %" PRIuS "\n", startup_kb); 124 } else { 125 std::vector<std::string> messages; 126 127 const base::TimeTicks before = base::TimeTicks::Now(); 128 db->IntegrityCheck(&messages); 129 base::StringAppendF(&debug_info, "# %" PRIx64 " ms, %" PRIuS " records\n", 130 (base::TimeTicks::Now() - before).InMilliseconds(), 131 messages.size()); 132 133 // SQLite returns up to 100 messages by default, trim deeper to 134 // keep close to the 2000-character size limit for dumping. 135 // 136 // TODO(shess): If the first 20 tend to be actionable, test if 137 // passing the count to integrity_check makes it exit earlier. In 138 // that case it may be possible to greatly ease the size 139 // restriction. 140 const size_t kMaxMessages = 20; 141 for (size_t i = 0; i < kMaxMessages && i < messages.size(); ++i) { 142 base::StringAppendF(&debug_info, "%s\n", messages[i].c_str()); 143 } 144 } 145 146 DumpWithoutCrashing2000(debug_info); 147 } 148 149 void ReportError(sql::Connection* db, int error) { 150 // Buffer for accumulating debugging info about the error. Place 151 // more-relevant information earlier, in case things overflow the 152 // fixed-size buffer. 153 std::string debug_info; 154 155 // The error message from the failed operation. 156 base::StringAppendF(&debug_info, "db error: %d/%s\n", 157 db->GetErrorCode(), db->GetErrorMessage()); 158 159 // System errno information. 160 base::StringAppendF(&debug_info, "errno: %d\n", db->GetLastErrno()); 161 162 // SQLITE_ERROR reports seem to be attempts to upgrade invalid 163 // schema, try to log that info. 164 if (error == SQLITE_ERROR) { 165 const char* kVersionSql = "SELECT value FROM meta WHERE key = 'version'"; 166 if (db->IsSQLValid(kVersionSql)) { 167 sql::Statement statement(db->GetUniqueStatement(kVersionSql)); 168 if (statement.Step()) { 169 debug_info += "version: "; 170 debug_info += statement.ColumnString(0); 171 debug_info += '\n'; 172 } else if (statement.Succeeded()) { 173 debug_info += "version: none\n"; 174 } else { 175 debug_info += "version: error\n"; 176 } 177 } else { 178 debug_info += "version: invalid\n"; 179 } 180 181 debug_info += "schema:\n"; 182 183 // sqlite_master has columns: 184 // type - "index" or "table". 185 // name - name of created element. 186 // tbl_name - name of element, or target table in case of index. 187 // rootpage - root page of the element in database file. 188 // sql - SQL to create the element. 189 // In general, the |sql| column is sufficient to derive the other 190 // columns. |rootpage| is not interesting for debugging, without 191 // the contents of the database. The COALESCE is because certain 192 // automatic elements will have a |name| but no |sql|, 193 const char* kSchemaSql = "SELECT COALESCE(sql, name) FROM sqlite_master"; 194 sql::Statement statement(db->GetUniqueStatement(kSchemaSql)); 195 while (statement.Step()) { 196 debug_info += statement.ColumnString(0); 197 debug_info += '\n'; 198 } 199 if (!statement.Succeeded()) 200 debug_info += "error\n"; 201 } 202 203 // TODO(shess): Think of other things to log. Not logging the 204 // statement text because the backtrace should suffice in most 205 // cases. The database schema is a possibility, but the 206 // likelihood of recursive error callbacks makes that risky (same 207 // reasoning applies to other data fetched from the database). 208 209 DumpWithoutCrashing2000(debug_info); 210 } 211 212 // TODO(shess): If this proves out, perhaps lift the code out to 213 // chrome/browser/diagnostics/sqlite_diagnostics.{h,cc}. 214 void DatabaseErrorCallback(sql::Connection* db, 215 size_t startup_kb, 216 int error, 217 sql::Statement* stmt) { 218 // TODO(shess): Assert that this is running on a safe thread. 219 // AFAICT, should be the history thread, but at this level I can't 220 // see how to reach that. 221 222 // Infrequently report information about the error up to the crash 223 // server. 224 static const uint64 kReportsPerMillion = 50000; 225 226 // TODO(shess): For now, don't report on beta or stable so as not to 227 // overwhelm the crash server. Once the big fish are fried, 228 // consider reporting at a reduced rate on the bigger channels. 229 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 230 231 // Since some/most errors will not resolve themselves, only report 232 // once per Chrome run. 233 static bool reported = false; 234 235 if (channel != chrome::VersionInfo::CHANNEL_STABLE && 236 channel != chrome::VersionInfo::CHANNEL_BETA && 237 !reported) { 238 uint64 rand = base::RandGenerator(1000000); 239 if (error == SQLITE_CORRUPT) { 240 // Once the database is known to be corrupt, it will generate a 241 // stream of errors until someone fixes it, so give one chance. 242 // Set first in case of errors in generating the report. 243 reported = true; 244 245 // Corrupt cases currently dominate, report them very infrequently. 246 static const uint64 kCorruptReportsPerMillion = 10000; 247 if (rand < kCorruptReportsPerMillion) 248 ReportCorrupt(db, startup_kb); 249 } else if (error == SQLITE_READONLY) { 250 // SQLITE_READONLY appears similar to SQLITE_CORRUPT - once it 251 // is seen, it is almost guaranteed to be seen again. 252 reported = true; 253 254 if (rand < kReportsPerMillion) 255 ReportError(db, error); 256 } else { 257 // Only set the flag when making a report. This should allow 258 // later (potentially different) errors in a stream of errors to 259 // be reported. 260 // 261 // TODO(shess): Would it be worthwile to audit for which cases 262 // want once-only handling? Sqlite.Error.Thumbnail shows 263 // CORRUPT and READONLY as almost 95% of all reports on these 264 // channels, so probably easier to just harvest from the field. 265 if (rand < kReportsPerMillion) { 266 reported = true; 267 ReportError(db, error); 268 } 269 } 270 } 271 272 // The default handling is to assert on debug and to ignore on release. 273 DLOG(FATAL) << db->GetErrorMessage(); 274 } 275 276 } // namespace 277 278 namespace history { 279 280 // Version number of the database. 281 static const int kCurrentVersionNumber = 7; 282 static const int kCompatibleVersionNumber = 7; 283 284 // Use 90 quality (out of 100) which is pretty high, because we're very 285 // sensitive to artifacts for these small sized, highly detailed images. 286 static const int kImageQuality = 90; 287 288 ThumbnailDatabase::IconMappingEnumerator::IconMappingEnumerator() { 289 } 290 291 ThumbnailDatabase::IconMappingEnumerator::~IconMappingEnumerator() { 292 } 293 294 bool ThumbnailDatabase::IconMappingEnumerator::GetNextIconMapping( 295 IconMapping* icon_mapping) { 296 if (!statement_.Step()) 297 return false; 298 FillIconMapping(statement_, GURL(statement_.ColumnString(4)), icon_mapping); 299 return true; 300 } 301 302 ThumbnailDatabase::ThumbnailDatabase() 303 : history_publisher_(NULL), 304 use_top_sites_(false) { 305 } 306 307 sql::InitStatus ThumbnailDatabase::CantUpgradeToVersion(int cur_version) { 308 LOG(WARNING) << "Unable to update to thumbnail database to version " << 309 cur_version << "."; 310 db_.Close(); 311 return sql::INIT_FAILURE; 312 } 313 314 ThumbnailDatabase::~ThumbnailDatabase() { 315 // The DBCloseScoper will delete the DB and the cache. 316 } 317 318 sql::InitStatus ThumbnailDatabase::Init( 319 const base::FilePath& db_name, 320 const HistoryPublisher* history_publisher, 321 URLDatabase* url_db) { 322 history_publisher_ = history_publisher; 323 sql::InitStatus status = OpenDatabase(&db_, db_name); 324 if (status != sql::INIT_OK) 325 return status; 326 327 // Scope initialization in a transaction so we can't be partially initialized. 328 sql::Transaction transaction(&db_); 329 transaction.Begin(); 330 331 #if defined(OS_MACOSX) 332 // Exclude the thumbnails file from backups. 333 base::mac::SetFileBackupExclusion(db_name); 334 #endif 335 336 // Create the tables. 337 if (!meta_table_.Init(&db_, kCurrentVersionNumber, 338 kCompatibleVersionNumber) || 339 !InitThumbnailTable() || 340 !InitFaviconBitmapsTable(&db_, false) || 341 !InitFaviconBitmapsIndex() || 342 !InitFaviconsTable(&db_, false) || 343 !InitFaviconsIndex() || 344 !InitIconMappingTable(&db_, false) || 345 !InitIconMappingIndex()) { 346 db_.Close(); 347 return sql::INIT_FAILURE; 348 } 349 350 // Version check. We should not encounter a database too old for us to handle 351 // in the wild, so we try to continue in that case. 352 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { 353 LOG(WARNING) << "Thumbnail database is too new."; 354 return sql::INIT_TOO_NEW; 355 } 356 357 int cur_version = meta_table_.GetVersionNumber(); 358 if (cur_version == 2) { 359 ++cur_version; 360 if (!UpgradeToVersion3()) 361 return CantUpgradeToVersion(cur_version); 362 } 363 364 if (cur_version == 3) { 365 ++cur_version; 366 if (!UpgradeToVersion4() || !MigrateIconMappingData(url_db)) 367 return CantUpgradeToVersion(cur_version); 368 } 369 370 if (!db_.DoesColumnExist("favicons", "icon_type")) { 371 LOG(ERROR) << "Raze because of missing favicon.icon_type"; 372 RecordInvalidStructure(STRUCTURE_EVENT_VERSION4); 373 374 db_.RazeAndClose(); 375 return sql::INIT_FAILURE; 376 } 377 378 if (cur_version == 4) { 379 ++cur_version; 380 if (!UpgradeToVersion5()) 381 return CantUpgradeToVersion(cur_version); 382 } 383 384 if (cur_version < 7 && !db_.DoesColumnExist("favicons", "sizes")) { 385 LOG(ERROR) << "Raze because of missing favicon.sizes"; 386 RecordInvalidStructure(STRUCTURE_EVENT_VERSION5); 387 388 db_.RazeAndClose(); 389 return sql::INIT_FAILURE; 390 } 391 392 if (cur_version == 5) { 393 ++cur_version; 394 if (!UpgradeToVersion6()) 395 return CantUpgradeToVersion(cur_version); 396 } 397 398 if (cur_version == 6) { 399 ++cur_version; 400 if (!UpgradeToVersion7()) 401 return CantUpgradeToVersion(cur_version); 402 } 403 404 LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << 405 "Thumbnail database version " << cur_version << " is too old to handle."; 406 407 // Initialization is complete. 408 if (!transaction.Commit()) { 409 db_.Close(); 410 return sql::INIT_FAILURE; 411 } 412 413 // Raze the database if the structure of the favicons database is not what 414 // it should be. This error cannot be detected via the SQL error code because 415 // the error code for running SQL statements against a database with missing 416 // columns is SQLITE_ERROR which is not unique enough to act upon. 417 // TODO(pkotwicz): Revisit this in M27 and see if the razing can be removed. 418 // (crbug.com/166453) 419 if (IsFaviconDBStructureIncorrect()) { 420 LOG(ERROR) << "Raze because of invalid favicon db structure."; 421 RecordInvalidStructure(STRUCTURE_EVENT_FAVICON); 422 423 db_.RazeAndClose(); 424 return sql::INIT_FAILURE; 425 } 426 427 return sql::INIT_OK; 428 } 429 430 sql::InitStatus ThumbnailDatabase::OpenDatabase(sql::Connection* db, 431 const base::FilePath& db_name) { 432 size_t startup_kb = 0; 433 int64 size_64; 434 if (file_util::GetFileSize(db_name, &size_64)) 435 startup_kb = static_cast<size_t>(size_64 / 1024); 436 437 db->set_histogram_tag("Thumbnail"); 438 db->set_error_callback(base::Bind(&DatabaseErrorCallback, db, startup_kb)); 439 440 // Thumbnails db now only stores favicons, so we don't need that big a page 441 // size or cache. 442 db->set_page_size(2048); 443 db->set_cache_size(32); 444 445 // Run the database in exclusive mode. Nobody else should be accessing the 446 // database while we're running, and this will give somewhat improved perf. 447 db->set_exclusive_locking(); 448 449 if (!db->Open(db_name)) 450 return sql::INIT_FAILURE; 451 452 return sql::INIT_OK; 453 } 454 455 void ThumbnailDatabase::ComputeDatabaseMetrics() { 456 sql::Statement favicon_count( 457 db_.GetCachedStatement(SQL_FROM_HERE, "SELECT COUNT(*) FROM favicons")); 458 UMA_HISTOGRAM_COUNTS_10000( 459 "History.NumFaviconsInDB", 460 favicon_count.Step() ? favicon_count.ColumnInt(0) : 0); 461 } 462 463 bool ThumbnailDatabase::InitThumbnailTable() { 464 if (!db_.DoesTableExist("thumbnails")) { 465 use_top_sites_ = true; 466 } 467 return true; 468 } 469 470 bool ThumbnailDatabase::UpgradeToVersion3() { 471 if (use_top_sites_) { 472 meta_table_.SetVersionNumber(3); 473 meta_table_.SetCompatibleVersionNumber( 474 std::min(3, kCompatibleVersionNumber)); 475 return true; // Not needed after migration to TopSites. 476 } 477 478 // sqlite doesn't like the "ALTER TABLE xxx ADD (column_one, two, 479 // three)" syntax, so list out the commands we need to execute: 480 const char* alterations[] = { 481 "ALTER TABLE thumbnails ADD boring_score DOUBLE DEFAULT 1.0", 482 "ALTER TABLE thumbnails ADD good_clipping INTEGER DEFAULT 0", 483 "ALTER TABLE thumbnails ADD at_top INTEGER DEFAULT 0", 484 "ALTER TABLE thumbnails ADD last_updated INTEGER DEFAULT 0", 485 NULL 486 }; 487 488 for (int i = 0; alterations[i] != NULL; ++i) { 489 if (!db_.Execute(alterations[i])) { 490 return false; 491 } 492 } 493 494 meta_table_.SetVersionNumber(3); 495 meta_table_.SetCompatibleVersionNumber(std::min(3, kCompatibleVersionNumber)); 496 return true; 497 } 498 499 bool ThumbnailDatabase::RecreateThumbnailTable() { 500 if (use_top_sites_) 501 return true; // Not needed after migration to TopSites. 502 503 if (!db_.Execute("DROP TABLE thumbnails")) 504 return false; 505 return InitThumbnailTable(); 506 } 507 508 bool ThumbnailDatabase::InitFaviconsTable(sql::Connection* db, 509 bool is_temporary) { 510 // Note: if you update the schema, don't forget to update 511 // CopyFaviconAndFaviconBitmapsToTemporaryTables as well. 512 const char* name = is_temporary ? "temp_favicons" : "favicons"; 513 if (!db->DoesTableExist(name)) { 514 std::string sql; 515 sql.append("CREATE TABLE "); 516 sql.append(name); 517 sql.append("(" 518 "id INTEGER PRIMARY KEY," 519 "url LONGVARCHAR NOT NULL," 520 // Set the default icon_type as FAVICON to be consistent with 521 // table upgrade in UpgradeToVersion4(). 522 "icon_type INTEGER DEFAULT 1)"); 523 if (!db->Execute(sql.c_str())) 524 return false; 525 } 526 return true; 527 } 528 529 bool ThumbnailDatabase::InitFaviconsIndex() { 530 // Add an index on the url column. 531 return 532 db_.Execute("CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)"); 533 } 534 535 bool ThumbnailDatabase::InitFaviconBitmapsTable(sql::Connection* db, 536 bool is_temporary) { 537 // Note: if you update the schema, don't forget to update 538 // CopyFaviconAndFaviconBitmapsToTemporaryTables as well. 539 const char* name = is_temporary ? "temp_favicon_bitmaps" : "favicon_bitmaps"; 540 if (!db->DoesTableExist(name)) { 541 std::string sql; 542 sql.append("CREATE TABLE "); 543 sql.append(name); 544 sql.append("(" 545 "id INTEGER PRIMARY KEY," 546 "icon_id INTEGER NOT NULL," 547 "last_updated INTEGER DEFAULT 0," 548 "image_data BLOB," 549 "width INTEGER DEFAULT 0," 550 "height INTEGER DEFAULT 0)"); 551 if (!db->Execute(sql.c_str())) 552 return false; 553 } 554 return true; 555 } 556 557 bool ThumbnailDatabase::InitFaviconBitmapsIndex() { 558 // Add an index on the icon_id column. 559 return db_.Execute("CREATE INDEX IF NOT EXISTS favicon_bitmaps_icon_id ON " 560 "favicon_bitmaps(icon_id)"); 561 } 562 563 bool ThumbnailDatabase::IsFaviconDBStructureIncorrect() { 564 return !db_.IsSQLValid("SELECT id, url, icon_type FROM favicons"); 565 } 566 567 void ThumbnailDatabase::BeginTransaction() { 568 db_.BeginTransaction(); 569 } 570 571 void ThumbnailDatabase::CommitTransaction() { 572 db_.CommitTransaction(); 573 } 574 575 void ThumbnailDatabase::RollbackTransaction() { 576 db_.RollbackTransaction(); 577 } 578 579 void ThumbnailDatabase::Vacuum() { 580 DCHECK(db_.transaction_nesting() == 0) << 581 "Can not have a transaction when vacuuming."; 582 ignore_result(db_.Execute("VACUUM")); 583 } 584 585 void ThumbnailDatabase::TrimMemory(bool aggressively) { 586 db_.TrimMemory(aggressively); 587 } 588 589 bool ThumbnailDatabase::SetPageThumbnail( 590 const GURL& url, 591 URLID id, 592 const gfx::Image* thumbnail, 593 const ThumbnailScore& score, 594 base::Time time) { 595 if (use_top_sites_) { 596 LOG(WARNING) << "Use TopSites instead."; 597 return false; // Not possible after migration to TopSites. 598 } 599 600 if (!thumbnail) 601 return DeleteThumbnail(id); 602 603 bool add_thumbnail = true; 604 ThumbnailScore current_score; 605 if (ThumbnailScoreForId(id, ¤t_score)) { 606 add_thumbnail = ShouldReplaceThumbnailWith(current_score, score); 607 } 608 609 if (!add_thumbnail) 610 return true; 611 612 std::vector<unsigned char> jpeg_data; 613 bool encoded = gfx::JPEG1xEncodedDataFromImage( 614 *thumbnail, kImageQuality, &jpeg_data); 615 if (encoded) { 616 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 617 "INSERT OR REPLACE INTO thumbnails " 618 "(url_id, boring_score, good_clipping, at_top, last_updated, data) " 619 "VALUES (?,?,?,?,?,?)")); 620 statement.BindInt64(0, id); 621 statement.BindDouble(1, score.boring_score); 622 statement.BindBool(2, score.good_clipping); 623 statement.BindBool(3, score.at_top); 624 statement.BindInt64(4, score.time_at_snapshot.ToInternalValue()); 625 statement.BindBlob(5, &jpeg_data[0], 626 static_cast<int>(jpeg_data.size())); 627 628 if (!statement.Run()) 629 return false; 630 } 631 632 // Publish the thumbnail to any indexers listening to us. 633 // The tests may send an invalid url. Hence avoid publishing those. 634 if (url.is_valid() && history_publisher_ != NULL) 635 history_publisher_->PublishPageThumbnail(jpeg_data, url, time); 636 637 return true; 638 } 639 640 bool ThumbnailDatabase::GetPageThumbnail(URLID id, 641 std::vector<unsigned char>* data) { 642 if (use_top_sites_) { 643 LOG(WARNING) << "Use TopSites instead."; 644 return false; // Not possible after migration to TopSites. 645 } 646 647 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 648 "SELECT data FROM thumbnails WHERE url_id=?")); 649 statement.BindInt64(0, id); 650 651 if (!statement.Step()) 652 return false; // don't have a thumbnail for this ID 653 654 statement.ColumnBlobAsVector(0, data); 655 return true; 656 } 657 658 bool ThumbnailDatabase::DeleteThumbnail(URLID id) { 659 if (use_top_sites_) { 660 return true; // Not possible after migration to TopSites. 661 } 662 663 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 664 "DELETE FROM thumbnails WHERE url_id = ?")); 665 statement.BindInt64(0, id); 666 667 return statement.Run(); 668 } 669 670 bool ThumbnailDatabase::ThumbnailScoreForId(URLID id, 671 ThumbnailScore* score) { 672 DCHECK(score); 673 if (use_top_sites_) { 674 LOG(WARNING) << "Use TopSites instead."; 675 return false; // Not possible after migration to TopSites. 676 } 677 678 // Fetch the current thumbnail's information to make sure we 679 // aren't replacing a good thumbnail with one that's worse. 680 sql::Statement select_statement(db_.GetCachedStatement(SQL_FROM_HERE, 681 "SELECT boring_score, good_clipping, at_top, last_updated " 682 "FROM thumbnails WHERE url_id=?")); 683 select_statement.BindInt64(0, id); 684 685 if (!select_statement.Step()) 686 return false; 687 688 double current_boring_score = select_statement.ColumnDouble(0); 689 bool current_clipping = select_statement.ColumnBool(1); 690 bool current_at_top = select_statement.ColumnBool(2); 691 base::Time last_updated = 692 base::Time::FromInternalValue(select_statement.ColumnInt64(3)); 693 *score = ThumbnailScore(current_boring_score, current_clipping, 694 current_at_top, last_updated); 695 return true; 696 } 697 698 bool ThumbnailDatabase::GetFaviconBitmapIDSizes( 699 chrome::FaviconID icon_id, 700 std::vector<FaviconBitmapIDSize>* bitmap_id_sizes) { 701 DCHECK(icon_id); 702 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 703 "SELECT id, width, height FROM favicon_bitmaps WHERE icon_id=?")); 704 statement.BindInt64(0, icon_id); 705 706 bool result = false; 707 while (statement.Step()) { 708 result = true; 709 if (!bitmap_id_sizes) 710 return result; 711 712 FaviconBitmapIDSize bitmap_id_size; 713 bitmap_id_size.bitmap_id = statement.ColumnInt64(0); 714 bitmap_id_size.pixel_size = gfx::Size(statement.ColumnInt(1), 715 statement.ColumnInt(2)); 716 bitmap_id_sizes->push_back(bitmap_id_size); 717 } 718 return result; 719 } 720 721 bool ThumbnailDatabase::GetFaviconBitmaps( 722 chrome::FaviconID icon_id, 723 std::vector<FaviconBitmap>* favicon_bitmaps) { 724 DCHECK(icon_id); 725 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 726 "SELECT id, last_updated, image_data, width, height FROM favicon_bitmaps " 727 "WHERE icon_id=?")); 728 statement.BindInt64(0, icon_id); 729 730 bool result = false; 731 while (statement.Step()) { 732 result = true; 733 if (!favicon_bitmaps) 734 return result; 735 736 FaviconBitmap favicon_bitmap; 737 favicon_bitmap.bitmap_id = statement.ColumnInt64(0); 738 favicon_bitmap.icon_id = icon_id; 739 favicon_bitmap.last_updated = 740 base::Time::FromInternalValue(statement.ColumnInt64(1)); 741 if (statement.ColumnByteLength(2) > 0) { 742 scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes()); 743 statement.ColumnBlobAsVector(2, &data->data()); 744 favicon_bitmap.bitmap_data = data; 745 } 746 favicon_bitmap.pixel_size = gfx::Size(statement.ColumnInt(3), 747 statement.ColumnInt(4)); 748 favicon_bitmaps->push_back(favicon_bitmap); 749 } 750 return result; 751 } 752 753 bool ThumbnailDatabase::GetFaviconBitmap( 754 FaviconBitmapID bitmap_id, 755 base::Time* last_updated, 756 scoped_refptr<base::RefCountedMemory>* png_icon_data, 757 gfx::Size* pixel_size) { 758 DCHECK(bitmap_id); 759 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 760 "SELECT last_updated, image_data, width, height FROM favicon_bitmaps " 761 "WHERE id=?")); 762 statement.BindInt64(0, bitmap_id); 763 764 if (!statement.Step()) 765 return false; 766 767 if (last_updated) 768 *last_updated = base::Time::FromInternalValue(statement.ColumnInt64(0)); 769 770 if (png_icon_data && statement.ColumnByteLength(1) > 0) { 771 scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes()); 772 statement.ColumnBlobAsVector(1, &data->data()); 773 *png_icon_data = data; 774 } 775 776 if (pixel_size) { 777 *pixel_size = gfx::Size(statement.ColumnInt(2), 778 statement.ColumnInt(3)); 779 } 780 return true; 781 } 782 783 FaviconBitmapID ThumbnailDatabase::AddFaviconBitmap( 784 chrome::FaviconID icon_id, 785 const scoped_refptr<base::RefCountedMemory>& icon_data, 786 base::Time time, 787 const gfx::Size& pixel_size) { 788 DCHECK(icon_id); 789 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 790 "INSERT INTO favicon_bitmaps (icon_id, image_data, last_updated, width, " 791 "height) VALUES (?, ?, ?, ?, ?)")); 792 statement.BindInt64(0, icon_id); 793 if (icon_data.get() && icon_data->size()) { 794 statement.BindBlob(1, icon_data->front(), 795 static_cast<int>(icon_data->size())); 796 } else { 797 statement.BindNull(1); 798 } 799 statement.BindInt64(2, time.ToInternalValue()); 800 statement.BindInt(3, pixel_size.width()); 801 statement.BindInt(4, pixel_size.height()); 802 803 if (!statement.Run()) 804 return 0; 805 return db_.GetLastInsertRowId(); 806 } 807 808 bool ThumbnailDatabase::SetFaviconBitmap( 809 FaviconBitmapID bitmap_id, 810 scoped_refptr<base::RefCountedMemory> bitmap_data, 811 base::Time time) { 812 DCHECK(bitmap_id); 813 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 814 "UPDATE favicon_bitmaps SET image_data=?, last_updated=? WHERE id=?")); 815 if (bitmap_data.get() && bitmap_data->size()) { 816 statement.BindBlob(0, bitmap_data->front(), 817 static_cast<int>(bitmap_data->size())); 818 } else { 819 statement.BindNull(0); 820 } 821 statement.BindInt64(1, time.ToInternalValue()); 822 statement.BindInt64(2, bitmap_id); 823 824 return statement.Run(); 825 } 826 827 bool ThumbnailDatabase::SetFaviconBitmapLastUpdateTime( 828 FaviconBitmapID bitmap_id, 829 base::Time time) { 830 DCHECK(bitmap_id); 831 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 832 "UPDATE favicon_bitmaps SET last_updated=? WHERE id=?")); 833 statement.BindInt64(0, time.ToInternalValue()); 834 statement.BindInt64(1, bitmap_id); 835 return statement.Run(); 836 } 837 838 bool ThumbnailDatabase::DeleteFaviconBitmapsForFavicon( 839 chrome::FaviconID icon_id) { 840 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 841 "DELETE FROM favicon_bitmaps WHERE icon_id=?")); 842 statement.BindInt64(0, icon_id); 843 return statement.Run(); 844 } 845 846 bool ThumbnailDatabase::DeleteFaviconBitmap(FaviconBitmapID bitmap_id) { 847 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 848 "DELETE FROM favicon_bitmaps WHERE id=?")); 849 statement.BindInt64(0, bitmap_id); 850 return statement.Run(); 851 } 852 853 bool ThumbnailDatabase::SetFaviconOutOfDate(chrome::FaviconID icon_id) { 854 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 855 "UPDATE favicon_bitmaps SET last_updated=? WHERE icon_id=?")); 856 statement.BindInt64(0, 0); 857 statement.BindInt64(1, icon_id); 858 859 return statement.Run(); 860 } 861 862 chrome::FaviconID ThumbnailDatabase::GetFaviconIDForFaviconURL( 863 const GURL& icon_url, 864 int required_icon_type, 865 chrome::IconType* icon_type) { 866 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 867 "SELECT id, icon_type FROM favicons WHERE url=? AND (icon_type & ? > 0) " 868 "ORDER BY icon_type DESC")); 869 statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url)); 870 statement.BindInt(1, required_icon_type); 871 872 if (!statement.Step()) 873 return 0; // not cached 874 875 if (icon_type) 876 *icon_type = static_cast<chrome::IconType>(statement.ColumnInt(1)); 877 return statement.ColumnInt64(0); 878 } 879 880 bool ThumbnailDatabase::GetFaviconHeader(chrome::FaviconID icon_id, 881 GURL* icon_url, 882 chrome::IconType* icon_type) { 883 DCHECK(icon_id); 884 885 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 886 "SELECT url, icon_type FROM favicons WHERE id=?")); 887 statement.BindInt64(0, icon_id); 888 889 if (!statement.Step()) 890 return false; // No entry for the id. 891 892 if (icon_url) 893 *icon_url = GURL(statement.ColumnString(0)); 894 if (icon_type) 895 *icon_type = static_cast<chrome::IconType>(statement.ColumnInt(1)); 896 897 return true; 898 } 899 900 chrome::FaviconID ThumbnailDatabase::AddFavicon( 901 const GURL& icon_url, 902 chrome::IconType icon_type) { 903 904 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 905 "INSERT INTO favicons (url, icon_type) VALUES (?, ?)")); 906 statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url)); 907 statement.BindInt(1, icon_type); 908 909 if (!statement.Run()) 910 return 0; 911 return db_.GetLastInsertRowId(); 912 } 913 914 chrome::FaviconID ThumbnailDatabase::AddFavicon( 915 const GURL& icon_url, 916 chrome::IconType icon_type, 917 const scoped_refptr<base::RefCountedMemory>& icon_data, 918 base::Time time, 919 const gfx::Size& pixel_size) { 920 chrome::FaviconID icon_id = AddFavicon(icon_url, icon_type); 921 if (!icon_id || !AddFaviconBitmap(icon_id, icon_data, time, pixel_size)) 922 return 0; 923 924 return icon_id; 925 } 926 927 bool ThumbnailDatabase::DeleteFavicon(chrome::FaviconID id) { 928 sql::Statement statement; 929 statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE, 930 "DELETE FROM favicons WHERE id = ?")); 931 statement.BindInt64(0, id); 932 if (!statement.Run()) 933 return false; 934 935 statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE, 936 "DELETE FROM favicon_bitmaps WHERE icon_id = ?")); 937 statement.BindInt64(0, id); 938 return statement.Run(); 939 } 940 941 bool ThumbnailDatabase::GetIconMappingsForPageURL( 942 const GURL& page_url, 943 int required_icon_types, 944 std::vector<IconMapping>* filtered_mapping_data) { 945 std::vector<IconMapping> mapping_data; 946 if (!GetIconMappingsForPageURL(page_url, &mapping_data)) 947 return false; 948 949 bool result = false; 950 for (std::vector<IconMapping>::iterator m = mapping_data.begin(); 951 m != mapping_data.end(); ++m) { 952 if (m->icon_type & required_icon_types) { 953 result = true; 954 if (!filtered_mapping_data) 955 return result; 956 957 // Restrict icon type of subsequent matches to |m->icon_type|. 958 // |m->icon_type| is the largest IconType in |mapping_data| because 959 // |mapping_data| is sorted in descending order of IconType. 960 required_icon_types = m->icon_type; 961 962 filtered_mapping_data->push_back(*m); 963 } 964 } 965 return result; 966 } 967 968 bool ThumbnailDatabase::GetIconMappingsForPageURL( 969 const GURL& page_url, 970 std::vector<IconMapping>* mapping_data) { 971 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 972 "SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type, " 973 "favicons.url " 974 "FROM icon_mapping " 975 "INNER JOIN favicons " 976 "ON icon_mapping.icon_id = favicons.id " 977 "WHERE icon_mapping.page_url=? " 978 "ORDER BY favicons.icon_type DESC")); 979 statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url)); 980 981 bool result = false; 982 while (statement.Step()) { 983 result = true; 984 if (!mapping_data) 985 return result; 986 987 IconMapping icon_mapping; 988 FillIconMapping(statement, page_url, &icon_mapping); 989 mapping_data->push_back(icon_mapping); 990 } 991 return result; 992 } 993 994 IconMappingID ThumbnailDatabase::AddIconMapping(const GURL& page_url, 995 chrome::FaviconID icon_id) { 996 return AddIconMapping(page_url, icon_id, false); 997 } 998 999 bool ThumbnailDatabase::UpdateIconMapping(IconMappingID mapping_id, 1000 chrome::FaviconID icon_id) { 1001 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 1002 "UPDATE icon_mapping SET icon_id=? WHERE id=?")); 1003 statement.BindInt64(0, icon_id); 1004 statement.BindInt64(1, mapping_id); 1005 1006 return statement.Run(); 1007 } 1008 1009 bool ThumbnailDatabase::DeleteIconMappings(const GURL& page_url) { 1010 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 1011 "DELETE FROM icon_mapping WHERE page_url = ?")); 1012 statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url)); 1013 1014 return statement.Run(); 1015 } 1016 1017 bool ThumbnailDatabase::DeleteIconMapping(IconMappingID mapping_id) { 1018 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 1019 "DELETE FROM icon_mapping WHERE id=?")); 1020 statement.BindInt64(0, mapping_id); 1021 1022 return statement.Run(); 1023 } 1024 1025 bool ThumbnailDatabase::HasMappingFor(chrome::FaviconID id) { 1026 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 1027 "SELECT id FROM icon_mapping " 1028 "WHERE icon_id=?")); 1029 statement.BindInt64(0, id); 1030 1031 return statement.Step(); 1032 } 1033 1034 bool ThumbnailDatabase::CloneIconMappings(const GURL& old_page_url, 1035 const GURL& new_page_url) { 1036 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, 1037 "SELECT icon_id FROM icon_mapping " 1038 "WHERE page_url=?")); 1039 if (!statement.is_valid()) 1040 return false; 1041 1042 // Do nothing if there are existing bindings 1043 statement.BindString(0, URLDatabase::GURLToDatabaseURL(new_page_url)); 1044 if (statement.Step()) 1045 return true; 1046 1047 statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE, 1048 "INSERT INTO icon_mapping (page_url, icon_id) " 1049 "SELECT ?, icon_id FROM icon_mapping " 1050 "WHERE page_url = ?")); 1051 1052 statement.BindString(0, URLDatabase::GURLToDatabaseURL(new_page_url)); 1053 statement.BindString(1, URLDatabase::GURLToDatabaseURL(old_page_url)); 1054 return statement.Run(); 1055 } 1056 1057 bool ThumbnailDatabase::InitIconMappingEnumerator( 1058 chrome::IconType type, 1059 IconMappingEnumerator* enumerator) { 1060 DCHECK(!enumerator->statement_.is_valid()); 1061 enumerator->statement_.Assign(db_.GetCachedStatement( 1062 SQL_FROM_HERE, 1063 "SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type, " 1064 "favicons.url, icon_mapping.page_url " 1065 "FROM icon_mapping JOIN favicons ON (" 1066 "icon_mapping.icon_id = favicons.id) " 1067 "WHERE favicons.icon_type = ?")); 1068 enumerator->statement_.BindInt(0, type); 1069 return enumerator->statement_.is_valid(); 1070 } 1071 1072 bool ThumbnailDatabase::MigrateIconMappingData(URLDatabase* url_db) { 1073 URLDatabase::IconMappingEnumerator e; 1074 if (!url_db->InitIconMappingEnumeratorForEverything(&e)) 1075 return false; 1076 1077 IconMapping info; 1078 while (e.GetNextIconMapping(&info)) { 1079 // TODO: Using bulk insert to improve the performance. 1080 if (!AddIconMapping(info.page_url, info.icon_id)) 1081 return false; 1082 } 1083 return true; 1084 } 1085 1086 bool ThumbnailDatabase::InitTemporaryTables() { 1087 return InitIconMappingTable(&db_, true) && 1088 InitFaviconsTable(&db_, true) && 1089 InitFaviconBitmapsTable(&db_, true); 1090 } 1091 1092 bool ThumbnailDatabase::CommitTemporaryTables() { 1093 const char* main_tables[] = { "icon_mapping", 1094 "favicons", 1095 "favicon_bitmaps" }; 1096 const char* temporary_tables[] = { "temp_icon_mapping", 1097 "temp_favicons", 1098 "temp_favicon_bitmaps" }; 1099 DCHECK_EQ(arraysize(main_tables), arraysize(temporary_tables)); 1100 1101 for (size_t i = 0; i < arraysize(main_tables); ++i) { 1102 // Delete the main table. 1103 std::string sql; 1104 sql.append("DROP TABLE "); 1105 sql.append(main_tables[i]); 1106 if (!db_.Execute(sql.c_str())) 1107 return false; 1108 1109 // Rename the temporary table. 1110 sql.clear(); 1111 sql.append("ALTER TABLE "); 1112 sql.append(temporary_tables[i]); 1113 sql.append(" RENAME TO "); 1114 sql.append(main_tables[i]); 1115 if (!db_.Execute(sql.c_str())) 1116 return false; 1117 } 1118 1119 // The renamed tables needs indices (the temporary tables don't have any). 1120 return InitIconMappingIndex() && 1121 InitFaviconsIndex() && 1122 InitFaviconBitmapsIndex(); 1123 } 1124 1125 IconMappingID ThumbnailDatabase::AddToTemporaryIconMappingTable( 1126 const GURL& page_url, 1127 const chrome::FaviconID icon_id) { 1128 return AddIconMapping(page_url, icon_id, true); 1129 } 1130 1131 chrome::FaviconID 1132 ThumbnailDatabase::CopyFaviconAndFaviconBitmapsToTemporaryTables( 1133 chrome::FaviconID source) { 1134 sql::Statement statement; 1135 statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE, 1136 "INSERT INTO temp_favicons (url, icon_type) " 1137 "SELECT url, icon_type FROM favicons WHERE id = ?")); 1138 statement.BindInt64(0, source); 1139 1140 if (!statement.Run()) 1141 return 0; 1142 1143 chrome::FaviconID new_favicon_id = db_.GetLastInsertRowId(); 1144 1145 statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE, 1146 "INSERT INTO temp_favicon_bitmaps (icon_id, last_updated, image_data, " 1147 "width, height) " 1148 "SELECT ?, last_updated, image_data, width, height " 1149 "FROM favicon_bitmaps WHERE icon_id = ?")); 1150 statement.BindInt64(0, new_favicon_id); 1151 statement.BindInt64(1, source); 1152 if (!statement.Run()) 1153 return 0; 1154 1155 return new_favicon_id; 1156 } 1157 1158 bool ThumbnailDatabase::NeedsMigrationToTopSites() { 1159 return !use_top_sites_; 1160 } 1161 1162 bool ThumbnailDatabase::RenameAndDropThumbnails( 1163 const base::FilePath& old_db_file, 1164 const base::FilePath& new_db_file) { 1165 // Init favicons tables - same schema as the thumbnails. 1166 sql::Connection favicons; 1167 if (OpenDatabase(&favicons, new_db_file) != sql::INIT_OK) 1168 return false; 1169 1170 if (!InitFaviconBitmapsTable(&favicons, false) || 1171 !InitFaviconsTable(&favicons, false) || 1172 !InitIconMappingTable(&favicons, false)) { 1173 favicons.Close(); 1174 return false; 1175 } 1176 favicons.Close(); 1177 1178 // Can't attach within a transaction. 1179 if (transaction_nesting()) 1180 CommitTransaction(); 1181 1182 // Attach new DB. 1183 { 1184 // This block is needed because otherwise the attach statement is 1185 // never cleared from cache and we can't close the DB :P 1186 sql::Statement attach(db_.GetUniqueStatement("ATTACH ? AS new_favicons")); 1187 if (!attach.is_valid()) { 1188 // Keep the transaction open, even though we failed. 1189 BeginTransaction(); 1190 return false; 1191 } 1192 1193 #if defined(OS_POSIX) 1194 attach.BindString(0, new_db_file.value()); 1195 #else 1196 attach.BindString(0, WideToUTF8(new_db_file.value())); 1197 #endif 1198 1199 if (!attach.Run()) { 1200 BeginTransaction(); 1201 return false; 1202 } 1203 } 1204 1205 // Move favicons and favicon_bitmaps to new DB. 1206 bool successfully_moved_data = 1207 db_.Execute("INSERT OR REPLACE INTO new_favicons.favicon_bitmaps " 1208 "SELECT * FROM favicon_bitmaps") && 1209 db_.Execute("INSERT OR REPLACE INTO new_favicons.favicons " 1210 "SELECT * FROM favicons"); 1211 if (!successfully_moved_data) { 1212 DLOG(FATAL) << "Unable to copy favicons and favicon_bitmaps."; 1213 BeginTransaction(); 1214 return false; 1215 } 1216 1217 if (!db_.Execute("DETACH new_favicons")) { 1218 DLOG(FATAL) << "Unable to detach database."; 1219 BeginTransaction(); 1220 return false; 1221 } 1222 1223 db_.Close(); 1224 1225 // Reset the DB to point to new file. 1226 if (OpenDatabase(&db_, new_db_file) != sql::INIT_OK) 1227 return false; 1228 1229 sql::Connection::Delete(old_db_file); 1230 1231 meta_table_.Reset(); 1232 if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber)) 1233 return false; 1234 1235 if (!InitFaviconBitmapsIndex() || !InitFaviconsIndex()) 1236 return false; 1237 1238 // Reopen the transaction. 1239 BeginTransaction(); 1240 use_top_sites_ = true; 1241 return true; 1242 } 1243 1244 bool ThumbnailDatabase::InitIconMappingTable(sql::Connection* db, 1245 bool is_temporary) { 1246 const char* name = is_temporary ? "temp_icon_mapping" : "icon_mapping"; 1247 if (!db->DoesTableExist(name)) { 1248 std::string sql; 1249 sql.append("CREATE TABLE "); 1250 sql.append(name); 1251 sql.append("(" 1252 "id INTEGER PRIMARY KEY," 1253 "page_url LONGVARCHAR NOT NULL," 1254 "icon_id INTEGER)"); 1255 if (!db->Execute(sql.c_str())) 1256 return false; 1257 } 1258 return true; 1259 } 1260 1261 bool ThumbnailDatabase::InitIconMappingIndex() { 1262 // Add an index on the url column. 1263 return 1264 db_.Execute("CREATE INDEX IF NOT EXISTS icon_mapping_page_url_idx" 1265 " ON icon_mapping(page_url)") && 1266 db_.Execute("CREATE INDEX IF NOT EXISTS icon_mapping_icon_id_idx" 1267 " ON icon_mapping(icon_id)"); 1268 } 1269 1270 IconMappingID ThumbnailDatabase::AddIconMapping(const GURL& page_url, 1271 chrome::FaviconID icon_id, 1272 bool is_temporary) { 1273 const char* name = is_temporary ? "temp_icon_mapping" : "icon_mapping"; 1274 const char* statement_name = 1275 is_temporary ? "add_temp_icon_mapping" : "add_icon_mapping"; 1276 1277 std::string sql; 1278 sql.append("INSERT INTO "); 1279 sql.append(name); 1280 sql.append("(page_url, icon_id) VALUES (?, ?)"); 1281 1282 sql::Statement statement( 1283 db_.GetCachedStatement(sql::StatementID(statement_name), sql.c_str())); 1284 statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url)); 1285 statement.BindInt64(1, icon_id); 1286 1287 if (!statement.Run()) 1288 return 0; 1289 1290 return db_.GetLastInsertRowId(); 1291 } 1292 1293 bool ThumbnailDatabase::IsLatestVersion() { 1294 return meta_table_.GetVersionNumber() == kCurrentVersionNumber; 1295 } 1296 1297 bool ThumbnailDatabase::UpgradeToVersion4() { 1298 // Set the default icon type as favicon, so the current data are set 1299 // correctly. 1300 if (!db_.Execute("ALTER TABLE favicons ADD icon_type INTEGER DEFAULT 1")) { 1301 return false; 1302 } 1303 meta_table_.SetVersionNumber(4); 1304 meta_table_.SetCompatibleVersionNumber(std::min(4, kCompatibleVersionNumber)); 1305 return true; 1306 } 1307 1308 bool ThumbnailDatabase::UpgradeToVersion5() { 1309 if (!db_.Execute("ALTER TABLE favicons ADD sizes LONGVARCHAR")) { 1310 return false; 1311 } 1312 meta_table_.SetVersionNumber(5); 1313 meta_table_.SetCompatibleVersionNumber(std::min(5, kCompatibleVersionNumber)); 1314 return true; 1315 } 1316 1317 bool ThumbnailDatabase::UpgradeToVersion6() { 1318 bool success = 1319 db_.Execute("INSERT INTO favicon_bitmaps (icon_id, last_updated, " 1320 "image_data, width, height)" 1321 "SELECT id, last_updated, image_data, 0, 0 FROM favicons") && 1322 db_.Execute("CREATE TABLE temp_favicons (" 1323 "id INTEGER PRIMARY KEY," 1324 "url LONGVARCHAR NOT NULL," 1325 "icon_type INTEGER DEFAULT 1," 1326 // Set the default icon_type as FAVICON to be consistent with 1327 // table upgrade in UpgradeToVersion4(). 1328 "sizes LONGVARCHAR)") && 1329 db_.Execute("INSERT INTO temp_favicons (id, url, icon_type) " 1330 "SELECT id, url, icon_type FROM favicons") && 1331 db_.Execute("DROP TABLE favicons") && 1332 db_.Execute("ALTER TABLE temp_favicons RENAME TO favicons"); 1333 if (!success) 1334 return false; 1335 1336 meta_table_.SetVersionNumber(6); 1337 meta_table_.SetCompatibleVersionNumber(std::min(6, kCompatibleVersionNumber)); 1338 return true; 1339 } 1340 1341 bool ThumbnailDatabase::UpgradeToVersion7() { 1342 bool success = 1343 db_.Execute("CREATE TABLE temp_favicons (" 1344 "id INTEGER PRIMARY KEY," 1345 "url LONGVARCHAR NOT NULL," 1346 "icon_type INTEGER DEFAULT 1)") && 1347 db_.Execute("INSERT INTO temp_favicons (id, url, icon_type) " 1348 "SELECT id, url, icon_type FROM favicons") && 1349 db_.Execute("DROP TABLE favicons") && 1350 db_.Execute("ALTER TABLE temp_favicons RENAME TO favicons") && 1351 db_.Execute("CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)"); 1352 1353 if (!success) 1354 return false; 1355 1356 meta_table_.SetVersionNumber(7); 1357 meta_table_.SetCompatibleVersionNumber(std::min(7, kCompatibleVersionNumber)); 1358 return true; 1359 } 1360 1361 } // namespace history 1362