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/history/in_memory_url_cache_database.h" | |
6 | |
7 #include <algorithm> | |
8 #include <functional> | |
9 #include <iterator> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/bind_helpers.h" | |
13 #include "base/file_path.h" | |
14 #include "base/string_util.h" | |
15 #include "base/stringprintf.h" | |
16 #include "base/time.h" | |
17 #include "chrome/browser/diagnostics/sqlite_diagnostics.h" | |
18 #include "chrome/browser/history/url_database.h" | |
19 #include "chrome/browser/history/url_index_private_data.h" | |
20 #include "chrome/common/chrome_notification_types.h" | |
21 #include "content/public/browser/browser_thread.h" | |
22 #include "content/public/browser/notification_service.h" | |
23 #include "sql/diagnostic_error_delegate.h" | |
24 #include "sql/statement.h" | |
25 #include "sql/transaction.h" | |
26 #include "third_party/sqlite/sqlite3.h" | |
27 | |
28 using content::BrowserThread; | |
29 | |
30 namespace history { | |
31 | |
32 namespace { | |
33 | |
34 static const int kCurrentVersionNumber = 1; | |
35 static const int kCompatibleVersionNumber = 1; | |
36 | |
37 const char* kWordsTableName = "words"; | |
38 const char* kCharWordsTableName = "char_words"; | |
39 const char* kWordHistoryTableName = "word_history"; | |
40 const char* kURLsTableName = "urls"; | |
41 const char* kURLWordStartsTableName = "url_word_starts"; | |
42 const char* kTitleWordStartsTableName = "title_word_starts"; | |
43 | |
44 } // namespace | |
45 | |
46 // Public Methods -------------------------------------------------------------- | |
47 | |
48 InMemoryURLCacheDatabase::InMemoryURLCacheDatabase() | |
49 : update_error_(SQLITE_OK), | |
50 shutdown_(false) { | |
51 } | |
52 | |
53 InMemoryURLCacheDatabase::~InMemoryURLCacheDatabase() {} | |
54 | |
55 bool InMemoryURLCacheDatabase::Init( | |
56 const FilePath& file_path, | |
57 const base::SequencedWorkerPool::SequenceToken& sequence_token) { | |
58 DCHECK(!BrowserThread::IsWellKnownThread(BrowserThread::UI) || | |
59 !BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
60 if (shutdown_) | |
61 return false; | |
62 sequence_token_ = sequence_token; | |
63 db_.set_error_delegate(GetErrorHandlerForHQPCacheDb()); | |
64 // Don't use very much memory caching this database. We primarily use it | |
65 // as a backing store for existing in-memory data. | |
66 db_.set_cache_size(64); | |
67 db_.set_exclusive_locking(); | |
68 if (!db_.Open(file_path)) { | |
69 UMA_HISTOGRAM_ENUMERATION("Sqlite.HQPCacheOpen.Error", db_.GetErrorCode(), | |
70 sql::kMaxSqliteError); | |
71 return false; | |
72 } | |
73 if (!InitDatabase()) { | |
74 db_.Close(); | |
75 return false; | |
76 } | |
77 return true; | |
78 } | |
79 | |
80 void InMemoryURLCacheDatabase::Shutdown() { | |
81 shutdown_ = true; | |
82 // Allow all outstanding sequenced database operations to complete. | |
83 PostSequencedDBTask(FROM_HERE, | |
84 base::Bind(&InMemoryURLCacheDatabase::ShutdownTask, this)); | |
85 } | |
86 | |
87 bool InMemoryURLCacheDatabase::RestorePrivateData(URLIndexPrivateData* data) { | |
88 DCHECK(!BrowserThread::IsWellKnownThread(BrowserThread::UI) || | |
89 !BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
90 return RestoreWords(data) && RestoreCharWords(data) && | |
91 RestoreWordHistory(data) && RestoreURLs(data) && RestoreWordStarts(data); | |
92 } | |
93 | |
94 bool InMemoryURLCacheDatabase::Reset() { | |
95 DCHECK(!BrowserThread::IsWellKnownThread(BrowserThread::UI) || | |
96 !BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
97 if (db_.Raze() && CreateTables()) | |
98 return true; | |
99 UMA_HISTOGRAM_ENUMERATION("Sqlite.HQPCacheCreate.Error", db_.GetErrorCode(), | |
100 sql::kMaxSqliteError); | |
101 return false; | |
102 } | |
103 | |
104 // Database Updating ----------------------------------------------------------- | |
105 | |
106 void InMemoryURLCacheDatabase::AddHistoryToURLs(history::HistoryID history_id, | |
107 const history::URLRow& row) { | |
108 PostSequencedDBTask(FROM_HERE, | |
109 base::Bind(&InMemoryURLCacheDatabase::AddHistoryToURLsTask, this, | |
110 history_id, row)); | |
111 } | |
112 | |
113 void InMemoryURLCacheDatabase::AddHistoryToWordHistory(WordID word_id, | |
114 HistoryID history_id) { | |
115 PostSequencedDBTask(FROM_HERE, | |
116 base::Bind(&InMemoryURLCacheDatabase::AddHistoryToWordHistoryTask, this, | |
117 word_id, history_id)); | |
118 } | |
119 | |
120 void InMemoryURLCacheDatabase::AddWordToWords(WordID word_id, | |
121 const string16& uni_word) { | |
122 PostSequencedDBTask(FROM_HERE, | |
123 base::Bind(&InMemoryURLCacheDatabase::AddWordToWordsTask, this, word_id, | |
124 uni_word)); | |
125 } | |
126 | |
127 void InMemoryURLCacheDatabase::AddWordToCharWords(char16 uni_char, | |
128 WordID word_id) { | |
129 PostSequencedDBTask(FROM_HERE, | |
130 base::Bind(&InMemoryURLCacheDatabase::AddWordToCharWordsTask, this, | |
131 uni_char, word_id)); | |
132 } | |
133 | |
134 void InMemoryURLCacheDatabase::AddRowWordStarts( | |
135 HistoryID history_id, | |
136 const RowWordStarts& row_word_starts) { | |
137 PostSequencedDBTask(FROM_HERE, | |
138 base::Bind(&InMemoryURLCacheDatabase::AddRowWordStartsTask, this, | |
139 history_id, row_word_starts)); | |
140 } | |
141 | |
142 void InMemoryURLCacheDatabase::RemoveHistoryIDFromURLs(HistoryID history_id) { | |
143 PostSequencedDBTask(FROM_HERE, | |
144 base::Bind(&InMemoryURLCacheDatabase::RemoveHistoryIDFromURLsTask, this, | |
145 history_id)); | |
146 } | |
147 | |
148 void InMemoryURLCacheDatabase::RemoveHistoryIDFromWordHistory( | |
149 HistoryID history_id) { | |
150 PostSequencedDBTask(FROM_HERE, | |
151 base::Bind(&InMemoryURLCacheDatabase::RemoveHistoryIDFromWordHistoryTask, | |
152 this, history_id)); | |
153 } | |
154 | |
155 void InMemoryURLCacheDatabase::RemoveWordFromWords(WordID word_id) { | |
156 PostSequencedDBTask(FROM_HERE, | |
157 base::Bind(&InMemoryURLCacheDatabase::RemoveWordFromWordsTask, this, | |
158 word_id)); | |
159 } | |
160 | |
161 void InMemoryURLCacheDatabase::RemoveWordStarts(HistoryID history_id) { | |
162 PostSequencedDBTask(FROM_HERE, | |
163 base::Bind(&InMemoryURLCacheDatabase::RemoveWordStartsTask, this, | |
164 history_id)); | |
165 } | |
166 | |
167 void InMemoryURLCacheDatabase::BeginTransaction() { | |
168 PostSequencedDBTask(FROM_HERE, | |
169 base::Bind(&InMemoryURLCacheDatabase::BeginTransactionTask, this)); | |
170 } | |
171 | |
172 void InMemoryURLCacheDatabase::CommitTransaction() { | |
173 PostSequencedDBTask(FROM_HERE, | |
174 base::Bind(&InMemoryURLCacheDatabase::CommitTransactionTask, this)); | |
175 } | |
176 | |
177 bool InMemoryURLCacheDatabase::Refresh(const URLIndexPrivateData& data) { | |
178 DCHECK(!BrowserThread::IsWellKnownThread(BrowserThread::UI) || | |
179 !BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
180 return RefreshWords(data) && RefreshCharWords(data) && | |
181 RefreshWordHistory(data) && RefreshURLs(data) && RefreshWordStarts(data); | |
182 } | |
183 | |
184 // Private Methods ------------------------------------------------------------- | |
185 | |
186 bool InMemoryURLCacheDatabase::InitDatabase() { | |
187 db_.Preload(); // Prime the cache. | |
188 if (EnsureCurrentVersion() != sql::INIT_OK) | |
189 return false; | |
190 | |
191 // Create the tables if any do not already exist. | |
192 return VerifyTables() || Reset(); | |
193 } | |
194 | |
195 void InMemoryURLCacheDatabase::PostSequencedDBTask( | |
196 const tracked_objects::Location& from_here, | |
197 const base::Closure& task) { | |
198 BrowserThread::GetBlockingPool()->PostSequencedWorkerTask( | |
199 sequence_token_, from_here, task); | |
200 } | |
201 | |
202 void InMemoryURLCacheDatabase::ShutdownTask() { | |
203 DCHECK(!BrowserThread::IsWellKnownThread(BrowserThread::UI) || | |
204 !BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
205 db_.Close(); | |
206 } | |
207 | |
208 sql::InitStatus InMemoryURLCacheDatabase::EnsureCurrentVersion() { | |
209 // We can't read databases newer than we were designed for. | |
210 if (!meta_table_.Init(&db_, kCurrentVersionNumber, kCompatibleVersionNumber)) | |
211 return sql::INIT_FAILURE; | |
212 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { | |
213 LOG(WARNING) << "In-memory URL Cache database is too new."; | |
214 return sql::INIT_TOO_NEW; | |
215 } | |
216 | |
217 // NOTE: Add migration code here as required. | |
218 | |
219 return sql::INIT_OK; | |
220 } | |
221 | |
222 void InMemoryURLCacheDatabase::BeginTransactionTask() { | |
223 DCHECK(!BrowserThread::IsWellKnownThread(BrowserThread::UI) || | |
224 !BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
225 db_.BeginTransaction(); | |
226 } | |
227 | |
228 void InMemoryURLCacheDatabase::CommitTransactionTask() { | |
229 DCHECK(!BrowserThread::IsWellKnownThread(BrowserThread::UI) || | |
230 !BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
231 if (update_error_ != SQLITE_OK) { | |
232 db_.RollbackTransaction(); | |
233 BrowserThread::PostTask( | |
234 BrowserThread::UI, FROM_HERE, | |
235 base::Bind(&InMemoryURLCacheDatabase::NotifyDatabaseFailure, | |
236 base::Unretained(this))); | |
237 return; | |
238 } | |
239 db_.CommitTransaction(); | |
240 } | |
241 | |
242 bool InMemoryURLCacheDatabase::VerifyTables() { | |
243 if (db_.DoesTableExist(kWordsTableName) && | |
244 db_.DoesTableExist(kCharWordsTableName) && | |
245 db_.DoesTableExist(kWordHistoryTableName) && | |
246 db_.DoesTableExist(kURLsTableName) && | |
247 db_.DoesTableExist(kURLWordStartsTableName) && | |
248 db_.DoesTableExist(kTitleWordStartsTableName)) | |
249 return true; | |
250 UMA_HISTOGRAM_ENUMERATION("Sqlite.HQPCacheVerify.Error", db_.GetErrorCode(), | |
251 sql::kMaxSqliteError); | |
252 return false; | |
253 } | |
254 | |
255 bool InMemoryURLCacheDatabase::CreateTables() { | |
256 std::string sql(StringPrintf( | |
257 "CREATE TABLE %s (word_id INTEGER PRIMARY KEY, word TEXT)", | |
258 kWordsTableName)); | |
259 if (!db_.Execute(sql.c_str())) | |
260 return false; | |
261 sql = StringPrintf("CREATE INDEX %s_index ON %s (word_id)", | |
262 kWordsTableName, kWordsTableName); | |
263 if (!db_.Execute(sql.c_str())) | |
264 return false; | |
265 | |
266 sql = StringPrintf("CREATE TABLE %s (char LONGCHAR, word_id INTEGER)", | |
267 kCharWordsTableName); | |
268 if (!db_.Execute(sql.c_str())) | |
269 return false; | |
270 sql = StringPrintf("CREATE INDEX %s_index ON %s (char)", | |
271 kCharWordsTableName, kCharWordsTableName); | |
272 if (!db_.Execute(sql.c_str())) | |
273 return false; | |
274 | |
275 sql = StringPrintf("CREATE TABLE %s (word_id INTEGER, history_id INTEGER)", | |
276 kWordHistoryTableName); | |
277 if (!db_.Execute(sql.c_str())) | |
278 return false; | |
279 sql = StringPrintf("CREATE INDEX %s_history_index ON %s (history_id)", | |
280 kWordHistoryTableName, kWordHistoryTableName); | |
281 if (!db_.Execute(sql.c_str())) | |
282 return false; | |
283 | |
284 sql = StringPrintf( | |
285 "CREATE TABLE %s (history_id INTEGER PRIMARY KEY, url TEXT, " | |
286 "title TEXT, visit_count INTEGER, typed_count INTEGER, " | |
287 "last_visit_time INTEGER, hidden INTEGER)", kURLsTableName); | |
288 if (!db_.Execute(sql.c_str())) | |
289 return false; | |
290 sql = StringPrintf("CREATE INDEX %s_index ON %s (history_id)", | |
291 kURLsTableName, kURLsTableName); | |
292 if (!db_.Execute(sql.c_str())) | |
293 return false; | |
294 | |
295 sql = StringPrintf("CREATE TABLE %s (history_id INTEGER, word_start INTEGER)", | |
296 kURLWordStartsTableName); | |
297 if (!db_.Execute(sql.c_str())) | |
298 return false; | |
299 sql = StringPrintf("CREATE INDEX %s_index ON %s (history_id)", | |
300 kURLWordStartsTableName, kURLWordStartsTableName); | |
301 if (!db_.Execute(sql.c_str())) | |
302 return false; | |
303 | |
304 sql = StringPrintf("CREATE TABLE %s (history_id INTEGER, word_start INTEGER)", | |
305 kTitleWordStartsTableName); | |
306 if (!db_.Execute(sql.c_str())) | |
307 return false; | |
308 sql = StringPrintf("CREATE INDEX %s_index ON %s (history_id)", | |
309 kTitleWordStartsTableName, kTitleWordStartsTableName); | |
310 return db_.Execute(sql.c_str()); | |
311 } | |
312 | |
313 void InMemoryURLCacheDatabase::NotifyDatabaseFailure() { | |
314 content::NotificationService::current()->Notify( | |
315 chrome::NOTIFICATION_IN_MEMORY_URL_CACHE_DATABASE_FAILURE, | |
316 content::Source<InMemoryURLCacheDatabase>(this), | |
317 content::NotificationService::NoDetails()); | |
318 } | |
319 | |
320 bool InMemoryURLCacheDatabase::RunStatement(sql::Statement* statement) { | |
321 if (statement->Run()) | |
322 return true; | |
323 // If a failure has already occurred don't spew more histograms and don't | |
324 // forget the very first failure code. | |
325 if (update_error_ != SQLITE_OK) | |
326 return false; | |
327 update_error_ = db_.GetErrorCode(); | |
328 UMA_HISTOGRAM_ENUMERATION("Sqlite.HQPCacheUpdate.Error", update_error_, | |
329 sql::kMaxSqliteError); | |
330 return false; | |
331 } | |
332 | |
333 // Database Additions ---------------------------------------------------------- | |
334 | |
335 void InMemoryURLCacheDatabase::AddHistoryToURLsTask( | |
336 history::HistoryID history_id, | |
337 const history::URLRow& row) { | |
338 std::string sql(StringPrintf( | |
339 "INSERT INTO %s (history_id, url, title, visit_count, typed_count, " | |
340 "last_visit_time, hidden) VALUES (?,?,?,?,?,?,?)", | |
341 kURLsTableName)); | |
342 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
343 statement.BindInt(0, history_id); | |
344 statement.BindString(1, URLDatabase::GURLToDatabaseURL(row.url())); | |
345 statement.BindString16(2, row.title()); | |
346 statement.BindInt(3, row.visit_count()); | |
347 statement.BindInt(4, row.typed_count()); | |
348 statement.BindInt64(5, row.last_visit().ToInternalValue()); | |
349 statement.BindBool(6, row.hidden()); | |
350 RunStatement(&statement); | |
351 } | |
352 | |
353 void InMemoryURLCacheDatabase::AddHistoryToWordHistoryTask( | |
354 WordID word_id, | |
355 HistoryID history_id) { | |
356 std::string sql(StringPrintf( | |
357 "INSERT INTO %s (word_id, history_id) VALUES (?,?)", | |
358 kWordHistoryTableName)); | |
359 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
360 statement.BindInt(0, word_id); | |
361 statement.BindInt(1, history_id); | |
362 RunStatement(&statement); | |
363 } | |
364 | |
365 void InMemoryURLCacheDatabase::AddWordToWordsTask(WordID word_id, | |
366 const string16& uni_word) { | |
367 std::string sql(StringPrintf( | |
368 "INSERT INTO %s (word_id, word) VALUES (?,?)", | |
369 kWordsTableName)); | |
370 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
371 statement.BindInt(0, word_id); | |
372 statement.BindString16(1, uni_word); | |
373 RunStatement(&statement); | |
374 } | |
375 | |
376 void InMemoryURLCacheDatabase::AddWordToCharWordsTask(char16 uni_char, | |
377 WordID word_id) { | |
378 std::string sql(StringPrintf("INSERT INTO %s (char, word_id) VALUES (?,?)", | |
379 kCharWordsTableName)); | |
380 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
381 statement.BindInt(0, uni_char); | |
382 statement.BindInt(1, word_id); | |
383 RunStatement(&statement); | |
384 } | |
385 | |
386 void InMemoryURLCacheDatabase::AddRowWordStartsTask( | |
387 HistoryID history_id, | |
388 const RowWordStarts& row_word_starts) { | |
389 std::string sql_1(StringPrintf( | |
390 "INSERT INTO %s (history_id, word_start) VALUES (?,?)", | |
391 kURLWordStartsTableName)); | |
392 sql::Statement url_statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
393 sql_1.c_str())); | |
394 for (WordStarts::const_iterator i = row_word_starts.url_word_starts_.begin(); | |
395 i != row_word_starts.url_word_starts_.end(); ++i) { | |
396 url_statement.Reset(true); | |
397 url_statement.BindInt(0, history_id); | |
398 url_statement.BindInt(1, *i); | |
399 RunStatement(&url_statement); | |
400 } | |
401 std::string sql_2(StringPrintf( | |
402 "INSERT INTO %s (history_id, word_start) VALUES (?,?)", | |
403 kTitleWordStartsTableName)); | |
404 sql::Statement title_statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
405 sql_2.c_str())); | |
406 for (WordStarts::const_iterator i = | |
407 row_word_starts.title_word_starts_.begin(); | |
408 i != row_word_starts.title_word_starts_.end(); ++i) { | |
409 title_statement.Reset(true); | |
410 title_statement.BindInt(0, history_id); | |
411 title_statement.BindInt(1, *i); | |
412 RunStatement(&title_statement); | |
413 } | |
414 } | |
415 | |
416 // Database Removals ----------------------------------------------------------- | |
417 | |
418 void InMemoryURLCacheDatabase::RemoveHistoryIDFromURLsTask( | |
419 HistoryID history_id) { | |
420 std::string sql(StringPrintf("DELETE FROM %s WHERE history_id = ?", | |
421 kURLsTableName)); | |
422 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
423 statement.BindInt(0, history_id); | |
424 RunStatement(&statement); | |
425 } | |
426 | |
427 void InMemoryURLCacheDatabase::RemoveHistoryIDFromWordHistoryTask( | |
428 HistoryID history_id) { | |
429 std::string sql(StringPrintf("DELETE FROM %s WHERE history_id = ?", | |
430 kWordHistoryTableName)); | |
431 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
432 statement.BindInt(0, history_id); | |
433 RunStatement(&statement); | |
434 } | |
435 | |
436 void InMemoryURLCacheDatabase::RemoveWordFromWordsTask(WordID word_id) { | |
437 std::string sql_1(StringPrintf("DELETE FROM %s WHERE word_id = ?", | |
438 kWordsTableName)); | |
439 sql::Statement statement_1(db_.GetCachedStatement(SQL_FROM_HERE, | |
440 sql_1.c_str())); | |
441 statement_1.BindInt(0, word_id); | |
442 RunStatement(&statement_1); | |
443 | |
444 std::string sql_2(StringPrintf("DELETE FROM %s WHERE word_id = ?", | |
445 kCharWordsTableName)); | |
446 sql::Statement statement_2(db_.GetCachedStatement(SQL_FROM_HERE, | |
447 sql_2.c_str())); | |
448 statement_2.BindInt(0, word_id); | |
449 RunStatement(&statement_2); | |
450 } | |
451 | |
452 void InMemoryURLCacheDatabase::RemoveWordStartsTask(HistoryID history_id) { | |
453 std::string url_sql(StringPrintf("DELETE FROM %s WHERE history_id = ?", | |
454 kURLWordStartsTableName)); | |
455 sql::Statement url_statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
456 url_sql.c_str())); | |
457 url_statement.BindInt(0, history_id); | |
458 RunStatement(&url_statement); | |
459 | |
460 std::string title_sql(StringPrintf("DELETE FROM %s WHERE history_id = ?", | |
461 kTitleWordStartsTableName)); | |
462 sql::Statement title_statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
463 title_sql.c_str())); | |
464 title_statement.BindInt(0, history_id); | |
465 RunStatement(&title_statement); | |
466 } | |
467 | |
468 // Database Refreshing --------------------------------------------------------- | |
469 | |
470 bool InMemoryURLCacheDatabase::RefreshWords(const URLIndexPrivateData& data) { | |
471 // Populate the words table from the contents of the word_map_. This will | |
472 // allow the restoration of the word_map_ as well as the word_list_ and | |
473 // available_words_. | |
474 std::string sql(StringPrintf("INSERT INTO %s (word_id, word) VALUES (?,?)", | |
475 kWordsTableName)); | |
476 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
477 for (WordMap::const_iterator i = data.word_map_.begin(); | |
478 i != data.word_map_.end(); ++i) { | |
479 statement.Reset(true); | |
480 statement.BindInt(0, i->second); | |
481 statement.BindString16(1, i->first); | |
482 if (!RunStatement(&statement)) | |
483 return false; | |
484 } | |
485 return true; | |
486 } | |
487 | |
488 bool InMemoryURLCacheDatabase::RefreshCharWords( | |
489 const URLIndexPrivateData& data) { | |
490 std::string sql(StringPrintf("INSERT INTO %s (char, word_id) VALUES (?,?)", | |
491 kCharWordsTableName)); | |
492 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
493 for (CharWordIDMap::const_iterator i = data.char_word_map_.begin(); | |
494 i != data.char_word_map_.end(); ++i) { | |
495 for (WordIDSet::const_iterator j = i->second.begin(); j != i->second.end(); | |
496 ++j) { | |
497 statement.Reset(true); | |
498 statement.BindInt(0, i->first); | |
499 statement.BindInt(1, *j); | |
500 if (!RunStatement(&statement)) | |
501 return false; | |
502 } | |
503 } | |
504 return true; | |
505 } | |
506 | |
507 bool InMemoryURLCacheDatabase::RefreshWordHistory( | |
508 const URLIndexPrivateData& data) { | |
509 // Populate the word_history table from the contents of the | |
510 // word_id_history_map_. This will allow the restoration of the | |
511 // word_id_history_map_ as well as the history_id_word_map_. | |
512 std::string sql(StringPrintf( | |
513 "INSERT INTO %s (word_id, history_id) VALUES (?,?)", | |
514 kWordHistoryTableName)); | |
515 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
516 for (WordIDHistoryMap::const_iterator i = data.word_id_history_map_.begin(); | |
517 i != data.word_id_history_map_.end(); ++i) { | |
518 for (HistoryIDSet::const_iterator j = i->second.begin(); | |
519 j != i->second.end(); ++j) { | |
520 statement.Reset(true); | |
521 statement.BindInt(0, i->first); | |
522 statement.BindInt64(1, *j); | |
523 if (!RunStatement(&statement)) | |
524 return false; | |
525 } | |
526 } | |
527 return true; | |
528 } | |
529 | |
530 bool InMemoryURLCacheDatabase::RefreshURLs(const URLIndexPrivateData& data) { | |
531 std::string sql(StringPrintf( | |
532 "INSERT INTO %s (history_id, url, title, visit_count, typed_count, " | |
533 "last_visit_time, hidden) VALUES (?,?,?,?,?,?,?)", | |
534 kURLsTableName)); | |
535 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
536 for (HistoryInfoMap::const_iterator i = data.history_info_map_.begin(); | |
537 i != data.history_info_map_.end(); ++i) { | |
538 statement.Reset(true); | |
539 statement.BindInt(0, i->first); | |
540 statement.BindString(1, URLDatabase::GURLToDatabaseURL(i->second.url())); | |
541 statement.BindString16(2, i->second.title()); | |
542 statement.BindInt(3, i->second.visit_count()); | |
543 statement.BindInt(4, i->second.typed_count()); | |
544 statement.BindInt64(5, i->second.last_visit().ToInternalValue()); | |
545 statement.BindBool(6, i->second.hidden()); | |
546 if (!RunStatement(&statement)) | |
547 return false; | |
548 } | |
549 return true; | |
550 } | |
551 | |
552 bool InMemoryURLCacheDatabase::RefreshWordStarts( | |
553 const URLIndexPrivateData& data) { | |
554 std::string url_sql(StringPrintf( | |
555 "INSERT INTO %s (history_id, word_start) VALUES (?,?)", | |
556 kURLWordStartsTableName)); | |
557 sql::Statement url_statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
558 url_sql.c_str())); | |
559 std::string title_sql(StringPrintf( | |
560 "INSERT INTO %s (history_id, word_start) VALUES (?,?)", | |
561 kTitleWordStartsTableName)); | |
562 sql::Statement title_statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
563 title_sql.c_str())); | |
564 for (WordStartsMap::const_iterator i = data.word_starts_map_.begin(); | |
565 i != data.word_starts_map_.end(); ++i) { | |
566 for (WordStarts::const_iterator w = i->second.url_word_starts_.begin(); | |
567 w != i->second.url_word_starts_.end(); ++w) { | |
568 url_statement.Reset(true); | |
569 url_statement.BindInt(0, i->first); | |
570 url_statement.BindInt(1, *w); | |
571 if (!RunStatement(&url_statement)) | |
572 return false; | |
573 } | |
574 for (WordStarts::const_iterator w = i->second.title_word_starts_.begin(); | |
575 w != i->second.title_word_starts_.end(); ++w) { | |
576 title_statement.Reset(true); | |
577 title_statement.BindInt(0, i->first); | |
578 title_statement.BindInt(1, *w); | |
579 if (!RunStatement(&title_statement)) | |
580 return false; | |
581 } | |
582 } | |
583 return true; | |
584 } | |
585 | |
586 // Database Restoration -------------------------------------------------------- | |
587 | |
588 bool InMemoryURLCacheDatabase::RestoreWords(URLIndexPrivateData* data) { | |
589 // Rebuild word_list_, word_map_ and available_words_. | |
590 std::string sql(StringPrintf("SELECT word_id, word FROM %s ORDER BY word_id", | |
591 kWordsTableName)); | |
592 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
593 while (statement.Step()) { | |
594 WordID word_id = statement.ColumnInt(0); | |
595 // A gap in contiguous word IDs indicates word slots in the vector that are | |
596 // currently unused and available for future use as new words are indexed. | |
597 while (data->word_list_.size() < word_id) { | |
598 data->available_words_.insert(data->word_list_.size()); | |
599 data->word_list_.push_back(string16()); | |
600 } | |
601 string16 word = statement.ColumnString16(1); | |
602 DCHECK_EQ(static_cast<size_t>(word_id), data->word_list_.size()); | |
603 data->word_list_.push_back(word); | |
604 data->word_map_[word] = word_id; | |
605 } | |
606 return statement.Succeeded() && !data->word_list_.empty(); | |
607 } | |
608 | |
609 bool InMemoryURLCacheDatabase::RestoreCharWords(URLIndexPrivateData* data) { | |
610 // Rebuild char_word_map_. | |
611 std::string sql(StringPrintf("SELECT char, word_id FROM %s ORDER BY char", | |
612 kCharWordsTableName)); | |
613 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
614 CharWordIDMap& char_map(data->char_word_map_); | |
615 CharWordIDMap::iterator it = char_map.begin(); | |
616 while (statement.Step()) { | |
617 char16 next_char = statement.ColumnInt(0); | |
618 if (it == char_map.begin() || it->first != next_char) | |
619 it = char_map.insert(it, std::make_pair(next_char, WordIDSet())); | |
620 it->second.insert(statement.ColumnInt(1)); | |
621 } | |
622 return statement.Succeeded() && !char_map.empty(); | |
623 } | |
624 | |
625 bool InMemoryURLCacheDatabase::RestoreWordHistory(URLIndexPrivateData* data) { | |
626 // Rebuild word_id_history_map_ and history_id_word_map_. | |
627 std::string sql(StringPrintf( | |
628 "SELECT word_id, history_id FROM %s ORDER BY word_id", | |
629 kWordHistoryTableName)); | |
630 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
631 WordIDHistoryMap& word_map(data->word_id_history_map_); | |
632 WordIDHistoryMap::iterator it = word_map.begin(); | |
633 while (statement.Step()) { | |
634 WordID word_id = statement.ColumnInt(0); | |
635 HistoryID history_id = statement.ColumnInt(1); | |
636 data->AddToHistoryIDWordMap(history_id, word_id); | |
637 if (it == word_map.begin() || it->first != word_id) | |
638 it = word_map.insert(it, std::make_pair(word_id, HistoryIDSet())); | |
639 it->second.insert(history_id); | |
640 } | |
641 return statement.Succeeded() && !word_map.empty(); | |
642 } | |
643 | |
644 bool InMemoryURLCacheDatabase::RestoreURLs(URLIndexPrivateData* data) { | |
645 // Rebuild history_info_map_. | |
646 std::string sql(StringPrintf( | |
647 "SELECT history_id, url, title, visit_count, typed_count, " | |
648 "last_visit_time, hidden FROM %s", | |
649 kURLsTableName)); | |
650 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, sql.c_str())); | |
651 while (statement.Step()) { | |
652 HistoryID history_id = statement.ColumnInt64(0); | |
653 URLRow row(GURL(statement.ColumnString(1)), history_id); | |
654 row.set_title(statement.ColumnString16(2)); | |
655 row.set_visit_count(statement.ColumnInt(3)); | |
656 row.set_typed_count(statement.ColumnInt(4)); | |
657 row.set_last_visit(base::Time::FromInternalValue(statement.ColumnInt64(5))); | |
658 row.set_hidden(statement.ColumnInt(6) != 0); | |
659 data->history_info_map_[history_id] = row; | |
660 } | |
661 return statement.Succeeded() && !data->history_info_map_.empty(); | |
662 } | |
663 | |
664 bool InMemoryURLCacheDatabase::RestoreWordStarts(URLIndexPrivateData* data) { | |
665 std::string url_sql(StringPrintf( | |
666 "SELECT history_id, word_start FROM %s ORDER BY word_start", | |
667 kURLWordStartsTableName)); | |
668 sql::Statement url_statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
669 url_sql.c_str())); | |
670 while (url_statement.Step()) { | |
671 HistoryID history_id = url_statement.ColumnInt64(0); | |
672 size_t word_start = url_statement.ColumnInt64(1); | |
673 data->word_starts_map_[history_id].url_word_starts_.push_back(word_start); | |
674 } | |
675 if (!url_statement.Succeeded()) | |
676 return false; | |
677 | |
678 std::string title_sql(StringPrintf( | |
679 "SELECT history_id, word_start FROM %s ORDER BY word_start", | |
680 kTitleWordStartsTableName)); | |
681 sql::Statement title_statement(db_.GetCachedStatement(SQL_FROM_HERE, | |
682 title_sql.c_str())); | |
683 while (title_statement.Step()) { | |
684 HistoryID history_id = title_statement.ColumnInt64(0); | |
685 size_t word_start = title_statement.ColumnInt64(1); | |
686 data->word_starts_map_[history_id].title_word_starts_.push_back(word_start); | |
687 } | |
688 | |
689 return title_statement.Succeeded() && !data->word_starts_map_.empty(); | |
690 } | |
691 | |
692 } // namespace history | |
OLD | NEW |