Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(219)

Side by Side Diff: chrome/browser/history/history_database.cc

Issue 849323002: Componentize HistoryDatabase (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/history/history_database.h"
6
7 #include <algorithm>
8 #include <set>
9 #include <string>
10
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/metrics/histogram.h"
14 #include "base/rand_util.h"
15 #include "base/strings/string_util.h"
16 #include "base/time/time.h"
17 #include "components/history/content/browser/download_constants_utils.h"
18 #include "content/public/browser/download_interrupt_reasons.h"
19 #include "sql/transaction.h"
20
21 #if defined(OS_MACOSX)
22 #include "base/mac/mac_util.h"
23 #endif
24
25 namespace history {
26
27 namespace {
28
29 // Current version number. We write databases at the "current" version number,
30 // but any previous version that can read the "compatible" one can make do with
31 // our database without *too* many bad effects.
32 const int kCurrentVersionNumber = 29;
33 const int kCompatibleVersionNumber = 16;
34 const char kEarlyExpirationThresholdKey[] = "early_expiration_threshold";
35
36 } // namespace
37
38 HistoryDatabase::HistoryDatabase()
39 : DownloadDatabase(ToHistoryDownloadInterruptReason(
40 content::DOWNLOAD_INTERRUPT_REASON_NONE),
41 ToHistoryDownloadInterruptReason(
42 content::DOWNLOAD_INTERRUPT_REASON_CRASH)) {
43 }
44
45 HistoryDatabase::~HistoryDatabase() {
46 }
47
48 sql::InitStatus HistoryDatabase::Init(const base::FilePath& history_name) {
49 db_.set_histogram_tag("History");
50
51 // Set the exceptional sqlite error handler.
52 db_.set_error_callback(error_callback_);
53
54 // Set the database page size to something a little larger to give us
55 // better performance (we're typically seek rather than bandwidth limited).
56 // This only has an effect before any tables have been created, otherwise
57 // this is a NOP. Must be a power of 2 and a max of 8192.
58 db_.set_page_size(4096);
59
60 // Set the cache size. The page size, plus a little extra, times this
61 // value, tells us how much memory the cache will use maximum.
62 // 1000 * 4kB = 4MB
63 // TODO(brettw) scale this value to the amount of available memory.
64 db_.set_cache_size(1000);
65
66 // Note that we don't set exclusive locking here. That's done by
67 // BeginExclusiveMode below which is called later (we have to be in shared
68 // mode to start out for the in-memory backend to read the data).
69
70 if (!db_.Open(history_name))
71 return sql::INIT_FAILURE;
72
73 // Wrap the rest of init in a tranaction. This will prevent the database from
74 // getting corrupted if we crash in the middle of initialization or migration.
75 sql::Transaction committer(&db_);
76 if (!committer.Begin())
77 return sql::INIT_FAILURE;
78
79 #if defined(OS_MACOSX)
80 // Exclude the history file from backups.
81 base::mac::SetFileBackupExclusion(history_name);
82 #endif
83
84 // Prime the cache.
85 db_.Preload();
86
87 // Create the tables and indices.
88 // NOTE: If you add something here, also add it to
89 // RecreateAllButStarAndURLTables.
90 if (!meta_table_.Init(&db_, GetCurrentVersion(), kCompatibleVersionNumber))
91 return sql::INIT_FAILURE;
92 if (!CreateURLTable(false) || !InitVisitTable() ||
93 !InitKeywordSearchTermsTable() || !InitDownloadTable() ||
94 !InitSegmentTables())
95 return sql::INIT_FAILURE;
96 CreateMainURLIndex();
97 CreateKeywordSearchTermsIndices();
98
99 // TODO(benjhayden) Remove at some point.
100 meta_table_.DeleteKey("next_download_id");
101
102 // Version check.
103 sql::InitStatus version_status = EnsureCurrentVersion();
104 if (version_status != sql::INIT_OK)
105 return version_status;
106
107 return committer.Commit() ? sql::INIT_OK : sql::INIT_FAILURE;
108 }
109
110 void HistoryDatabase::ComputeDatabaseMetrics(
111 const base::FilePath& history_name) {
112 base::TimeTicks start_time = base::TimeTicks::Now();
113 int64 file_size = 0;
114 if (!base::GetFileSize(history_name, &file_size))
115 return;
116 int file_mb = static_cast<int>(file_size / (1024 * 1024));
117 UMA_HISTOGRAM_MEMORY_MB("History.DatabaseFileMB", file_mb);
118
119 sql::Statement url_count(db_.GetUniqueStatement("SELECT count(*) FROM urls"));
120 if (!url_count.Step())
121 return;
122 UMA_HISTOGRAM_COUNTS("History.URLTableCount", url_count.ColumnInt(0));
123
124 sql::Statement visit_count(db_.GetUniqueStatement(
125 "SELECT count(*) FROM visits"));
126 if (!visit_count.Step())
127 return;
128 UMA_HISTOGRAM_COUNTS("History.VisitTableCount", visit_count.ColumnInt(0));
129
130 base::Time one_week_ago = base::Time::Now() - base::TimeDelta::FromDays(7);
131 sql::Statement weekly_visit_sql(db_.GetUniqueStatement(
132 "SELECT count(*) FROM visits WHERE visit_time > ?"));
133 weekly_visit_sql.BindInt64(0, one_week_ago.ToInternalValue());
134 int weekly_visit_count = 0;
135 if (weekly_visit_sql.Step())
136 weekly_visit_count = weekly_visit_sql.ColumnInt(0);
137 UMA_HISTOGRAM_COUNTS("History.WeeklyVisitCount", weekly_visit_count);
138
139 base::Time one_month_ago = base::Time::Now() - base::TimeDelta::FromDays(30);
140 sql::Statement monthly_visit_sql(db_.GetUniqueStatement(
141 "SELECT count(*) FROM visits WHERE visit_time > ? AND visit_time <= ?"));
142 monthly_visit_sql.BindInt64(0, one_month_ago.ToInternalValue());
143 monthly_visit_sql.BindInt64(1, one_week_ago.ToInternalValue());
144 int older_visit_count = 0;
145 if (monthly_visit_sql.Step())
146 older_visit_count = monthly_visit_sql.ColumnInt(0);
147 UMA_HISTOGRAM_COUNTS("History.MonthlyVisitCount",
148 older_visit_count + weekly_visit_count);
149
150 UMA_HISTOGRAM_TIMES("History.DatabaseBasicMetricsTime",
151 base::TimeTicks::Now() - start_time);
152
153 // Compute the advanced metrics even less often, pending timing data showing
154 // that's not necessary.
155 if (base::RandInt(1, 3) == 3) {
156 start_time = base::TimeTicks::Now();
157
158 // Collect all URLs visited within the last month.
159 sql::Statement url_sql(db_.GetUniqueStatement(
160 "SELECT url, last_visit_time FROM urls WHERE last_visit_time > ?"));
161 url_sql.BindInt64(0, one_month_ago.ToInternalValue());
162
163 // Count URLs (which will always be unique) and unique hosts within the last
164 // week and last month.
165 int week_url_count = 0;
166 int month_url_count = 0;
167 std::set<std::string> week_hosts;
168 std::set<std::string> month_hosts;
169 while (url_sql.Step()) {
170 GURL url(url_sql.ColumnString(0));
171 base::Time visit_time =
172 base::Time::FromInternalValue(url_sql.ColumnInt64(1));
173 ++month_url_count;
174 month_hosts.insert(url.host());
175 if (visit_time > one_week_ago) {
176 ++week_url_count;
177 week_hosts.insert(url.host());
178 }
179 }
180 UMA_HISTOGRAM_COUNTS("History.WeeklyURLCount", week_url_count);
181 UMA_HISTOGRAM_COUNTS_10000("History.WeeklyHostCount", week_hosts.size());
182 UMA_HISTOGRAM_COUNTS("History.MonthlyURLCount", month_url_count);
183 UMA_HISTOGRAM_COUNTS_10000("History.MonthlyHostCount", month_hosts.size());
184 UMA_HISTOGRAM_TIMES("History.DatabaseAdvancedMetricsTime",
185 base::TimeTicks::Now() - start_time);
186 }
187 }
188
189 void HistoryDatabase::BeginExclusiveMode() {
190 // We can't use set_exclusive_locking() since that only has an effect before
191 // the DB is opened.
192 ignore_result(db_.Execute("PRAGMA locking_mode=EXCLUSIVE"));
193 }
194
195 // static
196 int HistoryDatabase::GetCurrentVersion() {
197 return kCurrentVersionNumber;
198 }
199
200 void HistoryDatabase::BeginTransaction() {
201 db_.BeginTransaction();
202 }
203
204 void HistoryDatabase::CommitTransaction() {
205 db_.CommitTransaction();
206 }
207
208 void HistoryDatabase::RollbackTransaction() {
209 db_.RollbackTransaction();
210 }
211
212 bool HistoryDatabase::RecreateAllTablesButURL() {
213 if (!DropVisitTable())
214 return false;
215 if (!InitVisitTable())
216 return false;
217
218 if (!DropKeywordSearchTermsTable())
219 return false;
220 if (!InitKeywordSearchTermsTable())
221 return false;
222
223 if (!DropSegmentTables())
224 return false;
225 if (!InitSegmentTables())
226 return false;
227
228 CreateKeywordSearchTermsIndices();
229 return true;
230 }
231
232 void HistoryDatabase::Vacuum() {
233 DCHECK_EQ(0, db_.transaction_nesting()) <<
234 "Can not have a transaction when vacuuming.";
235 ignore_result(db_.Execute("VACUUM"));
236 }
237
238 void HistoryDatabase::TrimMemory(bool aggressively) {
239 db_.TrimMemory(aggressively);
240 }
241
242 bool HistoryDatabase::Raze() {
243 return db_.Raze();
244 }
245
246 bool HistoryDatabase::SetSegmentID(VisitID visit_id, SegmentID segment_id) {
247 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
248 "UPDATE visits SET segment_id = ? WHERE id = ?"));
249 s.BindInt64(0, segment_id);
250 s.BindInt64(1, visit_id);
251 DCHECK(db_.GetLastChangeCount() == 1);
252
253 return s.Run();
254 }
255
256 SegmentID HistoryDatabase::GetSegmentID(VisitID visit_id) {
257 sql::Statement s(db_.GetCachedStatement(SQL_FROM_HERE,
258 "SELECT segment_id FROM visits WHERE id = ?"));
259 s.BindInt64(0, visit_id);
260
261 if (s.Step()) {
262 if (s.ColumnType(0) == sql::COLUMN_TYPE_NULL)
263 return 0;
264 else
265 return s.ColumnInt64(0);
266 }
267 return 0;
268 }
269
270 base::Time HistoryDatabase::GetEarlyExpirationThreshold() {
271 if (!cached_early_expiration_threshold_.is_null())
272 return cached_early_expiration_threshold_;
273
274 int64 threshold;
275 if (!meta_table_.GetValue(kEarlyExpirationThresholdKey, &threshold)) {
276 // Set to a very early non-zero time, so it's before all history, but not
277 // zero to avoid re-retrieval.
278 threshold = 1L;
279 }
280
281 cached_early_expiration_threshold_ = base::Time::FromInternalValue(threshold);
282 return cached_early_expiration_threshold_;
283 }
284
285 void HistoryDatabase::UpdateEarlyExpirationThreshold(base::Time threshold) {
286 meta_table_.SetValue(kEarlyExpirationThresholdKey,
287 threshold.ToInternalValue());
288 cached_early_expiration_threshold_ = threshold;
289 }
290
291 sql::Connection& HistoryDatabase::GetDB() {
292 return db_;
293 }
294
295 // Migration -------------------------------------------------------------------
296
297 sql::InitStatus HistoryDatabase::EnsureCurrentVersion() {
298 // We can't read databases newer than we were designed for.
299 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
300 LOG(WARNING) << "History database is too new.";
301 return sql::INIT_TOO_NEW;
302 }
303
304 int cur_version = meta_table_.GetVersionNumber();
305
306 // Put migration code here
307
308 if (cur_version == 15) {
309 if (!db_.Execute("DROP TABLE starred") || !DropStarredIDFromURLs()) {
310 LOG(WARNING) << "Unable to update history database to version 16.";
311 return sql::INIT_FAILURE;
312 }
313 ++cur_version;
314 meta_table_.SetVersionNumber(cur_version);
315 meta_table_.SetCompatibleVersionNumber(
316 std::min(cur_version, kCompatibleVersionNumber));
317 }
318
319 if (cur_version == 16) {
320 #if !defined(OS_WIN)
321 // In this version we bring the time format on Mac & Linux in sync with the
322 // Windows version so that profiles can be moved between computers.
323 MigrateTimeEpoch();
324 #endif
325 // On all platforms we bump the version number, so on Windows this
326 // migration is a NOP. We keep the compatible version at 16 since things
327 // will basically still work, just history will be in the future if an
328 // old version reads it.
329 ++cur_version;
330 meta_table_.SetVersionNumber(cur_version);
331 }
332
333 if (cur_version == 17) {
334 // Version 17 was for thumbnails to top sites migration. We ended up
335 // disabling it though, so 17->18 does nothing.
336 ++cur_version;
337 meta_table_.SetVersionNumber(cur_version);
338 }
339
340 if (cur_version == 18) {
341 // This is the version prior to adding url_source column. We need to
342 // migrate the database.
343 cur_version = 19;
344 meta_table_.SetVersionNumber(cur_version);
345 }
346
347 if (cur_version == 19) {
348 cur_version++;
349 meta_table_.SetVersionNumber(cur_version);
350 // This was the thumbnail migration. Obsolete.
351 }
352
353 if (cur_version == 20) {
354 // This is the version prior to adding the visit_duration field in visits
355 // database. We need to migrate the database.
356 if (!MigrateVisitsWithoutDuration()) {
357 LOG(WARNING) << "Unable to update history database to version 21.";
358 return sql::INIT_FAILURE;
359 }
360 ++cur_version;
361 meta_table_.SetVersionNumber(cur_version);
362 }
363
364 if (cur_version == 21) {
365 // The android_urls table's data schemal was changed in version 21.
366 #if defined(OS_ANDROID)
367 if (!MigrateToVersion22()) {
368 LOG(WARNING) << "Unable to migrate the android_urls table to version 22";
369 }
370 #endif
371 ++cur_version;
372 meta_table_.SetVersionNumber(cur_version);
373 }
374
375 if (cur_version == 22) {
376 if (!MigrateDownloadsState()) {
377 LOG(WARNING) << "Unable to fix invalid downloads state values";
378 // Invalid state values may cause crashes.
379 return sql::INIT_FAILURE;
380 }
381 cur_version++;
382 meta_table_.SetVersionNumber(cur_version);
383 }
384
385 if (cur_version == 23) {
386 if (!MigrateDownloadsReasonPathsAndDangerType()) {
387 LOG(WARNING) << "Unable to upgrade download interrupt reason and paths";
388 // Invalid state values may cause crashes.
389 return sql::INIT_FAILURE;
390 }
391 cur_version++;
392 meta_table_.SetVersionNumber(cur_version);
393 }
394
395 if (cur_version == 24) {
396 if (!MigratePresentationIndex()) {
397 LOG(WARNING) << "Unable to migrate history to version 25";
398 return sql::INIT_FAILURE;
399 }
400 cur_version++;
401 meta_table_.SetVersionNumber(cur_version);
402 }
403
404 if (cur_version == 25) {
405 if (!MigrateReferrer()) {
406 LOG(WARNING) << "Unable to migrate history to version 26";
407 return sql::INIT_FAILURE;
408 }
409 cur_version++;
410 meta_table_.SetVersionNumber(cur_version);
411 }
412
413 if (cur_version == 26) {
414 if (!MigrateDownloadedByExtension()) {
415 LOG(WARNING) << "Unable to migrate history to version 27";
416 return sql::INIT_FAILURE;
417 }
418 cur_version++;
419 meta_table_.SetVersionNumber(cur_version);
420 }
421
422 if (cur_version == 27) {
423 if (!MigrateDownloadValidators()) {
424 LOG(WARNING) << "Unable to migrate history to version 28";
425 return sql::INIT_FAILURE;
426 }
427 cur_version++;
428 meta_table_.SetVersionNumber(cur_version);
429 }
430
431 if (cur_version == 28) {
432 if (!MigrateMimeType()) {
433 LOG(WARNING) << "Unable to migrate history to version 29";
434 return sql::INIT_FAILURE;
435 }
436 cur_version++;
437 meta_table_.SetVersionNumber(cur_version);
438 }
439
440 // When the version is too old, we just try to continue anyway, there should
441 // not be a released product that makes a database too old for us to handle.
442 LOG_IF(WARNING, cur_version < GetCurrentVersion()) <<
443 "History database version " << cur_version << " is too old to handle.";
444
445 return sql::INIT_OK;
446 }
447
448 #if !defined(OS_WIN)
449 void HistoryDatabase::MigrateTimeEpoch() {
450 // Update all the times in the URLs and visits table in the main database.
451 ignore_result(db_.Execute(
452 "UPDATE urls "
453 "SET last_visit_time = last_visit_time + 11644473600000000 "
454 "WHERE id IN (SELECT id FROM urls WHERE last_visit_time > 0);"));
455 ignore_result(db_.Execute(
456 "UPDATE visits "
457 "SET visit_time = visit_time + 11644473600000000 "
458 "WHERE id IN (SELECT id FROM visits WHERE visit_time > 0);"));
459 ignore_result(db_.Execute(
460 "UPDATE segment_usage "
461 "SET time_slot = time_slot + 11644473600000000 "
462 "WHERE id IN (SELECT id FROM segment_usage WHERE time_slot > 0);"));
463 }
464 #endif
465
466 } // namespace history
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698