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