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