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