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/shortcuts_database.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/guid.h"
11 #include "base/logging.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/time/time.h"
14 #include "sql/statement.h"
15 #include "sql/transaction.h"
16
17 namespace {
18
BindShortcutToStatement(const history::ShortcutsBackend::Shortcut & shortcut,sql::Statement * s)19 void BindShortcutToStatement(
20 const history::ShortcutsBackend::Shortcut& shortcut,
21 sql::Statement* s) {
22 DCHECK(base::IsValidGUID(shortcut.id));
23 s->BindString(0, shortcut.id);
24 s->BindString16(1, shortcut.text);
25 s->BindString16(2, shortcut.match_core.fill_into_edit);
26 s->BindString(3, shortcut.match_core.destination_url.spec());
27 s->BindString16(4, shortcut.match_core.contents);
28 s->BindString(5, AutocompleteMatch::ClassificationsToString(
29 shortcut.match_core.contents_class));
30 s->BindString16(6, shortcut.match_core.description);
31 s->BindString(7, AutocompleteMatch::ClassificationsToString(
32 shortcut.match_core.description_class));
33 s->BindInt(8, shortcut.match_core.transition);
34 s->BindInt(9, shortcut.match_core.type);
35 s->BindString16(10, shortcut.match_core.keyword);
36 s->BindInt64(11, shortcut.last_access_time.ToInternalValue());
37 s->BindInt(12, shortcut.number_of_hits);
38 }
39
DeleteShortcut(const char * field_name,const std::string & id,sql::Connection & db)40 bool DeleteShortcut(const char* field_name,
41 const std::string& id,
42 sql::Connection& db) {
43 sql::Statement s(db.GetUniqueStatement(
44 base::StringPrintf("DELETE FROM omni_box_shortcuts WHERE %s = ?",
45 field_name).c_str()));
46 s.BindString(0, id);
47 return s.Run();
48 }
49
50 } // namespace
51
52 namespace history {
53
ShortcutsDatabase(const base::FilePath & database_path)54 ShortcutsDatabase::ShortcutsDatabase(const base::FilePath& database_path)
55 : database_path_(database_path) {
56 }
57
Init()58 bool ShortcutsDatabase::Init() {
59 db_.set_histogram_tag("Shortcuts");
60
61 // Set the database page size to something a little larger to give us
62 // better performance (we're typically seek rather than bandwidth limited).
63 // This only has an effect before any tables have been created, otherwise
64 // this is a NOP. Must be a power of 2 and a max of 8192.
65 db_.set_page_size(4096);
66
67 // Run the database in exclusive mode. Nobody else should be accessing the
68 // database while we're running, and this will give somewhat improved perf.
69 db_.set_exclusive_locking();
70
71 // Attach the database to our index file.
72 return db_.Open(database_path_) && EnsureTable();
73 }
74
AddShortcut(const ShortcutsBackend::Shortcut & shortcut)75 bool ShortcutsDatabase::AddShortcut(
76 const ShortcutsBackend::Shortcut& shortcut) {
77 sql::Statement s(db_.GetCachedStatement(
78 SQL_FROM_HERE,
79 "INSERT INTO omni_box_shortcuts (id, text, fill_into_edit, url, "
80 "contents, contents_class, description, description_class, "
81 "transition, type, keyword, last_access_time, number_of_hits) "
82 "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)"));
83 BindShortcutToStatement(shortcut, &s);
84 return s.Run();
85 }
86
UpdateShortcut(const ShortcutsBackend::Shortcut & shortcut)87 bool ShortcutsDatabase::UpdateShortcut(
88 const ShortcutsBackend::Shortcut& shortcut) {
89 sql::Statement s(db_.GetCachedStatement(
90 SQL_FROM_HERE,
91 "UPDATE omni_box_shortcuts SET id=?, text=?, fill_into_edit=?, url=?, "
92 "contents=?, contents_class=?, description=?, description_class=?, "
93 "transition=?, type=?, keyword=?, last_access_time=?, "
94 "number_of_hits=? WHERE id=?"));
95 BindShortcutToStatement(shortcut, &s);
96 s.BindString(13, shortcut.id);
97 return s.Run();
98 }
99
DeleteShortcutsWithIds(const std::vector<std::string> & shortcut_ids)100 bool ShortcutsDatabase::DeleteShortcutsWithIds(
101 const std::vector<std::string>& shortcut_ids) {
102 bool success = true;
103 db_.BeginTransaction();
104 for (std::vector<std::string>::const_iterator it(shortcut_ids.begin());
105 it != shortcut_ids.end(); ++it) {
106 success &= DeleteShortcut("id", *it, db_);
107 }
108 db_.CommitTransaction();
109 return success;
110 }
111
DeleteShortcutsWithUrl(const std::string & shortcut_url_spec)112 bool ShortcutsDatabase::DeleteShortcutsWithUrl(
113 const std::string& shortcut_url_spec) {
114 return DeleteShortcut("url", shortcut_url_spec, db_);
115 }
116
DeleteAllShortcuts()117 bool ShortcutsDatabase::DeleteAllShortcuts() {
118 if (!db_.Execute("DELETE FROM omni_box_shortcuts"))
119 return false;
120
121 ignore_result(db_.Execute("VACUUM"));
122 return true;
123 }
124
LoadShortcuts(GuidToShortcutMap * shortcuts)125 void ShortcutsDatabase::LoadShortcuts(GuidToShortcutMap* shortcuts) {
126 DCHECK(shortcuts);
127 sql::Statement s(db_.GetCachedStatement(
128 SQL_FROM_HERE,
129 "SELECT id, text, fill_into_edit, url, contents, contents_class, "
130 "description, description_class, transition, type, keyword, "
131 "last_access_time, number_of_hits FROM omni_box_shortcuts"));
132
133 shortcuts->clear();
134 while (s.Step()) {
135 shortcuts->insert(std::make_pair(
136 s.ColumnString(0),
137 ShortcutsBackend::Shortcut(
138 s.ColumnString(0), // id
139 s.ColumnString16(1), // text
140 ShortcutsBackend::Shortcut::MatchCore(
141 s.ColumnString16(2), // fill_into_edit
142 GURL(s.ColumnString(3)), // destination_url
143 s.ColumnString16(4), // contents
144 AutocompleteMatch::ClassificationsFromString(s.ColumnString(5)),
145 // contents_class
146 s.ColumnString16(6), // description
147 AutocompleteMatch::ClassificationsFromString(s.ColumnString(7)),
148 // description_class
149 static_cast<content::PageTransition>(s.ColumnInt(8)),
150 // transition
151 static_cast<AutocompleteMatch::Type>(s.ColumnInt(9)),
152 // type
153 s.ColumnString16(10)), // keyword
154 base::Time::FromInternalValue(s.ColumnInt64(11)),
155 // last_access_time
156 s.ColumnInt(12)))); // number_of_hits
157 }
158 }
159
~ShortcutsDatabase()160 ShortcutsDatabase::~ShortcutsDatabase() {
161 }
162
EnsureTable()163 bool ShortcutsDatabase::EnsureTable() {
164 if (!db_.DoesTableExist("omni_box_shortcuts")) {
165 return db_.Execute(
166 "CREATE TABLE omni_box_shortcuts (id VARCHAR PRIMARY KEY, "
167 "text VARCHAR, fill_into_edit VARCHAR, url VARCHAR, "
168 "contents VARCHAR, contents_class VARCHAR, description VARCHAR, "
169 "description_class VARCHAR, transition INTEGER, type INTEGER, "
170 "keyword VARCHAR, last_access_time INTEGER, "
171 "number_of_hits INTEGER)");
172 }
173
174 // The first version of the shortcuts table lacked the fill_into_edit,
175 // transition, type, and keyword columns.
176 if (!db_.DoesColumnExist("omni_box_shortcuts", "fill_into_edit")) {
177 // Perform the upgrade in a transaction to ensure it doesn't happen
178 // incompletely.
179 sql::Transaction transaction(&db_);
180 transaction.Begin();
181 return
182 db_.Execute("ALTER TABLE omni_box_shortcuts "
183 "ADD COLUMN fill_into_edit VARCHAR") &&
184 db_.Execute("UPDATE omni_box_shortcuts SET fill_into_edit = url") &&
185 db_.Execute("ALTER TABLE omni_box_shortcuts "
186 "ADD COLUMN transition INTEGER") &&
187 db_.Execute(base::StringPrintf(
188 "UPDATE omni_box_shortcuts SET transition = %d",
189 static_cast<int>(content::PAGE_TRANSITION_TYPED)).c_str()) &&
190 db_.Execute("ALTER TABLE omni_box_shortcuts ADD COLUMN type INTEGER") &&
191 db_.Execute(base::StringPrintf(
192 "UPDATE omni_box_shortcuts SET type = %d",
193 static_cast<int>(AutocompleteMatchType::HISTORY_TITLE)).c_str()) &&
194 db_.Execute("ALTER TABLE omni_box_shortcuts "
195 "ADD COLUMN keyword VARCHAR") &&
196 transaction.Commit();
197 }
198
199 return true;
200 }
201
202 } // namespace history
203