1 // Copyright 2014 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 "components/history/core/browser/in_memory_database.h"
6
7 #include "base/files/file_path.h"
8 #include "base/logging.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/time/time.h"
12 #include "build/build_config.h"
13
14 namespace history {
15
InMemoryDatabase()16 InMemoryDatabase::InMemoryDatabase() : URLDatabase() {
17 }
18
~InMemoryDatabase()19 InMemoryDatabase::~InMemoryDatabase() {
20 }
21
InitDB()22 bool InMemoryDatabase::InitDB() {
23 // Set the database page size to 4K for better performance.
24 db_.set_page_size(4096);
25
26 if (!db_.OpenInMemory()) {
27 NOTREACHED() << "Cannot open databse " << GetDB().GetErrorMessage();
28 return false;
29 }
30
31 // No reason to leave data behind in memory when rows are removed.
32 ignore_result(db_.Execute("PRAGMA auto_vacuum=1"));
33
34 // Ensure this is really an in-memory-only cache.
35 ignore_result(db_.Execute("PRAGMA temp_store=MEMORY"));
36
37 // Create the URL table, but leave it empty for now.
38 if (!CreateURLTable(false)) {
39 NOTREACHED() << "Unable to create table";
40 db_.Close();
41 return false;
42 }
43
44 // Create the keyword search terms table.
45 if (!InitKeywordSearchTermsTable()) {
46 NOTREACHED() << "Unable to create keyword search terms";
47 db_.Close();
48 return false;
49 }
50
51 return true;
52 }
53
InitFromScratch()54 bool InMemoryDatabase::InitFromScratch() {
55 if (!InitDB())
56 return false;
57
58 // InitDB doesn't create the index so in the disk-loading case, it can be
59 // added afterwards.
60 CreateMainURLIndex();
61 CreateKeywordSearchTermsIndices();
62 return true;
63 }
64
InitFromDisk(const base::FilePath & history_name)65 bool InMemoryDatabase::InitFromDisk(const base::FilePath& history_name) {
66 if (!InitDB())
67 return false;
68
69 // Attach to the history database on disk. (We can't ATTACH in the middle of
70 // a transaction.)
71 sql::Statement attach(GetDB().GetUniqueStatement("ATTACH ? AS history"));
72 #if defined(OS_POSIX)
73 attach.BindString(0, history_name.value());
74 #else
75 attach.BindString(0, base::WideToUTF8(history_name.value()));
76 #endif
77 if (!attach.Run())
78 return false;
79
80 // Copy URL data to memory.
81 base::TimeTicks begin_load = base::TimeTicks::Now();
82 if (!db_.Execute(
83 "INSERT INTO urls SELECT * FROM history.urls WHERE typed_count > 0")) {
84 // Unable to get data from the history database. This is OK, the file may
85 // just not exist yet.
86 }
87 base::TimeTicks end_load = base::TimeTicks::Now();
88 UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBPopulate",
89 end_load - begin_load);
90 UMA_HISTOGRAM_COUNTS("History.InMemoryDBItemCount", db_.GetLastChangeCount());
91
92 {
93 // This calculation should be fast (since it's on an in-memory DB with
94 // an average of only 35 rows).
95 sql::Statement visit_count(db_.GetUniqueStatement(
96 "SELECT sum(visit_count) FROM urls"));
97 if (visit_count.Step()) {
98 UMA_HISTOGRAM_COUNTS("History.InMemoryTypedUrlVisitCount",
99 visit_count.ColumnInt(0));
100 }
101 }
102
103 // Insert keyword search related URLs.
104 begin_load = base::TimeTicks::Now();
105 if (!db_.Execute(
106 "INSERT OR IGNORE INTO urls SELECT u.id, u.url, u.title, u.visit_count, "
107 "u.typed_count, u.last_visit_time, u.hidden, u.favicon_id "
108 "FROM history.urls u JOIN history.keyword_search_terms kst "
109 "WHERE u.typed_count = 0 AND u.id = kst.url_id")) {
110 // Unable to get data from the history database. This is OK, the file may
111 // just not exist yet.
112 }
113 end_load = base::TimeTicks::Now();
114 UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBKeywordURLPopulate",
115 end_load - begin_load);
116 UMA_HISTOGRAM_COUNTS("History.InMemoryDBKeywordURLItemCount",
117 db_.GetLastChangeCount());
118
119 // Copy search terms to memory.
120 begin_load = base::TimeTicks::Now();
121 if (!db_.Execute(
122 "INSERT INTO keyword_search_terms SELECT * FROM "
123 "history.keyword_search_terms")) {
124 // Unable to get data from the history database. This is OK, the file may
125 // just not exist yet.
126 }
127 end_load = base::TimeTicks::Now();
128 UMA_HISTOGRAM_MEDIUM_TIMES("History.InMemoryDBKeywordTermsPopulate",
129 end_load - begin_load);
130 UMA_HISTOGRAM_COUNTS("History.InMemoryDBKeywordTermsCount",
131 db_.GetLastChangeCount());
132
133 // Detach from the history database on disk.
134 if (!db_.Execute("DETACH history")) {
135 NOTREACHED() << "Unable to detach from history database.";
136 return false;
137 }
138
139 // Index the table, this is faster than creating the index first and then
140 // inserting into it.
141 CreateMainURLIndex();
142 CreateKeywordSearchTermsIndices();
143
144 return true;
145 }
146
GetDB()147 sql::Connection& InMemoryDatabase::GetDB() {
148 return db_;
149 }
150
151 } // namespace history
152