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.
5 #include "chrome/browser/autocomplete/shortcuts_database.h"
10 #include "base/logging.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/time/time.h"
13 #include "components/omnibox/autocomplete_match_type.h"
14 #include "sql/meta_table.h"
15 #include "sql/statement.h"
16 #include "sql/transaction.h"
17 #include "ui/base/page_transition_types.h"
20 // Helpers --------------------------------------------------------------------
24 // Current version number. We write databases at the "current" version number,
25 // but any previous version that can read the "compatible" one can make do with
26 // our database without *too* many bad effects.
27 const int kCurrentVersionNumber
= 1;
28 const int kCompatibleVersionNumber
= 1;
30 void BindShortcutToStatement(const ShortcutsDatabase::Shortcut
& shortcut
,
32 DCHECK(base::IsValidGUID(shortcut
.id
));
33 s
->BindString(0, shortcut
.id
);
34 s
->BindString16(1, shortcut
.text
);
35 s
->BindString16(2, shortcut
.match_core
.fill_into_edit
);
36 s
->BindString(3, shortcut
.match_core
.destination_url
.spec());
37 s
->BindString16(4, shortcut
.match_core
.contents
);
38 s
->BindString(5, shortcut
.match_core
.contents_class
);
39 s
->BindString16(6, shortcut
.match_core
.description
);
40 s
->BindString(7, shortcut
.match_core
.description_class
);
41 s
->BindInt(8, shortcut
.match_core
.transition
);
42 s
->BindInt(9, shortcut
.match_core
.type
);
43 s
->BindString16(10, shortcut
.match_core
.keyword
);
44 s
->BindInt64(11, shortcut
.last_access_time
.ToInternalValue());
45 s
->BindInt(12, shortcut
.number_of_hits
);
48 bool DeleteShortcut(const char* field_name
,
49 const std::string
& id
,
50 sql::Connection
& db
) {
51 sql::Statement
s(db
.GetUniqueStatement(
52 base::StringPrintf("DELETE FROM omni_box_shortcuts WHERE %s = ?",
53 field_name
).c_str()));
60 // ShortcutsDatabase::Shortcut::MatchCore -------------------------------------
62 ShortcutsDatabase::Shortcut::MatchCore::MatchCore(
63 const base::string16
& fill_into_edit
,
64 const GURL
& destination_url
,
65 const base::string16
& contents
,
66 const std::string
& contents_class
,
67 const base::string16
& description
,
68 const std::string
& description_class
,
71 const base::string16
& keyword
)
72 : fill_into_edit(fill_into_edit
),
73 destination_url(destination_url
),
75 contents_class(contents_class
),
76 description(description
),
77 description_class(description_class
),
78 transition(transition
),
83 ShortcutsDatabase::Shortcut::MatchCore::~MatchCore() {
86 // ShortcutsDatabase::Shortcut ------------------------------------------------
88 ShortcutsDatabase::Shortcut::Shortcut(
89 const std::string
& id
,
90 const base::string16
& text
,
91 const MatchCore
& match_core
,
92 const base::Time
& last_access_time
,
96 match_core(match_core
),
97 last_access_time(last_access_time
),
98 number_of_hits(number_of_hits
) {
101 ShortcutsDatabase::Shortcut::Shortcut()
102 : match_core(base::string16(), GURL(), base::string16(), std::string(),
103 base::string16(), std::string(), 0, 0, base::string16()),
104 last_access_time(base::Time::Now()),
108 ShortcutsDatabase::Shortcut::~Shortcut() {
112 // ShortcutsDatabase ----------------------------------------------------------
114 ShortcutsDatabase::ShortcutsDatabase(const base::FilePath
& database_path
)
115 : database_path_(database_path
) {
118 bool ShortcutsDatabase::Init() {
119 db_
.set_histogram_tag("Shortcuts");
121 // Set the database page size to something a little larger to give us
122 // better performance (we're typically seek rather than bandwidth limited).
123 // This only has an effect before any tables have been created, otherwise
124 // this is a NOP. Must be a power of 2 and a max of 8192.
125 db_
.set_page_size(4096);
127 // Run the database in exclusive mode. Nobody else should be accessing the
128 // database while we're running, and this will give somewhat improved perf.
129 db_
.set_exclusive_locking();
131 // Attach the database to our index file.
132 return db_
.Open(database_path_
) && EnsureTable();
135 bool ShortcutsDatabase::AddShortcut(const Shortcut
& shortcut
) {
136 sql::Statement
s(db_
.GetCachedStatement(
138 "INSERT INTO omni_box_shortcuts (id, text, fill_into_edit, url, "
139 "contents, contents_class, description, description_class, "
140 "transition, type, keyword, last_access_time, number_of_hits) "
141 "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)"));
142 BindShortcutToStatement(shortcut
, &s
);
146 bool ShortcutsDatabase::UpdateShortcut(const Shortcut
& shortcut
) {
147 sql::Statement
s(db_
.GetCachedStatement(
149 "UPDATE omni_box_shortcuts SET id=?, text=?, fill_into_edit=?, url=?, "
150 "contents=?, contents_class=?, description=?, description_class=?, "
151 "transition=?, type=?, keyword=?, last_access_time=?, "
152 "number_of_hits=? WHERE id=?"));
153 BindShortcutToStatement(shortcut
, &s
);
154 s
.BindString(13, shortcut
.id
);
158 bool ShortcutsDatabase::DeleteShortcutsWithIDs(
159 const ShortcutIDs
& shortcut_ids
) {
161 db_
.BeginTransaction();
162 for (ShortcutIDs::const_iterator
it(shortcut_ids
.begin());
163 it
!= shortcut_ids
.end(); ++it
) {
164 success
&= DeleteShortcut("id", *it
, db_
);
166 db_
.CommitTransaction();
170 bool ShortcutsDatabase::DeleteShortcutsWithURL(
171 const std::string
& shortcut_url_spec
) {
172 return DeleteShortcut("url", shortcut_url_spec
, db_
);
175 bool ShortcutsDatabase::DeleteAllShortcuts() {
176 if (!db_
.Execute("DELETE FROM omni_box_shortcuts"))
179 ignore_result(db_
.Execute("VACUUM"));
183 void ShortcutsDatabase::LoadShortcuts(GuidToShortcutMap
* shortcuts
) {
185 sql::Statement
s(db_
.GetCachedStatement(
187 "SELECT id, text, fill_into_edit, url, contents, contents_class, "
188 "description, description_class, transition, type, keyword, "
189 "last_access_time, number_of_hits FROM omni_box_shortcuts"));
193 shortcuts
->insert(std::make_pair(
196 s
.ColumnString(0), // id
197 s
.ColumnString16(1), // text
199 s
.ColumnString16(2), // fill_into_edit
200 GURL(s
.ColumnString(3)), // destination_url
201 s
.ColumnString16(4), // contents
202 s
.ColumnString(5), // contents_class
203 s
.ColumnString16(6), // description
204 s
.ColumnString(7), // description_class
205 s
.ColumnInt(8), // transition
206 s
.ColumnInt(9), // type
207 s
.ColumnString16(10)), // keyword
208 base::Time::FromInternalValue(s
.ColumnInt64(11)),
210 s
.ColumnInt(12)))); // number_of_hits
214 ShortcutsDatabase::~ShortcutsDatabase() {
217 bool ShortcutsDatabase::EnsureTable() {
218 if (!db_
.DoesTableExist("omni_box_shortcuts")) {
220 "CREATE TABLE omni_box_shortcuts (id VARCHAR PRIMARY KEY, "
221 "text VARCHAR, fill_into_edit VARCHAR, url VARCHAR, "
222 "contents VARCHAR, contents_class VARCHAR, description VARCHAR, "
223 "description_class VARCHAR, transition INTEGER, type INTEGER, "
224 "keyword VARCHAR, last_access_time INTEGER, "
225 "number_of_hits INTEGER)");
228 // The first version of the shortcuts table lacked the fill_into_edit,
229 // transition, type, and keyword columns.
230 if (!db_
.DoesColumnExist("omni_box_shortcuts", "fill_into_edit")) {
231 // Perform the upgrade in a transaction to ensure it doesn't happen
233 sql::Transaction
transaction(&db_
);
234 if (!(transaction
.Begin() &&
235 db_
.Execute("ALTER TABLE omni_box_shortcuts "
236 "ADD COLUMN fill_into_edit VARCHAR") &&
237 db_
.Execute("UPDATE omni_box_shortcuts SET fill_into_edit = url") &&
238 db_
.Execute("ALTER TABLE omni_box_shortcuts "
239 "ADD COLUMN transition INTEGER") &&
240 db_
.Execute(base::StringPrintf(
241 "UPDATE omni_box_shortcuts SET transition = %d",
242 static_cast<int>(ui::PAGE_TRANSITION_TYPED
)).c_str()) &&
243 db_
.Execute("ALTER TABLE omni_box_shortcuts ADD COLUMN type INTEGER") &&
244 db_
.Execute(base::StringPrintf(
245 "UPDATE omni_box_shortcuts SET type = %d",
246 static_cast<int>(AutocompleteMatchType::HISTORY_TITLE
)).c_str()) &&
247 db_
.Execute("ALTER TABLE omni_box_shortcuts "
248 "ADD COLUMN keyword VARCHAR") &&
249 transaction
.Commit())) {
254 if (!sql::MetaTable::DoesTableExist(&db_
)) {
255 meta_table_
.Init(&db_
, kCurrentVersionNumber
, kCompatibleVersionNumber
);
256 sql::Transaction
transaction(&db_
);
257 if (!(transaction
.Begin() &&
258 // Migrate old SEARCH_OTHER_ENGINE values to the new type value.
259 db_
.Execute(base::StringPrintf("UPDATE omni_box_shortcuts "
260 "SET type = 13 WHERE type = 9").c_str()) &&
261 // Migrate old EXTENSION_APP values to the new type value.
262 db_
.Execute(base::StringPrintf("UPDATE omni_box_shortcuts "
263 "SET type = 14 WHERE type = 10").c_str()) &&
264 // Migrate old CONTACT values to the new type value.
265 db_
.Execute(base::StringPrintf("UPDATE omni_box_shortcuts "
266 "SET type = 15 WHERE type = 11").c_str()) &&
267 // Migrate old BOOKMARK_TITLE values to the new type value.
268 db_
.Execute(base::StringPrintf("UPDATE omni_box_shortcuts "
269 "SET type = 16 WHERE type = 12").c_str()) &&
270 transaction
.Commit())) {