Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(325)

Side by Side Diff: chrome/browser/history/url_database.cc

Issue 10948045: introduce chrome/browser/common directory and move url_database(and its dependencies) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/browser/history/url_database.h ('k') | chrome/browser/history/url_database_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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/url_database.h"
6
7 #include <algorithm>
8 #include <limits>
9 #include <string>
10 #include <vector>
11
12 #include "base/i18n/case_conversion.h"
13 #include "base/utf_string_conversions.h"
14 #include "chrome/common/url_constants.h"
15 #include "googleurl/src/gurl.h"
16 #include "sql/statement.h"
17 #include "ui/base/l10n/l10n_util.h"
18
19 namespace history {
20
21 const char URLDatabase::kURLRowFields[] = HISTORY_URL_ROW_FIELDS;
22 const int URLDatabase::kNumURLRowFields = 9;
23
24 URLDatabase::URLEnumeratorBase::URLEnumeratorBase()
25 : initialized_(false) {
26 }
27
28 URLDatabase::URLEnumeratorBase::~URLEnumeratorBase() {
29 }
30
31 URLDatabase::URLEnumerator::URLEnumerator() {
32 }
33
34 URLDatabase::IconMappingEnumerator::IconMappingEnumerator() {
35 }
36
37 bool URLDatabase::URLEnumerator::GetNextURL(URLRow* r) {
38 if (statement_.Step()) {
39 FillURLRow(statement_, r);
40 return true;
41 }
42 return false;
43 }
44
45 bool URLDatabase::IconMappingEnumerator::GetNextIconMapping(IconMapping* r) {
46 if (!statement_.Step())
47 return false;
48
49 r->page_url = GURL(statement_.ColumnString(0));
50 r->icon_id = statement_.ColumnInt64(1);
51 return true;
52 }
53
54 URLDatabase::URLDatabase()
55 : has_keyword_search_terms_(false) {
56 }
57
58 URLDatabase::~URLDatabase() {
59 }
60
61 // static
62 std::string URLDatabase::GURLToDatabaseURL(const GURL& gurl) {
63 // TODO(brettw): do something fancy here with encoding, etc.
64
65 // Strip username and password from URL before sending to DB.
66 GURL::Replacements replacements;
67 replacements.ClearUsername();
68 replacements.ClearPassword();
69
70 return (gurl.ReplaceComponents(replacements)).spec();
71 }
72
73 // Convenience to fill a history::URLRow. Must be in sync with the fields in
74 // kURLRowFields.
75 void URLDatabase::FillURLRow(sql::Statement& s, history::URLRow* i) {
76 DCHECK(i);
77 i->id_ = s.ColumnInt64(0);
78 i->url_ = GURL(s.ColumnString(1));
79 i->title_ = s.ColumnString16(2);
80 i->visit_count_ = s.ColumnInt(3);
81 i->typed_count_ = s.ColumnInt(4);
82 i->last_visit_ = base::Time::FromInternalValue(s.ColumnInt64(5));
83 i->hidden_ = s.ColumnInt(6) != 0;
84 }
85
86 bool URLDatabase::GetURLRow(URLID url_id, URLRow* info) {
87 // TODO(brettw) We need check for empty URLs to handle the case where
88 // there are old URLs in the database that are empty that got in before
89 // we added any checks. We should eventually be able to remove it
90 // when all inputs are using GURL (which prohibit empty input).
91 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
92 "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls WHERE id=?"));
93 statement.BindInt64(0, url_id);
94
95 if (statement.Step()) {
96 FillURLRow(statement, info);
97 return true;
98 }
99 return false;
100 }
101
102 bool URLDatabase::GetAllTypedUrls(URLRows* urls) {
103 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
104 "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls WHERE typed_count > 0"));
105
106 while (statement.Step()) {
107 URLRow info;
108 FillURLRow(statement, &info);
109 urls->push_back(info);
110 }
111 return true;
112 }
113
114 URLID URLDatabase::GetRowForURL(const GURL& url, history::URLRow* info) {
115 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
116 "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls WHERE url=?"));
117 std::string url_string = GURLToDatabaseURL(url);
118 statement.BindString(0, url_string);
119
120 if (!statement.Step())
121 return 0; // no data
122
123 if (info)
124 FillURLRow(statement, info);
125 return statement.ColumnInt64(0);
126 }
127
128 bool URLDatabase::UpdateURLRow(URLID url_id,
129 const history::URLRow& info) {
130 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
131 "UPDATE urls SET title=?,visit_count=?,typed_count=?,last_visit_time=?,"
132 "hidden=?"
133 "WHERE id=?"));
134 statement.BindString16(0, info.title());
135 statement.BindInt(1, info.visit_count());
136 statement.BindInt(2, info.typed_count());
137 statement.BindInt64(3, info.last_visit().ToInternalValue());
138 statement.BindInt(4, info.hidden() ? 1 : 0);
139 statement.BindInt64(5, url_id);
140
141 return statement.Run();
142 }
143
144 URLID URLDatabase::AddURLInternal(const history::URLRow& info,
145 bool is_temporary) {
146 // This function is used to insert into two different tables, so we have to
147 // do some shuffling. Unfortinately, we can't use the macro
148 // HISTORY_URL_ROW_FIELDS because that specifies the table name which is
149 // invalid in the insert syntax.
150 #define ADDURL_COMMON_SUFFIX \
151 " (url, title, visit_count, typed_count, "\
152 "last_visit_time, hidden) "\
153 "VALUES (?,?,?,?,?,?)"
154 const char* statement_name;
155 const char* statement_sql;
156 if (is_temporary) {
157 statement_name = "AddURLTemporary";
158 statement_sql = "INSERT INTO temp_urls" ADDURL_COMMON_SUFFIX;
159 } else {
160 statement_name = "AddURL";
161 statement_sql = "INSERT INTO urls" ADDURL_COMMON_SUFFIX;
162 }
163 #undef ADDURL_COMMON_SUFFIX
164
165 sql::Statement statement(GetDB().GetCachedStatement(
166 sql::StatementID(statement_name), statement_sql));
167 statement.BindString(0, GURLToDatabaseURL(info.url()));
168 statement.BindString16(1, info.title());
169 statement.BindInt(2, info.visit_count());
170 statement.BindInt(3, info.typed_count());
171 statement.BindInt64(4, info.last_visit().ToInternalValue());
172 statement.BindInt(5, info.hidden() ? 1 : 0);
173
174 if (!statement.Run()) {
175 VLOG(0) << "Failed to add url " << info.url().possibly_invalid_spec()
176 << " to table history.urls.";
177 return 0;
178 }
179 return GetDB().GetLastInsertRowId();
180 }
181
182 bool URLDatabase::DeleteURLRow(URLID id) {
183 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
184 "DELETE FROM urls WHERE id = ?"));
185 statement.BindInt64(0, id);
186
187 if (!statement.Run())
188 return false;
189
190 // And delete any keyword visits.
191 if (!has_keyword_search_terms_)
192 return true;
193
194 sql::Statement del_keyword_visit(GetDB().GetCachedStatement(SQL_FROM_HERE,
195 "DELETE FROM keyword_search_terms WHERE url_id=?"));
196 del_keyword_visit.BindInt64(0, id);
197
198 return del_keyword_visit.Run();
199 }
200
201 bool URLDatabase::CreateTemporaryURLTable() {
202 return CreateURLTable(true);
203 }
204
205 bool URLDatabase::CommitTemporaryURLTable() {
206 // See the comments in the header file as well as
207 // HistoryBackend::DeleteAllHistory() for more information on how this works
208 // and why it does what it does.
209 //
210 // Note that the main database overrides this to additionally create the
211 // supplimentary indices that the archived database doesn't need.
212
213 // Swap the url table out and replace it with the temporary one.
214 if (!GetDB().Execute("DROP TABLE urls")) {
215 NOTREACHED() << GetDB().GetErrorMessage();
216 return false;
217 }
218 if (!GetDB().Execute("ALTER TABLE temp_urls RENAME TO urls")) {
219 NOTREACHED() << GetDB().GetErrorMessage();
220 return false;
221 }
222
223 // Create the index over URLs. This is needed for the main, in-memory, and
224 // archived databases, so we always do it. The supplimentary indices used by
225 // the main database are not created here. When deleting all history, they
226 // are created by HistoryDatabase::RecreateAllButStarAndURLTables().
227 CreateMainURLIndex();
228
229 return true;
230 }
231
232 bool URLDatabase::InitURLEnumeratorForEverything(URLEnumerator* enumerator) {
233 DCHECK(!enumerator->initialized_);
234 std::string sql("SELECT ");
235 sql.append(kURLRowFields);
236 sql.append(" FROM urls");
237 enumerator->statement_.Assign(GetDB().GetUniqueStatement(sql.c_str()));
238 enumerator->initialized_ = enumerator->statement_.is_valid();
239 return enumerator->statement_.is_valid();
240 }
241
242 bool URLDatabase::InitURLEnumeratorForSignificant(URLEnumerator* enumerator) {
243 DCHECK(!enumerator->initialized_);
244 std::string sql("SELECT ");
245 sql.append(kURLRowFields);
246 sql.append(" FROM urls WHERE last_visit_time >= ? OR visit_count >= ? OR "
247 "typed_count >= ?");
248 enumerator->statement_.Assign(GetDB().GetUniqueStatement(sql.c_str()));
249 enumerator->statement_.BindInt64(
250 0, AutocompleteAgeThreshold().ToInternalValue());
251 enumerator->statement_.BindInt(1, kLowQualityMatchVisitLimit);
252 enumerator->statement_.BindInt(2, kLowQualityMatchTypedLimit);
253 enumerator->initialized_ = enumerator->statement_.is_valid();
254 return enumerator->statement_.is_valid();
255 }
256
257 bool URLDatabase::InitIconMappingEnumeratorForEverything(
258 IconMappingEnumerator* enumerator) {
259 DCHECK(!enumerator->initialized_);
260 enumerator->statement_.Assign(GetDB().GetUniqueStatement(
261 "SELECT url, favicon_id FROM urls WHERE favicon_id <> 0"));
262 enumerator->initialized_ = enumerator->statement_.is_valid();
263 return enumerator->statement_.is_valid();
264 }
265
266 bool URLDatabase::AutocompleteForPrefix(const std::string& prefix,
267 size_t max_results,
268 bool typed_only,
269 URLRows* results) {
270 // NOTE: this query originally sorted by starred as the second parameter. But
271 // as bookmarks is no longer part of the db we no longer include the order
272 // by clause.
273 results->clear();
274 const char* sql;
275 int line;
276 if (typed_only) {
277 sql = "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls "
278 "WHERE url >= ? AND url < ? AND hidden = 0 AND typed_count > 0 "
279 "ORDER BY typed_count DESC, visit_count DESC, last_visit_time DESC "
280 "LIMIT ?";
281 line = __LINE__;
282 } else {
283 sql = "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls "
284 "WHERE url >= ? AND url < ? AND hidden = 0 "
285 "ORDER BY typed_count DESC, visit_count DESC, last_visit_time DESC "
286 "LIMIT ?";
287 line = __LINE__;
288 }
289 sql::Statement statement(
290 GetDB().GetCachedStatement(sql::StatementID(__FILE__, line), sql));
291
292 // We will find all strings between "prefix" and this string, which is prefix
293 // followed by the maximum character size. Use 8-bit strings for everything
294 // so we can be sure sqlite is comparing everything in 8-bit mode. Otherwise,
295 // it will have to convert strings either to UTF-8 or UTF-16 for comparison.
296 std::string end_query(prefix);
297 end_query.push_back(std::numeric_limits<unsigned char>::max());
298
299 statement.BindString(0, prefix);
300 statement.BindString(1, end_query);
301 statement.BindInt(2, static_cast<int>(max_results));
302
303 while (statement.Step()) {
304 history::URLRow info;
305 FillURLRow(statement, &info);
306 if (info.url().is_valid())
307 results->push_back(info);
308 }
309 return !results->empty();
310 }
311
312 bool URLDatabase::IsTypedHost(const std::string& host) {
313 const char* schemes[] = {
314 chrome::kHttpScheme,
315 chrome::kHttpsScheme,
316 chrome::kFtpScheme
317 };
318 URLRows dummy;
319 for (size_t i = 0; i < arraysize(schemes); ++i) {
320 std::string scheme_and_host(schemes[i]);
321 scheme_and_host += content::kStandardSchemeSeparator + host;
322 if (AutocompleteForPrefix(scheme_and_host + '/', 1, true, &dummy) ||
323 AutocompleteForPrefix(scheme_and_host + ':', 1, true, &dummy))
324 return true;
325 }
326 return false;
327 }
328
329 bool URLDatabase::FindShortestURLFromBase(const std::string& base,
330 const std::string& url,
331 int min_visits,
332 int min_typed,
333 bool allow_base,
334 history::URLRow* info) {
335 // Select URLs that start with |base| and are prefixes of |url|. All parts
336 // of this query except the substr() call can be done using the index. We
337 // could do this query with a couple of LIKE or GLOB statements as well, but
338 // those wouldn't use the index, and would run into problems with "wildcard"
339 // characters that appear in URLs (% for LIKE, or *, ? for GLOB).
340 std::string sql("SELECT ");
341 sql.append(kURLRowFields);
342 sql.append(" FROM urls WHERE url ");
343 sql.append(allow_base ? ">=" : ">");
344 sql.append(" ? AND url < :end AND url = substr(:end, 1, length(url)) "
345 "AND hidden = 0 AND visit_count >= ? AND typed_count >= ? "
346 "ORDER BY url LIMIT 1");
347 sql::Statement statement(GetDB().GetUniqueStatement(sql.c_str()));
348 statement.BindString(0, base);
349 statement.BindString(1, url); // :end
350 statement.BindInt(2, min_visits);
351 statement.BindInt(3, min_typed);
352
353 if (!statement.Step())
354 return false;
355
356 DCHECK(info);
357 FillURLRow(statement, info);
358 return true;
359 }
360
361 bool URLDatabase::InitKeywordSearchTermsTable() {
362 has_keyword_search_terms_ = true;
363 if (!GetDB().DoesTableExist("keyword_search_terms")) {
364 if (!GetDB().Execute("CREATE TABLE keyword_search_terms ("
365 "keyword_id INTEGER NOT NULL," // ID of the TemplateURL.
366 "url_id INTEGER NOT NULL," // ID of the url.
367 "lower_term LONGVARCHAR NOT NULL," // The search term, in lower case.
368 "term LONGVARCHAR NOT NULL)")) // The actual search term.
369 return false;
370 }
371 return true;
372 }
373
374 bool URLDatabase::CreateKeywordSearchTermsIndices() {
375 // For searching.
376 if (!GetDB().Execute(
377 "CREATE INDEX IF NOT EXISTS keyword_search_terms_index1 ON "
378 "keyword_search_terms (keyword_id, lower_term)")) {
379 return false;
380 }
381
382 // For deletion.
383 if (!GetDB().Execute(
384 "CREATE INDEX IF NOT EXISTS keyword_search_terms_index2 ON "
385 "keyword_search_terms (url_id)")) {
386 return false;
387 }
388
389 // For query or deletion by term.
390 if (!GetDB().Execute(
391 "CREATE INDEX IF NOT EXISTS keyword_search_terms_index3 ON "
392 "keyword_search_terms (term)")) {
393 return false;
394 }
395 return true;
396 }
397
398 bool URLDatabase::DropKeywordSearchTermsTable() {
399 // This will implicitly delete the indices over the table.
400 return GetDB().Execute("DROP TABLE keyword_search_terms");
401 }
402
403 bool URLDatabase::SetKeywordSearchTermsForURL(URLID url_id,
404 TemplateURLID keyword_id,
405 const string16& term) {
406 DCHECK(url_id && keyword_id && !term.empty());
407
408 sql::Statement exist_statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
409 "SELECT term FROM keyword_search_terms "
410 "WHERE keyword_id = ? AND url_id = ?"));
411 exist_statement.BindInt64(0, keyword_id);
412 exist_statement.BindInt64(1, url_id);
413
414 if (exist_statement.Step())
415 return true; // Term already exists, no need to add it.
416
417 if (!exist_statement.Succeeded())
418 return false;
419
420 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
421 "INSERT INTO keyword_search_terms (keyword_id, url_id, lower_term, term) "
422 "VALUES (?,?,?,?)"));
423 statement.BindInt64(0, keyword_id);
424 statement.BindInt64(1, url_id);
425 statement.BindString16(2, base::i18n::ToLower(term));
426 statement.BindString16(3, term);
427 return statement.Run();
428 }
429
430 bool URLDatabase::GetKeywordSearchTermRow(URLID url_id,
431 KeywordSearchTermRow* row) {
432 DCHECK(url_id);
433 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
434 "SELECT keyword_id, term FROM keyword_search_terms WHERE url_id=?"));
435 statement.BindInt64(0, url_id);
436
437 if (!statement.Step())
438 return false;
439
440 if (row) {
441 row->url_id = url_id;
442 row->keyword_id = statement.ColumnInt64(0);
443 row->term = statement.ColumnString16(1);
444 }
445 return true;
446 }
447
448 bool URLDatabase::GetKeywordSearchTermRows(
449 const string16& term,
450 std::vector<KeywordSearchTermRow>* rows) {
451 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
452 "SELECT keyword_id, url_id FROM keyword_search_terms WHERE term=?"));
453 statement.BindString16(0, term);
454
455 if (!statement.is_valid())
456 return false;
457
458 while (statement.Step()) {
459 KeywordSearchTermRow row;
460 row.url_id = statement.ColumnInt64(1);
461 row.keyword_id = statement.ColumnInt64(0);
462 row.term = term;
463 rows->push_back(row);
464 }
465 return true;
466 }
467
468 void URLDatabase::DeleteAllSearchTermsForKeyword(
469 TemplateURLID keyword_id) {
470 DCHECK(keyword_id);
471 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
472 "DELETE FROM keyword_search_terms WHERE keyword_id=?"));
473 statement.BindInt64(0, keyword_id);
474
475 statement.Run();
476 }
477
478 void URLDatabase::GetMostRecentKeywordSearchTerms(
479 TemplateURLID keyword_id,
480 const string16& prefix,
481 int max_count,
482 std::vector<KeywordSearchTermVisit>* matches) {
483 // NOTE: the keyword_id can be zero if on first run the user does a query
484 // before the TemplateURLService has finished loading. As the chances of this
485 // occurring are small, we ignore it.
486 if (!keyword_id)
487 return;
488
489 DCHECK(!prefix.empty());
490 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
491 "SELECT DISTINCT kv.term, u.visit_count, u.last_visit_time "
492 "FROM keyword_search_terms kv "
493 "JOIN urls u ON kv.url_id = u.id "
494 "WHERE kv.keyword_id = ? AND kv.lower_term >= ? AND kv.lower_term < ? "
495 "ORDER BY u.last_visit_time DESC LIMIT ?"));
496
497 // NOTE: Keep this ToLower() call in sync with search_provider.cc.
498 string16 lower_prefix = base::i18n::ToLower(prefix);
499 // This magic gives us a prefix search.
500 string16 next_prefix = lower_prefix;
501 next_prefix[next_prefix.size() - 1] =
502 next_prefix[next_prefix.size() - 1] + 1;
503 statement.BindInt64(0, keyword_id);
504 statement.BindString16(1, lower_prefix);
505 statement.BindString16(2, next_prefix);
506 statement.BindInt(3, max_count);
507
508 KeywordSearchTermVisit visit;
509 while (statement.Step()) {
510 visit.term = statement.ColumnString16(0);
511 visit.visits = statement.ColumnInt(1);
512 visit.time = base::Time::FromInternalValue(statement.ColumnInt64(2));
513 matches->push_back(visit);
514 }
515 }
516
517 bool URLDatabase::DeleteKeywordSearchTerm(const string16& term) {
518 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
519 "DELETE FROM keyword_search_terms WHERE term=?"));
520 statement.BindString16(0, term);
521
522 return statement.Run();
523 }
524
525 bool URLDatabase::DropStarredIDFromURLs() {
526 if (!GetDB().DoesColumnExist("urls", "starred_id"))
527 return true; // urls is already updated, no need to continue.
528
529 // Create a temporary table to contain the new URLs table.
530 if (!CreateTemporaryURLTable()) {
531 NOTREACHED();
532 return false;
533 }
534
535 // Copy the contents.
536 if (!GetDB().Execute(
537 "INSERT INTO temp_urls (id, url, title, visit_count, typed_count, "
538 "last_visit_time, hidden, favicon_id) "
539 "SELECT id, url, title, visit_count, typed_count, last_visit_time, "
540 "hidden, favicon_id FROM urls")) {
541 NOTREACHED() << GetDB().GetErrorMessage();
542 return false;
543 }
544
545 // Rename/commit the tmp table.
546 CommitTemporaryURLTable();
547
548 return true;
549 }
550
551 bool URLDatabase::CreateURLTable(bool is_temporary) {
552 const char* name = is_temporary ? "temp_urls" : "urls";
553 if (GetDB().DoesTableExist(name))
554 return true;
555
556 std::string sql;
557 sql.append("CREATE TABLE ");
558 sql.append(name);
559 sql.append("("
560 "id INTEGER PRIMARY KEY,"
561 "url LONGVARCHAR,"
562 "title LONGVARCHAR,"
563 "visit_count INTEGER DEFAULT 0 NOT NULL,"
564 "typed_count INTEGER DEFAULT 0 NOT NULL,"
565 "last_visit_time INTEGER NOT NULL,"
566 "hidden INTEGER DEFAULT 0 NOT NULL,"
567 "favicon_id INTEGER DEFAULT 0 NOT NULL)"); // favicon_id is not used now.
568
569 return GetDB().Execute(sql.c_str());
570 }
571
572 bool URLDatabase::CreateMainURLIndex() {
573 // Index over URLs so we can quickly look up based on URL.
574 return GetDB().Execute(
575 "CREATE INDEX IF NOT EXISTS urls_url_index ON urls (url)");
576 }
577
578 } // namespace history
OLDNEW
« no previous file with comments | « chrome/browser/history/url_database.h ('k') | chrome/browser/history/url_database_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698