Home | History | Annotate | Download | only in history
      1 // Copyright (c) 2009 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 "app/sql/connection.h"
      6 #include "app/sql/transaction.h"
      7 #include "base/file_util.h"
      8 #include "base/string_split.h"
      9 #include "base/string_util.h"
     10 #include "chrome/browser/diagnostics/sqlite_diagnostics.h"
     11 #include "chrome/browser/history/history_types.h"
     12 #include "chrome/browser/history/top_sites.h"
     13 #include "chrome/browser/history/top_sites_database.h"
     14 
     15 namespace history {
     16 
     17 static const int kVersionNumber = 1;
     18 
     19 TopSitesDatabase::TopSitesDatabase() : may_need_history_migration_(false) {
     20 }
     21 
     22 TopSitesDatabase::~TopSitesDatabase() {
     23 }
     24 
     25 bool TopSitesDatabase::Init(const FilePath& db_name) {
     26   bool file_existed = file_util::PathExists(db_name);
     27 
     28   if (!file_existed)
     29     may_need_history_migration_ = true;
     30 
     31   db_.reset(CreateDB(db_name));
     32   if (!db_.get())
     33     return false;
     34 
     35   bool does_meta_exist = sql::MetaTable::DoesTableExist(db_.get());
     36   if (!does_meta_exist && file_existed) {
     37     may_need_history_migration_ = true;
     38 
     39     // If the meta file doesn't exist, this version is old. We could remove all
     40     // the entries as they are no longer applicable, but it's safest to just
     41     // remove the file and start over.
     42     db_.reset(NULL);
     43     if (!file_util::Delete(db_name, false) &&
     44         !file_util::Delete(db_name, false)) {
     45       // Try to delete twice. If we can't, fail.
     46       LOG(ERROR) << "unable to delete old TopSites file";
     47       return false;
     48     }
     49     db_.reset(CreateDB(db_name));
     50     if (!db_.get())
     51       return false;
     52   }
     53 
     54   if (!meta_table_.Init(db_.get(), kVersionNumber, kVersionNumber))
     55     return false;
     56 
     57   if (!InitThumbnailTable())
     58     return false;
     59 
     60   // Version check.
     61   if (meta_table_.GetVersionNumber() != kVersionNumber)
     62     return false;
     63 
     64   return true;
     65 }
     66 
     67 bool TopSitesDatabase::InitThumbnailTable() {
     68   if (!db_->DoesTableExist("thumbnails")) {
     69     if (!db_->Execute("CREATE TABLE thumbnails ("
     70                       "url LONGVARCHAR PRIMARY KEY,"
     71                       "url_rank INTEGER ,"
     72                       "title LONGVARCHAR,"
     73                       "thumbnail BLOB,"
     74                       "redirects LONGVARCHAR,"
     75                       "boring_score DOUBLE DEFAULT 1.0, "
     76                       "good_clipping INTEGER DEFAULT 0, "
     77                       "at_top INTEGER DEFAULT 0, "
     78                       "last_updated INTEGER DEFAULT 0) ")) {
     79       LOG(WARNING) << db_->GetErrorMessage();
     80       return false;
     81     }
     82   }
     83   return true;
     84 }
     85 
     86 void TopSitesDatabase::GetPageThumbnails(MostVisitedURLList* urls,
     87                                              URLToImagesMap* thumbnails) {
     88   sql::Statement statement(db_->GetCachedStatement(
     89       SQL_FROM_HERE,
     90       "SELECT url, url_rank, title, thumbnail, redirects, "
     91       "boring_score, good_clipping, at_top, last_updated "
     92       "FROM thumbnails ORDER BY url_rank "));
     93 
     94   if (!statement) {
     95     LOG(WARNING) << db_->GetErrorMessage();
     96     return;
     97   }
     98 
     99   urls->clear();
    100   thumbnails->clear();
    101 
    102   while (statement.Step()) {
    103     // Results are sorted by url_rank.
    104     MostVisitedURL url;
    105     GURL gurl(statement.ColumnString(0));
    106     url.url = gurl;
    107     url.title = statement.ColumnString16(2);
    108     std::string redirects = statement.ColumnString(4);
    109     SetRedirects(redirects, &url);
    110     urls->push_back(url);
    111 
    112     std::vector<unsigned char> data;
    113     statement.ColumnBlobAsVector(3, &data);
    114     Images thumbnail;
    115     thumbnail.thumbnail = RefCountedBytes::TakeVector(&data);
    116     thumbnail.thumbnail_score.boring_score = statement.ColumnDouble(5);
    117     thumbnail.thumbnail_score.good_clipping = statement.ColumnBool(6);
    118     thumbnail.thumbnail_score.at_top = statement.ColumnBool(7);
    119     thumbnail.thumbnail_score.time_at_snapshot =
    120         base::Time::FromInternalValue(statement.ColumnInt64(8));
    121 
    122     (*thumbnails)[gurl] = thumbnail;
    123   }
    124 }
    125 
    126 // static
    127 std::string TopSitesDatabase::GetRedirects(const MostVisitedURL& url) {
    128   std::vector<std::string> redirects;
    129   for (size_t i = 0; i < url.redirects.size(); i++)
    130     redirects.push_back(url.redirects[i].spec());
    131   return JoinString(redirects, ' ');
    132 }
    133 
    134 // static
    135 void TopSitesDatabase::SetRedirects(const std::string& redirects,
    136                                     MostVisitedURL* url) {
    137   std::vector<std::string> redirects_vector;
    138   base::SplitStringAlongWhitespace(redirects, &redirects_vector);
    139   for (size_t i = 0; i < redirects_vector.size(); ++i)
    140     url->redirects.push_back(GURL(redirects_vector[i]));
    141 }
    142 
    143 void TopSitesDatabase::SetPageThumbnail(const MostVisitedURL& url,
    144                                             int new_rank,
    145                                             const Images& thumbnail) {
    146   sql::Transaction transaction(db_.get());
    147   transaction.Begin();
    148 
    149   int rank = GetURLRank(url);
    150   if (rank == -1) {
    151     AddPageThumbnail(url, new_rank, thumbnail);
    152   } else {
    153     UpdatePageRankNoTransaction(url, new_rank);
    154     UpdatePageThumbnail(url, thumbnail);
    155   }
    156 
    157   transaction.Commit();
    158 }
    159 
    160 void TopSitesDatabase::UpdatePageThumbnail(
    161     const MostVisitedURL& url, const Images& thumbnail) {
    162   sql::Statement statement(db_->GetCachedStatement(
    163       SQL_FROM_HERE,
    164       "UPDATE thumbnails SET "
    165       "title = ?, thumbnail = ?, redirects = ?, "
    166       "boring_score = ?, good_clipping = ?, at_top = ?, last_updated = ? "
    167       "WHERE url = ? "));
    168   if (!statement)
    169     return;
    170 
    171   statement.BindString16(0, url.title);
    172   if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) {
    173     statement.BindBlob(1, thumbnail.thumbnail->front(),
    174                        static_cast<int>(thumbnail.thumbnail->size()));
    175   }
    176   statement.BindString(2, GetRedirects(url));
    177   const ThumbnailScore& score = thumbnail.thumbnail_score;
    178   statement.BindDouble(3, score.boring_score);
    179   statement.BindBool(4, score.good_clipping);
    180   statement.BindBool(5, score.at_top);
    181   statement.BindInt64(6, score.time_at_snapshot.ToInternalValue());
    182   statement.BindString(7, url.url.spec());
    183   if (!statement.Run())
    184     NOTREACHED() << db_->GetErrorMessage();
    185 }
    186 
    187 void TopSitesDatabase::AddPageThumbnail(const MostVisitedURL& url,
    188                                             int new_rank,
    189                                             const Images& thumbnail) {
    190   int count = GetRowCount();
    191 
    192   sql::Statement statement(db_->GetCachedStatement(
    193       SQL_FROM_HERE,
    194       "INSERT OR REPLACE INTO thumbnails "
    195       "(url, url_rank, title, thumbnail, redirects, "
    196       "boring_score, good_clipping, at_top, last_updated) "
    197       "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"));
    198   if (!statement)
    199     return;
    200 
    201   statement.BindString(0, url.url.spec());
    202   statement.BindInt(1, count);  // Make it the last url.
    203   statement.BindString16(2, url.title);
    204   if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) {
    205     statement.BindBlob(3, thumbnail.thumbnail->front(),
    206                        static_cast<int>(thumbnail.thumbnail->size()));
    207   }
    208   statement.BindString(4, GetRedirects(url));
    209   const ThumbnailScore& score = thumbnail.thumbnail_score;
    210   statement.BindDouble(5, score.boring_score);
    211   statement.BindBool(6, score.good_clipping);
    212   statement.BindBool(7, score.at_top);
    213   statement.BindInt64(8, score.time_at_snapshot.ToInternalValue());
    214   if (!statement.Run())
    215     NOTREACHED() << db_->GetErrorMessage();
    216 
    217   UpdatePageRankNoTransaction(url, new_rank);
    218 }
    219 
    220 void TopSitesDatabase::UpdatePageRank(const MostVisitedURL& url,
    221                                           int new_rank) {
    222   sql::Transaction transaction(db_.get());
    223   transaction.Begin();
    224   UpdatePageRankNoTransaction(url, new_rank);
    225   transaction.Commit();
    226 }
    227 
    228 // Caller should have a transaction open.
    229 void TopSitesDatabase::UpdatePageRankNoTransaction(
    230     const MostVisitedURL& url, int new_rank) {
    231   int prev_rank = GetURLRank(url);
    232   if (prev_rank == -1) {
    233     LOG(WARNING) << "Updating rank of an unknown URL: " << url.url.spec();
    234     return;
    235   }
    236 
    237   // Shift the ranks.
    238   if (prev_rank > new_rank) {
    239     // Shift up
    240     sql::Statement shift_statement(db_->GetCachedStatement(
    241         SQL_FROM_HERE,
    242         "UPDATE thumbnails "
    243         "SET url_rank = url_rank + 1 "
    244         "WHERE url_rank >= ? AND url_rank < ?"));
    245     shift_statement.BindInt(0, new_rank);
    246     shift_statement.BindInt(1, prev_rank);
    247     if (shift_statement)
    248       shift_statement.Run();
    249   } else if (prev_rank < new_rank) {
    250     // Shift down
    251     sql::Statement shift_statement(db_->GetCachedStatement(
    252         SQL_FROM_HERE,
    253         "UPDATE thumbnails "
    254         "SET url_rank = url_rank - 1 "
    255         "WHERE url_rank > ? AND url_rank <= ?"));
    256     shift_statement.BindInt(0, prev_rank);
    257     shift_statement.BindInt(1, new_rank);
    258     if (shift_statement)
    259       shift_statement.Run();
    260   }
    261 
    262   // Set the url's rank.
    263   sql::Statement set_statement(db_->GetCachedStatement(
    264       SQL_FROM_HERE,
    265       "UPDATE thumbnails "
    266       "SET url_rank = ? "
    267       "WHERE url == ?"));
    268   set_statement.BindInt(0, new_rank);
    269   set_statement.BindString(1, url.url.spec());
    270   if (set_statement)
    271     set_statement.Run();
    272 }
    273 
    274 bool TopSitesDatabase::GetPageThumbnail(const GURL& url,
    275                                             Images* thumbnail) {
    276   sql::Statement statement(db_->GetCachedStatement(
    277       SQL_FROM_HERE,
    278       "SELECT thumbnail, boring_score, good_clipping, at_top, last_updated "
    279       "FROM thumbnails WHERE url=?"));
    280 
    281   if (!statement) {
    282     LOG(WARNING) << db_->GetErrorMessage();
    283     return false;
    284   }
    285 
    286   statement.BindString(0, url.spec());
    287   if (!statement.Step())
    288     return false;
    289 
    290   std::vector<unsigned char> data;
    291   statement.ColumnBlobAsVector(0, &data);
    292   thumbnail->thumbnail = RefCountedBytes::TakeVector(&data);
    293   thumbnail->thumbnail_score.boring_score = statement.ColumnDouble(1);
    294   thumbnail->thumbnail_score.good_clipping = statement.ColumnBool(2);
    295   thumbnail->thumbnail_score.at_top = statement.ColumnBool(3);
    296   thumbnail->thumbnail_score.time_at_snapshot =
    297       base::Time::FromInternalValue(statement.ColumnInt64(4));
    298   return true;
    299 }
    300 
    301 int TopSitesDatabase::GetRowCount() {
    302   int result = 0;
    303   sql::Statement select_statement(db_->GetCachedStatement(
    304       SQL_FROM_HERE,
    305       "SELECT COUNT (url) FROM thumbnails"));
    306   if (!select_statement) {
    307     LOG(WARNING) << db_->GetErrorMessage();
    308     return result;
    309   }
    310 
    311   if (select_statement.Step())
    312     result = select_statement.ColumnInt(0);
    313 
    314   return result;
    315 }
    316 
    317 int TopSitesDatabase::GetURLRank(const MostVisitedURL& url) {
    318   int result = -1;
    319   sql::Statement select_statement(db_->GetCachedStatement(
    320       SQL_FROM_HERE,
    321       "SELECT url_rank "
    322       "FROM thumbnails WHERE url=?"));
    323   if (!select_statement) {
    324     LOG(WARNING) << db_->GetErrorMessage();
    325     return result;
    326   }
    327 
    328   select_statement.BindString(0, url.url.spec());
    329   if (select_statement.Step())
    330     result = select_statement.ColumnInt(0);
    331 
    332   return result;
    333 }
    334 
    335 // Remove the record for this URL. Returns true iff removed successfully.
    336 bool TopSitesDatabase::RemoveURL(const MostVisitedURL& url) {
    337   int old_rank = GetURLRank(url);
    338   if (old_rank < 0)
    339     return false;
    340 
    341   sql::Transaction transaction(db_.get());
    342   transaction.Begin();
    343   // Decrement all following ranks.
    344   sql::Statement shift_statement(db_->GetCachedStatement(
    345       SQL_FROM_HERE,
    346       "UPDATE thumbnails "
    347       "SET url_rank = url_rank - 1 "
    348       "WHERE url_rank > ?"));
    349   if (!shift_statement)
    350     return false;
    351   shift_statement.BindInt(0, old_rank);
    352   shift_statement.Run();
    353 
    354   sql::Statement delete_statement(
    355       db_->GetCachedStatement(SQL_FROM_HERE,
    356                               "DELETE FROM thumbnails WHERE url = ?"));
    357   if (!delete_statement)
    358     return false;
    359   delete_statement.BindString(0, url.url.spec());
    360   delete_statement.Run();
    361 
    362   return transaction.Commit();
    363 }
    364 
    365 sql::Connection* TopSitesDatabase::CreateDB(const FilePath& db_name) {
    366   scoped_ptr<sql::Connection> db(new sql::Connection());
    367   // Settings copied from ThumbnailDatabase.
    368   db->set_error_delegate(GetErrorHandlerForThumbnailDb());
    369   db->set_page_size(4096);
    370   db->set_cache_size(32);
    371 
    372   if (!db->Open(db_name)) {
    373     LOG(ERROR) << db->GetErrorMessage();
    374     return NULL;
    375   }
    376 
    377   return db.release();
    378 }
    379 
    380 }  // namespace history
    381