Home | History | Annotate | Download | only in history
      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, &current_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