OLD | NEW |
---|---|
(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/predictors/predictor_database.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/file_util.h" | |
9 #include "base/logging.h" | |
10 #include "base/metrics/histogram.h" | |
11 #include "base/stringprintf.h" | |
12 #include "base/utf_string_conversions.h" | |
13 #include "chrome/browser/profiles/profile.h" | |
14 #include "chrome/common/guid.h" | |
15 #include "content/public/browser/browser_thread.h" | |
16 #include "sql/statement.h" | |
17 | |
18 namespace { | |
19 | |
20 // TODO(shishir): Rename the table for consistency. | |
21 const char kAutocompletePredictorTableName[] = "network_action_predictor"; | |
22 const FilePath::CharType kPredictorDatabaseName[] = | |
23 FILE_PATH_LITERAL("Network Action Predictor"); | |
24 | |
25 // The maximum length allowed for strings in the database. | |
26 const size_t kMaxDataLength = 2048; | |
27 | |
28 void LogDatabaseStats(const FilePath& db_path, sql::Connection* db) { | |
29 int64 db_size; | |
30 bool success = file_util::GetFileSize(db_path, &db_size); | |
31 DCHECK(success) << "Failed to get file size for " << db_path.value(); | |
32 UMA_HISTOGRAM_MEMORY_KB("Predictor.DatabaseSizeKB", | |
33 static_cast<int>(db_size / 1024)); | |
34 | |
35 sql::Statement count_statement(db->GetUniqueStatement( | |
36 base::StringPrintf("SELECT count(id) FROM %s", | |
37 kAutocompletePredictorTableName).c_str())); | |
38 if (!count_statement.Step()) | |
39 return; | |
40 UMA_HISTOGRAM_COUNTS("AutocompleteActionPredictor.DatabaseRowCount", | |
41 count_statement.ColumnInt(0)); | |
42 } | |
43 | |
44 } // namespace | |
45 | |
46 namespace predictors { | |
47 | |
48 PredictorTableBase::PredictorTableBase() : db_(NULL) { | |
49 } | |
50 | |
51 PredictorTableBase::~PredictorTableBase() { | |
52 } | |
53 | |
54 void PredictorTableBase::Initialize(sql::Connection* db) { | |
55 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB)); | |
56 | |
57 db_ = db; | |
58 CreateTableIfNonExistent(); | |
59 } | |
60 | |
61 | |
62 AutocompleteActionPredictorTable::Row::Row() | |
63 : number_of_hits(0), | |
64 number_of_misses(0) { | |
65 } | |
66 | |
67 AutocompleteActionPredictorTable::Row::Row(const Row::Id& id, | |
68 const string16& user_text, | |
69 const GURL& url, | |
70 int number_of_hits, | |
71 int number_of_misses) | |
72 : id(id), | |
73 user_text(user_text), | |
74 url(url), | |
75 number_of_hits(number_of_hits), | |
76 number_of_misses(number_of_misses) { | |
77 } | |
78 | |
79 AutocompleteActionPredictorTable::Row::Row(const Row& row) | |
80 : id(row.id), | |
81 user_text(row.user_text), | |
82 url(row.url), | |
83 number_of_hits(row.number_of_hits), | |
84 number_of_misses(row.number_of_misses) { | |
85 } | |
86 | |
87 void AutocompleteActionPredictorTable::GetRow(const Row::Id& id, Row* row) { | |
88 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB)); | |
89 if (cancelled_.IsSet() || !db_) | |
90 return; | |
91 | |
92 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, | |
93 base::StringPrintf("SELECT * FROM %s WHERE id=?", | |
94 kAutocompletePredictorTableName).c_str())); | |
95 statement.BindString(0, id); | |
96 | |
97 bool success = StepAndInitializeRow(&statement, row); | |
98 DCHECK(success) << "Failed to get row " << id << " from " | |
99 << kAutocompletePredictorTableName; | |
100 } | |
101 | |
102 void AutocompleteActionPredictorTable::GetAllRows(Rows* row_buffer) { | |
103 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB)); | |
104 if (cancelled_.IsSet() || !db_) | |
105 return; | |
106 | |
107 CHECK(row_buffer); | |
108 row_buffer->clear(); | |
109 | |
110 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, | |
111 base::StringPrintf( | |
112 "SELECT * FROM %s", kAutocompletePredictorTableName).c_str())); | |
113 | |
114 Row row; | |
115 while (StepAndInitializeRow(&statement, &row)) | |
116 row_buffer->push_back(row); | |
117 } | |
118 | |
119 void AutocompleteActionPredictorTable::AddRow( | |
120 const AutocompleteActionPredictorTable::Row& row) { | |
121 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB)); | |
122 if (cancelled_.IsSet() || !db_) | |
123 return; | |
124 | |
125 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, | |
126 base::StringPrintf( | |
127 "INSERT INTO %s " | |
128 "(id, user_text, url, number_of_hits, number_of_misses) " | |
129 "VALUES (?,?,?,?,?)", kAutocompletePredictorTableName).c_str())); | |
130 BindRowToStatement(row, &statement); | |
131 | |
132 bool success = statement.Run(); | |
133 DCHECK(success) << "Failed to insert row " << row.id << " into " | |
134 << kAutocompletePredictorTableName; | |
135 } | |
136 | |
137 void AutocompleteActionPredictorTable::UpdateRow( | |
138 const AutocompleteActionPredictorTable::Row& row) { | |
139 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB)); | |
140 if (cancelled_.IsSet() || !db_) | |
141 return; | |
142 | |
143 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, | |
144 base::StringPrintf( | |
145 "UPDATE %s " | |
146 "SET id=?, user_text=?, url=?, number_of_hits=?, number_of_misses=? " | |
147 "WHERE id=?1", kAutocompletePredictorTableName).c_str())); | |
148 BindRowToStatement(row, &statement); | |
149 | |
150 statement.Run(); | |
151 DCHECK_GT(db_->GetLastChangeCount(), 0); | |
152 } | |
153 | |
154 void AutocompleteActionPredictorTable::AddAndUpdateRows( | |
155 const Rows& rows_to_add, | |
156 const Rows& rows_to_update) { | |
157 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB)); | |
158 if (cancelled_.IsSet() || !db_) | |
159 return; | |
160 | |
161 db_->BeginTransaction(); | |
162 for (Rows::const_iterator it = rows_to_add.begin(); | |
163 it != rows_to_add.end(); ++it) { | |
164 AddRow(*it); | |
165 } | |
166 for (Rows::const_iterator it = rows_to_update.begin(); | |
167 it != rows_to_update.end(); ++it) { | |
168 AddRow(*it); | |
169 } | |
170 db_->CommitTransaction(); | |
171 } | |
172 | |
173 void AutocompleteActionPredictorTable::DeleteRow(const Row::Id& id) { | |
174 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB)); | |
175 if (cancelled_.IsSet() || !db_) | |
176 return; | |
177 | |
178 DeleteRows(std::vector<Row::Id>(1, id)); | |
179 } | |
180 | |
181 void AutocompleteActionPredictorTable::DeleteRows( | |
182 const std::vector<Row::Id>& id_list) { | |
183 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB)); | |
184 if (cancelled_.IsSet() || !db_) | |
185 return; | |
186 | |
187 sql::Statement statement(db_->GetUniqueStatement(base::StringPrintf( | |
188 "DELETE FROM %s WHERE id=?", | |
189 kAutocompletePredictorTableName).c_str())); | |
190 | |
191 db_->BeginTransaction(); | |
192 for (std::vector<Row::Id>::const_iterator it = id_list.begin(); | |
193 it != id_list.end(); ++it) { | |
194 statement.BindString(0, *it); | |
195 statement.Run(); | |
196 statement.Reset(); | |
197 } | |
198 db_->CommitTransaction(); | |
199 } | |
200 | |
201 void AutocompleteActionPredictorTable::DeleteAllRows() { | |
202 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB)); | |
203 if (cancelled_.IsSet() || !db_) | |
204 return; | |
205 | |
206 sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, | |
207 base::StringPrintf("DELETE FROM %s", | |
208 kAutocompletePredictorTableName).c_str())); | |
209 | |
210 statement.Run(); | |
211 } | |
212 | |
213 AutocompleteActionPredictorTable::AutocompleteActionPredictorTable() | |
214 : PredictorTableBase() { | |
215 } | |
216 | |
217 AutocompleteActionPredictorTable::~AutocompleteActionPredictorTable() { | |
218 } | |
219 | |
220 void AutocompleteActionPredictorTable::CreateTableIfNonExistent() { | |
221 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB)); | |
222 if (cancelled_.IsSet() || !db_) | |
223 return; | |
224 | |
225 if (db_->DoesTableExist(kAutocompletePredictorTableName)) | |
226 return; | |
227 | |
228 bool success = db_->Execute(base::StringPrintf( | |
229 "CREATE TABLE %s ( " | |
230 "id TEXT PRIMARY KEY, " | |
231 "user_text TEXT, " | |
232 "url TEXT, " | |
233 "number_of_hits INTEGER, " | |
234 "number_of_misses INTEGER)", kAutocompletePredictorTableName).c_str()); | |
235 DCHECK(success) << "Failed to create " << kAutocompletePredictorTableName | |
236 << " table."; | |
237 } | |
238 | |
239 void AutocompleteActionPredictorTable::BindRowToStatement( | |
240 const Row& row, | |
241 sql::Statement* statement) { | |
242 DCHECK(guid::IsValidGUID(row.id)); | |
243 statement->BindString(0, row.id); | |
244 statement->BindString16(1, row.user_text.substr(0, kMaxDataLength)); | |
245 statement->BindString(2, row.url.spec().substr(0, kMaxDataLength)); | |
246 statement->BindInt(3, row.number_of_hits); | |
247 statement->BindInt(4, row.number_of_misses); | |
248 } | |
249 | |
250 bool AutocompleteActionPredictorTable::StepAndInitializeRow( | |
251 sql::Statement* statement, | |
252 Row* row) { | |
253 if (!statement->Step()) | |
254 return false; | |
255 | |
256 row->id = statement->ColumnString(0); | |
257 row->user_text = statement->ColumnString16(1); | |
258 row->url = GURL(statement->ColumnString(2)); | |
259 row->number_of_hits = statement->ColumnInt(3); | |
260 row->number_of_misses = statement->ColumnInt(4); | |
261 return true; | |
262 } | |
263 | |
264 | |
265 PredictorDatabase::PredictorDatabase(Profile* profile) | |
266 : db_path_(profile->GetPath().Append(kPredictorDatabaseName)), | |
267 autocomplete_table_(new AutocompleteActionPredictorTable()) { | |
dominich
2012/03/13 14:55:21
could the tables be created on the DB thread in th
Shishir
2012/03/14 21:14:37
I need to be able to bind to them in the UI thread
| |
268 } | |
269 | |
270 PredictorDatabase::~PredictorDatabase() { | |
271 } | |
272 | |
273 void PredictorDatabase::Initialize() { | |
274 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::DB)); | |
275 | |
276 db_.set_exclusive_locking(); | |
dominich
2012/03/13 14:55:21
Does this add a mutex that blocks concurrent acces
Shishir
2012/03/14 21:14:37
Does it matter? Isnt all access to the db_ on the
| |
277 bool success = db_.Open(db_path_); | |
278 | |
279 if (!success) { | |
280 LOG(ERROR) << "Could not open predictor database @ " | |
281 << db_path_.AsUTF8Unsafe(); | |
282 return; | |
283 } | |
284 | |
285 autocomplete_table_->Initialize(&db_); | |
286 | |
287 LogDatabaseStats(db_path_, &db_); | |
288 } | |
289 | |
290 void PredictorDatabase::ShutdownOnUIThread() { | |
291 CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); | |
292 | |
293 autocomplete_table_->cancelled_.Set(); | |
294 } | |
295 | |
296 } // namespace predictors | |
OLD | NEW |