15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/file_util.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/ref_counted.h" 7a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "base/metrics/histogram.h" 82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/string_split.h" 9868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h" 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/history/history_types.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/history/top_sites.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/history/top_sites_database.h" 131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#include "chrome/common/thumbnail_score.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sql/connection.h" 15a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "sql/recovery.h" 16a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "sql/statement.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "sql/transaction.h" 18a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "third_party/sqlite/sqlite3.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// Description of database table: 211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// 221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// thumbnails 231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// url URL of the sites for which we have a thumbnail. 241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// url_rank Index of the URL in that thumbnail, 0-based. The thumbnail 251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// with the highest rank will be the next one evicted. Forced 261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// thumbnails have a rank of -1. 271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// title The title to display under that thumbnail. 281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// redirects A space separated list of URLs that are known to redirect 291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// to this url. 301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// boring_score How "boring" that thumbnail is. See ThumbnailScore. 311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// good_clipping True if the thumbnail was clipped from the bottom, keeping 321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// the entire width of the window. See ThumbnailScore. 331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// at_top True if the thumbnail was captured at the top of the 341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// website. 351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// last_updated The time at which this thumbnail was last updated. 361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// load_completed True if the thumbnail was captured after the page load was 371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// completed. 381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// last_forced If this is a forced thumbnail, records the last time it 391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)// was forced. If it's not a forced thumbnail, 0. 401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace { 42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// For this database, schema migrations are deprecated after two 44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// years. This means that the oldest non-deprecated version should be 45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// two years old or greater (thus the migrations to get there are 46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// older). Databases containing deprecated versions will be cleared 47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// at startup. Since this database is a cache, losing old data is not 48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// fatal (in fact, very old data may be expired immediately at startup 49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// anyhow). 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 51f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Version 3: b6d6a783/r231648 by beaudoin@chromium.org on 2013-10-29 5258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// Version 2: eb0b24e6/r87284 by satorux@chromium.org on 2011-05-31 53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Version 1: 809cc4d8/r64072 by sky@chromium.org on 2010-10-27 (deprecated) 5458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles) 5558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// NOTE(shess): When changing the version, add a new golden file for 5658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)// the new version and a test to verify that Init() works with it. 57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// NOTE(shess): RecoverDatabaseOrRaze() depends on the specific 58a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// version number. The code is subtle and in development, contact me 59a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// if the necessary changes are not obvious. 601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)static const int kVersionNumber = 3; 61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)static const int kDeprecatedVersionNumber = 1; // and earlier. 62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)bool InitTables(sql::Connection* db) { 64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const char kThumbnailsSql[] = 65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "CREATE TABLE IF NOT EXISTS thumbnails (" 66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "url LONGVARCHAR PRIMARY KEY," 67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "url_rank INTEGER," 68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "title LONGVARCHAR," 69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "thumbnail BLOB," 70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "redirects LONGVARCHAR," 71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "boring_score DOUBLE DEFAULT 1.0," 72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "good_clipping INTEGER DEFAULT 0," 73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "at_top INTEGER DEFAULT 0," 74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "last_updated INTEGER DEFAULT 0," 75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "load_completed INTEGER DEFAULT 0," 76f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) "last_forced INTEGER DEFAULT 0)"; 77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return db->Execute(kThumbnailsSql); 78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Encodes redirects into a string. 81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)std::string GetRedirects(const history::MostVisitedURL& url) { 82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) std::vector<std::string> redirects; 83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for (size_t i = 0; i < url.redirects.size(); i++) 84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) redirects.push_back(url.redirects[i].spec()); 85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return JoinString(redirects, ' '); 86f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// Decodes redirects from a string and sets them for the url. 89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)void SetRedirects(const std::string& redirects, history::MostVisitedURL* url) { 90f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) std::vector<std::string> redirects_vector; 91f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) base::SplitStringAlongWhitespace(redirects, &redirects_vector); 92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for (size_t i = 0; i < redirects_vector.size(); ++i) 93f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) url->redirects.push_back(GURL(redirects_vector[i])); 94f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} 95f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 96a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Track various failure (and success) cases in recovery code. 97a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// 98a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// TODO(shess): The recovery code is complete, but by nature runs in challenging 99a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// circumstances, so initially the default error response is to leave the 100a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// existing database in place. This histogram is intended to expose the 101a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// failures seen in the fleet. Frequent failure cases can be explored more 102a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// deeply to see if the complexity to fix them is warranted. Infrequent failure 103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// cases can be resolved by marking the database unrecoverable (which will 104a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// delete the data). 105a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// 106a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Based on the thumbnail_database.cc recovery code, FAILED_SCOPER should 107a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// dominate, followed distantly by FAILED_META, with few or no other failures. 108a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)enum RecoveryEventType { 109a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Database successfully recovered. 110a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_RECOVERED = 0, 111a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 112a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Database successfully deprecated. 113a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_DEPRECATED, 114a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 115a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Sqlite.RecoveryEvent can usually be used to get more detail about the 116a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // specific failure (see sql/recovery.cc). 117a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_FAILED_SCOPER, 118a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_FAILED_META_VERSION, 119a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_FAILED_META_WRONG_VERSION, 120a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_FAILED_META_INIT, 121a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_FAILED_SCHEMA_INIT, 122a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_FAILED_AUTORECOVER_THUMBNAILS, 123a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_FAILED_COMMIT, 124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 125a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Track invariants resolved by FixThumbnailsTable(). 126a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_INVARIANT_RANK, 127a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_INVARIANT_REDIRECT, 128a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_INVARIANT_CONTIGUOUS, 129a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 130a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Always keep this at the end. 131a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RECOVERY_EVENT_MAX, 132a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}; 133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void RecordRecoveryEvent(RecoveryEventType recovery_event) { 135a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) UMA_HISTOGRAM_ENUMERATION("History.TopSitesRecovery", 136a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) recovery_event, RECOVERY_EVENT_MAX); 137a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)} 138a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Most corruption comes down to atomic updates between pages being broken 140a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// somehow. This can result in either missing data, or overlapping data, 141a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// depending on the operation broken. This table has large rows, which will use 142a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// overflow pages, so it is possible (though unlikely) that a chain could fit 143a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// together and yield a row with errors. 144a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void FixThumbnailsTable(sql::Connection* db) { 145a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Enforce invariant separating forced and non-forced thumbnails. 146a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const char kFixRankSql[] = 147a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) "DELETE FROM thumbnails " 148a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) "WHERE (url_rank = -1 AND last_forced = 0) " 149a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) "OR (url_rank <> -1 AND last_forced <> 0)"; 150a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) ignore_result(db->Execute(kFixRankSql)); 151a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (db->GetLastChangeCount() > 0) 152a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecordRecoveryEvent(RECOVERY_EVENT_INVARIANT_RANK); 153a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 154a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Enforce invariant that url is in its own redirects. 155a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const char kFixRedirectsSql[] = 156a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) "DELETE FROM thumbnails " 157a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) "WHERE url <> substr(redirects, -length(url), length(url))"; 158a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) ignore_result(db->Execute(kFixRedirectsSql)); 159a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (db->GetLastChangeCount() > 0) 160a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecordRecoveryEvent(RECOVERY_EVENT_INVARIANT_REDIRECT); 161a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 162a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Enforce invariant that url_rank>=0 forms a contiguous series. 163a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // TODO(shess): I have not found an UPDATE+SUBSELECT method of managing this. 164a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // It can be done with a temporary table and a subselect, but doing it 165a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // manually is easier to follow. Another option would be to somehow integrate 166a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // the renumbering into the table recovery code. 167a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const char kByRankSql[] = 168a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) "SELECT url_rank, rowid FROM thumbnails WHERE url_rank <> -1 " 169a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) "ORDER BY url_rank"; 170a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) sql::Statement select_statement(db->GetUniqueStatement(kByRankSql)); 171a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 172a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const char kAdjustRankSql[] = 173a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) "UPDATE thumbnails SET url_rank = ? WHERE rowid = ?"; 174a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) sql::Statement update_statement(db->GetUniqueStatement(kAdjustRankSql)); 175a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 176a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Update any rows where |next_rank| doesn't match |url_rank|. 177a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int next_rank = 0; 178a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) bool adjusted = false; 179a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) while (select_statement.Step()) { 180a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const int url_rank = select_statement.ColumnInt(0); 181a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (url_rank != next_rank) { 182a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) adjusted = true; 183a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) update_statement.Reset(true); 184a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) update_statement.BindInt(0, next_rank); 185a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) update_statement.BindInt64(1, select_statement.ColumnInt64(1)); 186a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) update_statement.Run(); 187a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 188a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) ++next_rank; 189a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 190a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (adjusted) 191a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecordRecoveryEvent(RECOVERY_EVENT_INVARIANT_CONTIGUOUS); 192a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)} 193a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 194a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// Recover the database to the extent possible, razing it if recovery is not 195a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// possible. 196a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void RecoverDatabaseOrRaze(sql::Connection* db, const base::FilePath& db_path) { 197a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // NOTE(shess): If the version changes, review this code. 198a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) DCHECK_EQ(3, kVersionNumber); 199a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 200a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // It is almost certain that some operation against |db| will fail, prevent 201a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // reentry. 202a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) db->reset_error_callback(); 203a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 204a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // For generating histogram stats. 205a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) size_t thumbnails_recovered = 0; 206a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int64 original_size = 0; 207a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::GetFileSize(db_path, &original_size); 208a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 209a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); 210a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (!recovery) { 211a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecordRecoveryEvent(RECOVERY_EVENT_FAILED_SCOPER); 212a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return; 213a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 214a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 215a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Setup the meta recovery table and fetch the version number from the corrupt 216a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // database. 217a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int version = 0; 218a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (!recovery->SetupMeta() || !recovery->GetMetaVersionNumber(&version)) { 219a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // TODO(shess): Prior histograms indicate all failures are in creating the 220a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // recover virtual table for corrupt.meta. The table may not exist, or the 221a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // database may be too far gone. Either way, unclear how to resolve. 222a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) sql::Recovery::Rollback(recovery.Pass()); 223a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_VERSION); 224a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return; 225a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 226a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 227a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // This code runs in a context which may be able to read version information 228a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // that the regular deprecation path cannot. The effect of this code will be 229a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // to raze the database. 230a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (version <= kDeprecatedVersionNumber) { 231a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) sql::Recovery::Unrecoverable(recovery.Pass()); 232a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecordRecoveryEvent(RECOVERY_EVENT_DEPRECATED); 233a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return; 234a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 235a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 236a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // TODO(shess): Earlier versions have been deprecated, later versions should 237a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // be impossible. Unrecoverable() seems like a feasible response if this is 238a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // infrequent enough. 239a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (version != 2 && version != 3) { 240a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_WRONG_VERSION); 241a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) sql::Recovery::Rollback(recovery.Pass()); 242a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return; 243a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 244a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 245a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Both v2 and v3 recover to current schema version. 246a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) sql::MetaTable recover_meta_table; 247a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (!recover_meta_table.Init(recovery->db(), kVersionNumber, 248a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) kVersionNumber)) { 249a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) sql::Recovery::Rollback(recovery.Pass()); 250a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_INIT); 251a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return; 252a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 253a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 254a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Create a fresh version of the schema. The recovery code uses 255a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // conflict-resolution to handle duplicates, so any indices are necessary. 256a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (!InitTables(recovery->db())) { 257a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // TODO(shess): Unable to create the new schema in the new database. The 258a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // new database should be a temporary file, so being unable to work with it 259a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // is pretty unclear. 260a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // 261a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // What are the potential responses, even? The recovery database could be 262a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // opened as in-memory. If the temp database had a filesystem problem and 263a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // the temp filesystem differs from the main database, then that could fix 264a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // it. 265a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) sql::Recovery::Rollback(recovery.Pass()); 266a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecordRecoveryEvent(RECOVERY_EVENT_FAILED_SCHEMA_INIT); 267a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return; 268a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 269a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 270a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // The |1| is because v2 [thumbnails] has one less column than v3 did. In the 271a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // v2 case the column will get default values. 272a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (!recovery->AutoRecoverTable("thumbnails", 1, &thumbnails_recovered)) { 273a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) sql::Recovery::Rollback(recovery.Pass()); 274a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_THUMBNAILS); 275a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return; 276a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 277a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 278a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // TODO(shess): Inline this? 279a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) FixThumbnailsTable(recovery->db()); 280a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 281a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (!sql::Recovery::Recovered(recovery.Pass())) { 282a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // TODO(shess): Very unclear what this failure would actually mean, and what 283a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // should be done. Add histograms to Recovered() implementation to get some 284a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // insight. 285a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecordRecoveryEvent(RECOVERY_EVENT_FAILED_COMMIT); 286a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return; 287a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 288a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 289a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Track the size of the recovered database relative to the size of the input 290a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // database. The size should almost always be smaller, unless the input 291a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // database was empty to start with. If the percentage results are very low, 292a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // something is awry. 293a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int64 final_size = 0; 294a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (original_size > 0 && 295a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) base::GetFileSize(db_path, &final_size) && 296a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) final_size > 0) { 297a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) UMA_HISTOGRAM_PERCENTAGE("History.TopSitesRecoveredPercentage", 298d57369da7c6519fef57db42085f7b42d4c8845c1Torne (Richard Coles) final_size * 100 / original_size); 299a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 300a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 301a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Using 10,000 because these cases mostly care about "none recovered" and 302a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // "lots recovered". More than 10,000 rows recovered probably means there's 303a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // something wrong with the profile. 304a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) UMA_HISTOGRAM_COUNTS_10000("History.TopSitesRecoveredRowsThumbnails", 305a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) thumbnails_recovered); 306a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 307a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecordRecoveryEvent(RECOVERY_EVENT_RECOVERED); 308a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)} 309a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 310a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void DatabaseErrorCallback(sql::Connection* db, 311a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const base::FilePath& db_path, 312a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int extended_error, 313a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) sql::Statement* stmt) { 314a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // TODO(shess): Assert that this is running on a safe thread. AFAICT, should 315a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // be the history thread, but at this level I can't see how to reach that. 316a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 317a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Attempt to recover corrupt databases. 318a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) int error = (extended_error & 0xFF); 319a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (error == SQLITE_CORRUPT || 320a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) error == SQLITE_CANTOPEN || 321a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) error == SQLITE_NOTADB) { 322a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) RecoverDatabaseOrRaze(db, db_path); 323a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 324a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 325a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // TODO(shess): This database's error histograms look like: 326a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // 84% SQLITE_CORRUPT, SQLITE_CANTOPEN, SQLITE_NOTADB 327a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // 7% SQLITE_ERROR 328a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // 6% SQLITE_IOERR variants 329a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // 2% SQLITE_READONLY 330a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // .4% SQLITE_FULL 331a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // nominal SQLITE_TOBIG, SQLITE_AUTH, and SQLITE_BUSY. In the case of 332a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // thumbnail_database.cc, as soon as the recovery code landed, SQLITE_IOERR 333a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // shot to leadership. If the I/O error is system-level, there is probably no 334a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // hope, but if it is restricted to something about the database file, it is 335a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // possible that the recovery code could be brought to bear. In fact, it is 336a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // possible that running recovery would be a reasonable default when errors 337a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // are seen. 338a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 339a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // The default handling is to assert on debug and to ignore on release. 340a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (!sql::Connection::ShouldIgnoreSqliteError(extended_error)) 341a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) DLOG(FATAL) << db->GetErrorMessage(); 342a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)} 343a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 344f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)} // namespace 345f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 346f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)namespace history { 347f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 348f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)// static 349f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int TopSitesDatabase::kRankOfForcedURL = -1; 350f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 351a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)// static 352f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)const int TopSitesDatabase::kRankOfNonExistingURL = -2; 3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)TopSitesDatabase::TopSitesDatabase() { 3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)TopSitesDatabase::~TopSitesDatabase() { 3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool TopSitesDatabase::Init(const base::FilePath& db_name) { 361a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Retry failed InitImpl() in case the recovery system fixed things. 362a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // TODO(shess): Instrument to figure out if there are any persistent failure 363a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // cases which do not resolve themselves. 364a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) const size_t kAttempts = 2; 365a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 366a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) for (size_t i = 0; i < kAttempts; ++i) { 367a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) if (InitImpl(db_name)) 368a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return true; 369a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 370a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) meta_table_.Reset(); 371a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) db_.reset(); 372a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) } 373a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return false; 374a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)} 375a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 376a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)bool TopSitesDatabase::InitImpl(const base::FilePath& db_name) { 377f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const bool file_existed = base::PathExists(db_name); 3785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) db_.reset(CreateDB(db_name)); 380b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles) if (!db_) 3815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 383f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // An older version had data with no meta table. Deprecate by razing. 384f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // TODO(shess): Just have RazeIfDeprecated() handle this case. 385f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) const bool does_meta_exist = sql::MetaTable::DoesTableExist(db_.get()); 3865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!does_meta_exist && file_existed) { 387f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!db_->Raze()) 3885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 3895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 3905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 391f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // Clear databases which are too old to process. 392f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK_LT(kDeprecatedVersionNumber, kVersionNumber); 393f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) sql::MetaTable::RazeIfDeprecated(db_.get(), kDeprecatedVersionNumber); 394f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 3955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Scope initialization in a transaction so we can't be partially 3965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // initialized. 3975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sql::Transaction transaction(db_.get()); 398f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) // TODO(shess): Failure to open transaction is bad, address it. 399f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!transaction.Begin()) 400f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return false; 4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!meta_table_.Init(db_.get(), kVersionNumber, kVersionNumber)) 4035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 4045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 405f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!InitTables(db_.get())) 4065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 4075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (meta_table_.GetVersionNumber() == 2) { 4091e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (!UpgradeToVersion3()) { 4101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) LOG(WARNING) << "Unable to upgrade top sites database to version 3."; 4111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return false; 4121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 4131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 4141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 4155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Version check. 4165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (meta_table_.GetVersionNumber() != kVersionNumber) 4175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 4185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Initialization is complete. 4205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!transaction.Commit()) 4215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 4225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 4245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)bool TopSitesDatabase::UpgradeToVersion3() { 4271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Add 'last_forced' column. 4281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (!db_->Execute( 4291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "ALTER TABLE thumbnails ADD last_forced INTEGER DEFAULT 0")) { 4301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) NOTREACHED(); 4311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return false; 4321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 4331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) meta_table_.SetVersionNumber(3); 4341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return true; 4351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)} 4361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TopSitesDatabase::GetPageThumbnails(MostVisitedURLList* urls, 4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) URLToImagesMap* thumbnails) { 4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sql::Statement statement(db_->GetCachedStatement( 4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SQL_FROM_HERE, 4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "SELECT url, url_rank, title, thumbnail, redirects, " 4421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "boring_score, good_clipping, at_top, last_updated, load_completed, " 4431e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "last_forced FROM thumbnails ORDER BY url_rank, last_forced")); 4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!statement.is_valid()) { 4465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << db_->GetErrorMessage(); 4475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) urls->clear(); 4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thumbnails->clear(); 4525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) while (statement.Step()) { 4541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Results are sorted by url_rank. For forced thumbnails with url_rank = -1, 4551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // thumbnails are sorted by last_forced. 4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) MostVisitedURL url; 4575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) GURL gurl(statement.ColumnString(0)); 4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url.url = gurl; 4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) url.title = statement.ColumnString16(2); 4601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) url.last_forced_time = 4611e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) base::Time::FromInternalValue(statement.ColumnInt64(10)); 4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::string redirects = statement.ColumnString(4); 4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SetRedirects(redirects, &url); 4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) urls->push_back(url); 4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<unsigned char> data; 4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.ColumnBlobAsVector(3, &data); 4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Images thumbnail; 4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!data.empty()) 4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thumbnail.thumbnail = base::RefCountedBytes::TakeVector(&data); 4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thumbnail.thumbnail_score.boring_score = statement.ColumnDouble(5); 4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thumbnail.thumbnail_score.good_clipping = statement.ColumnBool(6); 4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thumbnail.thumbnail_score.at_top = statement.ColumnBool(7); 4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thumbnail.thumbnail_score.time_at_snapshot = 4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Time::FromInternalValue(statement.ColumnInt64(8)); 4765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thumbnail.thumbnail_score.load_completed = statement.ColumnBool(9); 4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) (*thumbnails)[gurl] = thumbnail; 4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TopSitesDatabase::SetPageThumbnail(const MostVisitedURL& url, 4821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) int new_rank, 4831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) const Images& thumbnail) { 4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sql::Transaction transaction(db_.get()); 4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) transaction.Begin(); 4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int rank = GetURLRank(url); 4881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (rank == kRankOfNonExistingURL) { 4895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) AddPageThumbnail(url, new_rank, thumbnail); 4905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else { 4915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UpdatePageRankNoTransaction(url, new_rank); 4925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UpdatePageThumbnail(url, thumbnail); 4935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 4945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) transaction.Commit(); 4965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 4975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 4985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TopSitesDatabase::UpdatePageThumbnail( 4995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const MostVisitedURL& url, const Images& thumbnail) { 5005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sql::Statement statement(db_->GetCachedStatement( 5015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SQL_FROM_HERE, 5025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "UPDATE thumbnails SET " 5035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "title = ?, thumbnail = ?, redirects = ?, " 5045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "boring_score = ?, good_clipping = ?, at_top = ?, last_updated = ?, " 5051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "load_completed = ?, last_forced = ?" 5065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "WHERE url = ? ")); 5075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindString16(0, url.title); 5085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) { 5095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindBlob(1, thumbnail.thumbnail->front(), 5105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<int>(thumbnail.thumbnail->size())); 5115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindString(2, GetRedirects(url)); 5135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const ThumbnailScore& score = thumbnail.thumbnail_score; 5145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindDouble(3, score.boring_score); 5155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindBool(4, score.good_clipping); 5165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindBool(5, score.at_top); 5175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindInt64(6, score.time_at_snapshot.ToInternalValue()); 5185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindBool(7, score.load_completed); 5191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) statement.BindInt64(8, url.last_forced_time.ToInternalValue()); 5201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) statement.BindString(9, url.url.spec()); 5215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return statement.Run(); 5235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TopSitesDatabase::AddPageThumbnail(const MostVisitedURL& url, 5261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) int new_rank, 5271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) const Images& thumbnail) { 5285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sql::Statement statement(db_->GetCachedStatement( 5295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SQL_FROM_HERE, 5305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "INSERT OR REPLACE INTO thumbnails " 5315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "(url, url_rank, title, thumbnail, redirects, " 5321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "boring_score, good_clipping, at_top, last_updated, load_completed, " 5331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "last_forced) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); 5345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindString(0, url.url.spec()); 5351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) statement.BindInt(1, kRankOfForcedURL); // Fist make it a forced thumbnail. 5365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindString16(2, url.title); 5375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (thumbnail.thumbnail.get() && thumbnail.thumbnail->front()) { 5385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindBlob(3, thumbnail.thumbnail->front(), 5395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) static_cast<int>(thumbnail.thumbnail->size())); 5405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindString(4, GetRedirects(url)); 5425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const ThumbnailScore& score = thumbnail.thumbnail_score; 5435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindDouble(5, score.boring_score); 5445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindBool(6, score.good_clipping); 5455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindBool(7, score.at_top); 5465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindInt64(8, score.time_at_snapshot.ToInternalValue()); 5475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindBool(9, score.load_completed); 5481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) int64 last_forced = url.last_forced_time.ToInternalValue(); 5491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) DCHECK((last_forced == 0) == (new_rank != kRankOfForcedURL)) 5501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) << "Thumbnail without a forced time stamp has a forced rank, or the " 5511e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) << "opposite."; 5521e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) statement.BindInt64(10, last_forced); 5535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!statement.Run()) 5545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 5555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Update rank if this is not a forced thumbnail. 5571e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (new_rank != kRankOfForcedURL) 5581e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) UpdatePageRankNoTransaction(url, new_rank); 5595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TopSitesDatabase::UpdatePageRank(const MostVisitedURL& url, 5621e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) int new_rank) { 5631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) DCHECK((url.last_forced_time.ToInternalValue() == 0) == 5641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) (new_rank != kRankOfForcedURL)) 5651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) << "Thumbnail without a forced time stamp has a forced rank, or the " 5661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) << "opposite."; 5675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sql::Transaction transaction(db_.get()); 5685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) transaction.Begin(); 5695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UpdatePageRankNoTransaction(url, new_rank); 5705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) transaction.Commit(); 5715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 5725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Caller should have a transaction open. 5745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void TopSitesDatabase::UpdatePageRankNoTransaction( 5755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const MostVisitedURL& url, int new_rank) { 5763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) DCHECK_GT(db_->transaction_nesting(), 0); 577f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) DCHECK((url.last_forced_time.is_null()) == (new_rank != kRankOfForcedURL)) 578f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) << "Thumbnail without a forced time stamp has a forced rank, or the " 579f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) << "opposite."; 5801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) 5815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int prev_rank = GetURLRank(url); 5821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (prev_rank == kRankOfNonExistingURL) { 5835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) LOG(WARNING) << "Updating rank of an unknown URL: " << url.url.spec(); 5845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 5855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 5865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 5875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Shift the ranks. 5885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (prev_rank > new_rank) { 5891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (new_rank == kRankOfForcedURL) { 5901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // From non-forced to forced, shift down. 5911e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Example: 2 -> -1 5921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // -1, -1, -1, 0, 1, [2 -> -1], [3 -> 2], [4 -> 3] 5931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) sql::Statement shift_statement(db_->GetCachedStatement( 5941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) SQL_FROM_HERE, 5951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "UPDATE thumbnails " 5961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "SET url_rank = url_rank - 1 " 5971e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "WHERE url_rank > ?")); 5981e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) shift_statement.BindInt(0, prev_rank); 5991e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) shift_statement.Run(); 6001e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } else { 6011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // From non-forced to non-forced, shift up. 6021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Example: 3 -> 1 6031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // -1, -1, -1, 0, [1 -> 2], [2 -> 3], [3 -> 1], 4 6041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) sql::Statement shift_statement(db_->GetCachedStatement( 6051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) SQL_FROM_HERE, 6061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "UPDATE thumbnails " 6071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "SET url_rank = url_rank + 1 " 6081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "WHERE url_rank >= ? AND url_rank < ?")); 6091e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) shift_statement.BindInt(0, new_rank); 6101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) shift_statement.BindInt(1, prev_rank); 6111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) shift_statement.Run(); 6121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 6135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } else if (prev_rank < new_rank) { 6141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (prev_rank == kRankOfForcedURL) { 6151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // From non-forced to forced, shift up. 6161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Example: -1 -> 2 6171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // -1, [-1 -> 2], -1, 0, 1, [2 -> 3], [3 -> 4], [4 -> 5] 6181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) sql::Statement shift_statement(db_->GetCachedStatement( 6191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) SQL_FROM_HERE, 6201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "UPDATE thumbnails " 6211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "SET url_rank = url_rank + 1 " 6221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "WHERE url_rank >= ?")); 6231e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) shift_statement.BindInt(0, new_rank); 6241e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) shift_statement.Run(); 6251e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } else { 6261e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // From non-forced to non-forced, shift down. 6271e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Example: 1 -> 3. 6281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // -1, -1, -1, 0, [1 -> 3], [2 -> 1], [3 -> 2], 4 6291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) sql::Statement shift_statement(db_->GetCachedStatement( 6301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) SQL_FROM_HERE, 6311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "UPDATE thumbnails " 6321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "SET url_rank = url_rank - 1 " 6331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "WHERE url_rank > ? AND url_rank <= ?")); 6341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) shift_statement.BindInt(0, prev_rank); 6351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) shift_statement.BindInt(1, new_rank); 6361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) shift_statement.Run(); 6371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 6385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 6395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Set the url's rank and last_forced, since the latter changes when a URL 6411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // goes from forced to non-forced and vice-versa. 6425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sql::Statement set_statement(db_->GetCachedStatement( 6435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SQL_FROM_HERE, 6445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "UPDATE thumbnails " 6451e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "SET url_rank = ?, last_forced = ? " 6465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "WHERE url == ?")); 6475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_statement.BindInt(0, new_rank); 6481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) set_statement.BindInt64(1, url.last_forced_time.ToInternalValue()); 6491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) set_statement.BindString(2, url.url.spec()); 6505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) set_statement.Run(); 6515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 6525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TopSitesDatabase::GetPageThumbnail(const GURL& url, 6543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles) Images* thumbnail) { 6555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sql::Statement statement(db_->GetCachedStatement( 6565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SQL_FROM_HERE, 6575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "SELECT thumbnail, boring_score, good_clipping, at_top, last_updated " 6585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "FROM thumbnails WHERE url=?")); 6595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.BindString(0, url.spec()); 6605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!statement.Step()) 6615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 6625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) std::vector<unsigned char> data; 6645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) statement.ColumnBlobAsVector(0, &data); 6655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thumbnail->thumbnail = base::RefCountedBytes::TakeVector(&data); 6665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thumbnail->thumbnail_score.boring_score = statement.ColumnDouble(1); 6675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thumbnail->thumbnail_score.good_clipping = statement.ColumnBool(2); 6685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thumbnail->thumbnail_score.at_top = statement.ColumnBool(3); 6695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) thumbnail->thumbnail_score.time_at_snapshot = 6705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::Time::FromInternalValue(statement.ColumnInt64(4)); 6715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 6725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 6735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int TopSitesDatabase::GetURLRank(const MostVisitedURL& url) { 6755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sql::Statement select_statement(db_->GetCachedStatement( 6765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) SQL_FROM_HERE, 6775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "SELECT url_rank " 6785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "FROM thumbnails WHERE url=?")); 6795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) select_statement.BindString(0, url.url.spec()); 6805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (select_statement.Step()) 6815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return select_statement.ColumnInt(0); 6825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return kRankOfNonExistingURL; 6845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 6855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Remove the record for this URL. Returns true iff removed successfully. 6875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool TopSitesDatabase::RemoveURL(const MostVisitedURL& url) { 6885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) int old_rank = GetURLRank(url); 6891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (old_rank == kRankOfNonExistingURL) 6905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 6915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 6925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sql::Transaction transaction(db_.get()); 6935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) transaction.Begin(); 6941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (old_rank != kRankOfForcedURL) { 6951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) // Decrement all following ranks. 6961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) sql::Statement shift_statement(db_->GetCachedStatement( 6971e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) SQL_FROM_HERE, 6981e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "UPDATE thumbnails " 6991e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "SET url_rank = url_rank - 1 " 7001e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) "WHERE url_rank > ?")); 7011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) shift_statement.BindInt(0, old_rank); 7025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) if (!shift_statement.Run()) 7041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return false; 7051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) } 7065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) sql::Statement delete_statement( 7085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) db_->GetCachedStatement(SQL_FROM_HERE, 7095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) "DELETE FROM thumbnails WHERE url = ?")); 7105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) delete_statement.BindString(0, url.url.spec()); 7115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!delete_statement.Run()) 7135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 7145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return transaction.Commit(); 7165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 7175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)sql::Connection* TopSitesDatabase::CreateDB(const base::FilePath& db_name) { 7195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) scoped_ptr<sql::Connection> db(new sql::Connection()); 7205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Settings copied from ThumbnailDatabase. 72190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles) db->set_histogram_tag("TopSites"); 722a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) db->set_error_callback(base::Bind(&DatabaseErrorCallback, 723a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) db.get(), db_name)); 7245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) db->set_page_size(4096); 7255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) db->set_cache_size(32); 7265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 727f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if (!db->Open(db_name)) 7285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return NULL; 7295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return db.release(); 7305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 7315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 7325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} // namespace history 733