| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/history/core/browser/url_database.h" | 5 #include "components/history/core/browser/url_database.h" |
| 6 | 6 |
| 7 #include <limits> | 7 #include <limits> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 51 // TODO(brettw): do something fancy here with encoding, etc. | 51 // TODO(brettw): do something fancy here with encoding, etc. |
| 52 | 52 |
| 53 // Strip username and password from URL before sending to DB. | 53 // Strip username and password from URL before sending to DB. |
| 54 GURL::Replacements replacements; | 54 GURL::Replacements replacements; |
| 55 replacements.ClearUsername(); | 55 replacements.ClearUsername(); |
| 56 replacements.ClearPassword(); | 56 replacements.ClearPassword(); |
| 57 | 57 |
| 58 return (gurl.ReplaceComponents(replacements)).spec(); | 58 return (gurl.ReplaceComponents(replacements)).spec(); |
| 59 } | 59 } |
| 60 | 60 |
| 61 // Convenience to fill a history::URLRow. Must be in sync with the fields in | 61 // Convenience to fill a URLRow. Must be in sync with the fields in |
| 62 // kURLRowFields. | 62 // kURLRowFields. |
| 63 void URLDatabase::FillURLRow(sql::Statement& s, history::URLRow* i) { | 63 void URLDatabase::FillURLRow(sql::Statement& s, URLRow* i) { |
| 64 DCHECK(i); | 64 DCHECK(i); |
| 65 i->id_ = s.ColumnInt64(0); | 65 i->id_ = s.ColumnInt64(0); |
| 66 i->url_ = GURL(s.ColumnString(1)); | 66 i->url_ = GURL(s.ColumnString(1)); |
| 67 i->title_ = s.ColumnString16(2); | 67 i->title_ = s.ColumnString16(2); |
| 68 i->visit_count_ = s.ColumnInt(3); | 68 i->visit_count_ = s.ColumnInt(3); |
| 69 i->typed_count_ = s.ColumnInt(4); | 69 i->typed_count_ = s.ColumnInt(4); |
| 70 i->last_visit_ = base::Time::FromInternalValue(s.ColumnInt64(5)); | 70 i->last_visit_ = base::Time::FromInternalValue(s.ColumnInt64(5)); |
| 71 i->hidden_ = s.ColumnInt(6) != 0; | 71 i->hidden_ = s.ColumnInt(6) != 0; |
| 72 } | 72 } |
| 73 | 73 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 92 "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls WHERE typed_count > 0")); | 92 "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls WHERE typed_count > 0")); |
| 93 | 93 |
| 94 while (statement.Step()) { | 94 while (statement.Step()) { |
| 95 URLRow info; | 95 URLRow info; |
| 96 FillURLRow(statement, &info); | 96 FillURLRow(statement, &info); |
| 97 urls->push_back(info); | 97 urls->push_back(info); |
| 98 } | 98 } |
| 99 return true; | 99 return true; |
| 100 } | 100 } |
| 101 | 101 |
| 102 URLID URLDatabase::GetRowForURL(const GURL& url, history::URLRow* info) { | 102 URLID URLDatabase::GetRowForURL(const GURL& url, URLRow* info) { |
| 103 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 103 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| 104 "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls WHERE url=?")); | 104 "SELECT" HISTORY_URL_ROW_FIELDS "FROM urls WHERE url=?")); |
| 105 std::string url_string = GURLToDatabaseURL(url); | 105 std::string url_string = GURLToDatabaseURL(url); |
| 106 statement.BindString(0, url_string); | 106 statement.BindString(0, url_string); |
| 107 | 107 |
| 108 if (!statement.Step()) | 108 if (!statement.Step()) |
| 109 return 0; // no data | 109 return 0; // no data |
| 110 | 110 |
| 111 if (info) | 111 if (info) |
| 112 FillURLRow(statement, info); | 112 FillURLRow(statement, info); |
| 113 return statement.ColumnInt64(0); | 113 return statement.ColumnInt64(0); |
| 114 } | 114 } |
| 115 | 115 |
| 116 bool URLDatabase::UpdateURLRow(URLID url_id, | 116 bool URLDatabase::UpdateURLRow(URLID url_id, const URLRow& info) { |
| 117 const history::URLRow& info) { | |
| 118 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, | 117 sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| 119 "UPDATE urls SET title=?,visit_count=?,typed_count=?,last_visit_time=?," | 118 "UPDATE urls SET title=?,visit_count=?,typed_count=?,last_visit_time=?," |
| 120 "hidden=?" | 119 "hidden=?" |
| 121 "WHERE id=?")); | 120 "WHERE id=?")); |
| 122 statement.BindString16(0, info.title()); | 121 statement.BindString16(0, info.title()); |
| 123 statement.BindInt(1, info.visit_count()); | 122 statement.BindInt(1, info.visit_count()); |
| 124 statement.BindInt(2, info.typed_count()); | 123 statement.BindInt(2, info.typed_count()); |
| 125 statement.BindInt64(3, info.last_visit().ToInternalValue()); | 124 statement.BindInt64(3, info.last_visit().ToInternalValue()); |
| 126 statement.BindInt(4, info.hidden() ? 1 : 0); | 125 statement.BindInt(4, info.hidden() ? 1 : 0); |
| 127 statement.BindInt64(5, url_id); | 126 statement.BindInt64(5, url_id); |
| 128 | 127 |
| 129 return statement.Run() && GetDB().GetLastChangeCount() > 0; | 128 return statement.Run() && GetDB().GetLastChangeCount() > 0; |
| 130 } | 129 } |
| 131 | 130 |
| 132 URLID URLDatabase::AddURLInternal(const history::URLRow& info, | 131 URLID URLDatabase::AddURLInternal(const URLRow& info, bool is_temporary) { |
| 133 bool is_temporary) { | |
| 134 // This function is used to insert into two different tables, so we have to | 132 // This function is used to insert into two different tables, so we have to |
| 135 // do some shuffling. Unfortinately, we can't use the macro | 133 // do some shuffling. Unfortinately, we can't use the macro |
| 136 // HISTORY_URL_ROW_FIELDS because that specifies the table name which is | 134 // HISTORY_URL_ROW_FIELDS because that specifies the table name which is |
| 137 // invalid in the insert syntax. | 135 // invalid in the insert syntax. |
| 138 #define ADDURL_COMMON_SUFFIX \ | 136 #define ADDURL_COMMON_SUFFIX \ |
| 139 " (url, title, visit_count, typed_count, "\ | 137 " (url, title, visit_count, typed_count, "\ |
| 140 "last_visit_time, hidden) "\ | 138 "last_visit_time, hidden) "\ |
| 141 "VALUES (?,?,?,?,?,?)" | 139 "VALUES (?,?,?,?,?,?)" |
| 142 const char* statement_name; | 140 const char* statement_name; |
| 143 const char* statement_sql; | 141 const char* statement_sql; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 160 statement.BindInt(5, info.hidden() ? 1 : 0); | 158 statement.BindInt(5, info.hidden() ? 1 : 0); |
| 161 | 159 |
| 162 if (!statement.Run()) { | 160 if (!statement.Run()) { |
| 163 VLOG(0) << "Failed to add url " << info.url().possibly_invalid_spec() | 161 VLOG(0) << "Failed to add url " << info.url().possibly_invalid_spec() |
| 164 << " to table history.urls."; | 162 << " to table history.urls."; |
| 165 return 0; | 163 return 0; |
| 166 } | 164 } |
| 167 return GetDB().GetLastInsertRowId(); | 165 return GetDB().GetLastInsertRowId(); |
| 168 } | 166 } |
| 169 | 167 |
| 170 bool URLDatabase::InsertOrUpdateURLRowByID(const history::URLRow& info) { | 168 bool URLDatabase::InsertOrUpdateURLRowByID(const URLRow& info) { |
| 171 // SQLite does not support INSERT OR UPDATE, however, it does have INSERT OR | 169 // SQLite does not support INSERT OR UPDATE, however, it does have INSERT OR |
| 172 // REPLACE, which is feasible to use, because of the following. | 170 // REPLACE, which is feasible to use, because of the following. |
| 173 // * Before INSERTing, REPLACE will delete all pre-existing rows that cause | 171 // * Before INSERTing, REPLACE will delete all pre-existing rows that cause |
| 174 // constraint violations. Here, we only have a PRIMARY KEY constraint, so | 172 // constraint violations. Here, we only have a PRIMARY KEY constraint, so |
| 175 // the only row that might get deleted is an old one with the same ID. | 173 // the only row that might get deleted is an old one with the same ID. |
| 176 // * Another difference between the two flavors is that the latter actually | 174 // * Another difference between the two flavors is that the latter actually |
| 177 // deletes the old row, and thus the old values are lost in columns which | 175 // deletes the old row, and thus the old values are lost in columns which |
| 178 // are not explicitly assigned new values. This is not an issue, however, | 176 // are not explicitly assigned new values. This is not an issue, however, |
| 179 // as we assign values to all columns. | 177 // as we assign values to all columns. |
| 180 // * When rows are deleted due to constraint violations, the delete triggers | 178 // * When rows are deleted due to constraint violations, the delete triggers |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 290 // so we can be sure sqlite is comparing everything in 8-bit mode. Otherwise, | 288 // so we can be sure sqlite is comparing everything in 8-bit mode. Otherwise, |
| 291 // it will have to convert strings either to UTF-8 or UTF-16 for comparison. | 289 // it will have to convert strings either to UTF-8 or UTF-16 for comparison. |
| 292 std::string end_query(prefix); | 290 std::string end_query(prefix); |
| 293 end_query.push_back(std::numeric_limits<unsigned char>::max()); | 291 end_query.push_back(std::numeric_limits<unsigned char>::max()); |
| 294 | 292 |
| 295 statement.BindString(0, prefix); | 293 statement.BindString(0, prefix); |
| 296 statement.BindString(1, end_query); | 294 statement.BindString(1, end_query); |
| 297 statement.BindInt(2, static_cast<int>(max_results)); | 295 statement.BindInt(2, static_cast<int>(max_results)); |
| 298 | 296 |
| 299 while (statement.Step()) { | 297 while (statement.Step()) { |
| 300 history::URLRow info; | 298 URLRow info; |
| 301 FillURLRow(statement, &info); | 299 FillURLRow(statement, &info); |
| 302 if (info.url().is_valid()) | 300 if (info.url().is_valid()) |
| 303 results->push_back(info); | 301 results->push_back(info); |
| 304 } | 302 } |
| 305 return !results->empty(); | 303 return !results->empty(); |
| 306 } | 304 } |
| 307 | 305 |
| 308 bool URLDatabase::IsTypedHost(const std::string& host) { | 306 bool URLDatabase::IsTypedHost(const std::string& host) { |
| 309 const char* schemes[] = { | 307 const char* schemes[] = { |
| 310 url::kHttpScheme, | 308 url::kHttpScheme, |
| 311 url::kHttpsScheme, | 309 url::kHttpsScheme, |
| 312 url::kFtpScheme | 310 url::kFtpScheme |
| 313 }; | 311 }; |
| 314 URLRows dummy; | 312 URLRows dummy; |
| 315 for (size_t i = 0; i < arraysize(schemes); ++i) { | 313 for (size_t i = 0; i < arraysize(schemes); ++i) { |
| 316 std::string scheme_and_host(schemes[i]); | 314 std::string scheme_and_host(schemes[i]); |
| 317 scheme_and_host += url::kStandardSchemeSeparator + host; | 315 scheme_and_host += url::kStandardSchemeSeparator + host; |
| 318 if (AutocompleteForPrefix(scheme_and_host + '/', 1, true, &dummy) || | 316 if (AutocompleteForPrefix(scheme_and_host + '/', 1, true, &dummy) || |
| 319 AutocompleteForPrefix(scheme_and_host + ':', 1, true, &dummy)) | 317 AutocompleteForPrefix(scheme_and_host + ':', 1, true, &dummy)) |
| 320 return true; | 318 return true; |
| 321 } | 319 } |
| 322 return false; | 320 return false; |
| 323 } | 321 } |
| 324 | 322 |
| 325 bool URLDatabase::FindShortestURLFromBase(const std::string& base, | 323 bool URLDatabase::FindShortestURLFromBase(const std::string& base, |
| 326 const std::string& url, | 324 const std::string& url, |
| 327 int min_visits, | 325 int min_visits, |
| 328 int min_typed, | 326 int min_typed, |
| 329 bool allow_base, | 327 bool allow_base, |
| 330 history::URLRow* info) { | 328 URLRow* info) { |
| 331 // Select URLs that start with |base| and are prefixes of |url|. All parts | 329 // Select URLs that start with |base| and are prefixes of |url|. All parts |
| 332 // of this query except the substr() call can be done using the index. We | 330 // of this query except the substr() call can be done using the index. We |
| 333 // could do this query with a couple of LIKE or GLOB statements as well, but | 331 // could do this query with a couple of LIKE or GLOB statements as well, but |
| 334 // those wouldn't use the index, and would run into problems with "wildcard" | 332 // those wouldn't use the index, and would run into problems with "wildcard" |
| 335 // characters that appear in URLs (% for LIKE, or *, ? for GLOB). | 333 // characters that appear in URLs (% for LIKE, or *, ? for GLOB). |
| 336 std::string sql("SELECT "); | 334 std::string sql("SELECT "); |
| 337 sql.append(kURLRowFields); | 335 sql.append(kURLRowFields); |
| 338 sql.append(" FROM urls WHERE url "); | 336 sql.append(" FROM urls WHERE url "); |
| 339 sql.append(allow_base ? ">=" : ">"); | 337 sql.append(allow_base ? ">=" : ">"); |
| 340 sql.append(" ? AND url < :end AND url = substr(:end, 1, length(url)) " | 338 sql.append(" ? AND url < :end AND url = substr(:end, 1, length(url)) " |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 375 // |languages| to reduce dependency (no need to call PrefService). | 373 // |languages| to reduce dependency (no need to call PrefService). |
| 376 base::string16 ascii = base::ASCIIToUTF16(gurl.host()); | 374 base::string16 ascii = base::ASCIIToUTF16(gurl.host()); |
| 377 base::string16 utf = net::IDNToUnicode(gurl.host(), std::string()); | 375 base::string16 utf = net::IDNToUnicode(gurl.host(), std::string()); |
| 378 if (ascii != utf) | 376 if (ascii != utf) |
| 379 query_parser_.ExtractQueryWords(utf, &query_words); | 377 query_parser_.ExtractQueryWords(utf, &query_words); |
| 380 } | 378 } |
| 381 base::string16 title = base::i18n::ToLower(statement.ColumnString16(2)); | 379 base::string16 title = base::i18n::ToLower(statement.ColumnString16(2)); |
| 382 query_parser_.ExtractQueryWords(title, &query_words); | 380 query_parser_.ExtractQueryWords(title, &query_words); |
| 383 | 381 |
| 384 if (query_parser_.DoesQueryMatch(query_words, query_nodes.get())) { | 382 if (query_parser_.DoesQueryMatch(query_words, query_nodes.get())) { |
| 385 history::URLResult info; | 383 URLResult info; |
| 386 FillURLRow(statement, &info); | 384 FillURLRow(statement, &info); |
| 387 if (info.url().is_valid()) | 385 if (info.url().is_valid()) |
| 388 results->push_back(info); | 386 results->push_back(info); |
| 389 } | 387 } |
| 390 } | 388 } |
| 391 return !results->empty(); | 389 return !results->empty(); |
| 392 } | 390 } |
| 393 | 391 |
| 394 bool URLDatabase::InitKeywordSearchTermsTable() { | 392 bool URLDatabase::InitKeywordSearchTermsTable() { |
| 395 has_keyword_search_terms_ = true; | 393 has_keyword_search_terms_ = true; |
| (...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 628 bool RowQualifiesAsSignificant(const URLRow& row, | 626 bool RowQualifiesAsSignificant(const URLRow& row, |
| 629 const base::Time& threshold) { | 627 const base::Time& threshold) { |
| 630 const base::Time& real_threshold = | 628 const base::Time& real_threshold = |
| 631 threshold.is_null() ? AutocompleteAgeThreshold() : threshold; | 629 threshold.is_null() ? AutocompleteAgeThreshold() : threshold; |
| 632 return (row.typed_count() >= kLowQualityMatchTypedLimit) || | 630 return (row.typed_count() >= kLowQualityMatchTypedLimit) || |
| 633 (row.visit_count() >= kLowQualityMatchVisitLimit) || | 631 (row.visit_count() >= kLowQualityMatchVisitLimit) || |
| 634 (row.last_visit() >= real_threshold); | 632 (row.last_visit() >= real_threshold); |
| 635 } | 633 } |
| 636 | 634 |
| 637 } // namespace history | 635 } // namespace history |
| OLD | NEW |