summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authormichaelbai@chromium.org <michaelbai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-31 04:23:22 +0000
committermichaelbai@chromium.org <michaelbai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-03-31 04:23:22 +0000
commitf0b437f60f00985fca65e5095be3eed1402489f8 (patch)
tree5c09e900a6b4dbf3fa9ab4d0978efb36fe0360fc /chrome
parentbd844b34a70fd98844081ed3d9c99b680556384b (diff)
downloadchromium_src-f0b437f60f00985fca65e5095be3eed1402489f8.zip
chromium_src-f0b437f60f00985fca65e5095be3eed1402489f8.tar.gz
chromium_src-f0b437f60f00985fca65e5095be3eed1402489f8.tar.bz2
Implement the Android API for search terms.
- Added the keyword_cache table in AndroidCacheDatabase. - Added the methods for getting and deleting the terms by term in URLDatabase. - Added the index of term for keyword_search_term table. BUG= TEST= Review URL: http://codereview.chromium.org/9887004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@130041 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/history/android/android_cache_database.cc95
-rw-r--r--chrome/browser/history/android/android_cache_database.h48
-rw-r--r--chrome/browser/history/android/android_cache_database_unittest.cc82
-rw-r--r--chrome/browser/history/android/android_history_types.cc15
-rw-r--r--chrome/browser/history/android/android_history_types.h30
-rw-r--r--chrome/browser/history/android/android_provider_backend.cc284
-rw-r--r--chrome/browser/history/android/android_provider_backend.h60
-rw-r--r--chrome/browser/history/android/android_provider_backend_unittest.cc361
-rw-r--r--chrome/browser/history/url_database.cc34
-rw-r--r--chrome/browser/history/url_database.h12
-rw-r--r--chrome/browser/history/url_database_unittest.cc76
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/chrome_tests.gypi5
13 files changed, 1066 insertions, 38 deletions
diff --git a/chrome/browser/history/android/android_cache_database.cc b/chrome/browser/history/android/android_cache_database.cc
index 926e099..4fb5422 100644
--- a/chrome/browser/history/android/android_cache_database.cc
+++ b/chrome/browser/history/android/android_cache_database.cc
@@ -30,6 +30,9 @@ sql::InitStatus AndroidCacheDatabase::InitAndroidCacheDatabase(
if (!CreateBookmarkCacheTable())
return sql::INIT_FAILURE;
+ if (!CreateSearchTermsTable())
+ return sql::INIT_FAILURE;
+
return sql::INIT_OK;
}
@@ -103,6 +106,71 @@ bool AndroidCacheDatabase::SetFaviconID(URLID url_id, FaviconID favicon_id) {
return true;
}
+SearchTermID AndroidCacheDatabase::AddSearchTerm(
+ const string16& term,
+ const base::Time& last_visit_time) {
+ sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
+ "INSERT INTO android_cache_db.search_terms (search, "
+ "date) VALUES (?, ?)"));
+
+ statement.BindString16(0, term);
+ statement.BindInt64(1, ToMilliseconds(last_visit_time));
+
+ if (!statement.Run()) {
+ LOG(ERROR) << GetDB().GetErrorMessage();
+ return 0;
+ }
+
+ return GetDB().GetLastInsertRowId();
+}
+
+bool AndroidCacheDatabase::UpdateSearchTerm(SearchTermID id,
+ const SearchTermRow& row) {
+ sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
+ "UPDATE android_cache_db.search_terms "
+ "SET search = ?, date = ? "
+ "WHERE _id = ?"
+ ));
+ statement.BindString16(0, row.term);
+ statement.BindInt64(1, ToMilliseconds(row.last_visit_time));
+ statement.BindInt64(2, id);
+
+ return statement.Run();
+}
+
+SearchTermID AndroidCacheDatabase::GetSearchTerm(const string16& term,
+ SearchTermRow* row) {
+ sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
+ "SELECT _id, search, date "
+ "FROM android_cache_db.search_terms "
+ "WHERE search = ?"
+ ));
+ if (!statement.is_valid()) {
+ LOG(ERROR) << GetDB().GetErrorMessage();
+ return 0;
+ }
+ statement.BindString16(0, term);
+ if (!statement.Step())
+ return 0;
+
+ if (row) {
+ row->id = statement.ColumnInt64(0);
+ row->term = statement.ColumnString16(1);
+ row->last_visit_time = MillisecondsToTime(statement.ColumnInt64(2));
+ }
+ return statement.ColumnInt64(0);
+}
+
+bool AndroidCacheDatabase::DeleteUnusedSearchTerms() {
+ sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
+ "DELETE FROM android_cache_db.search_terms "
+ "WHERE search NOT IN (SELECT DISTINCT term FROM keyword_search_terms)"
+ ));
+ if (!statement.is_valid())
+ return false;
+ return statement.Run();
+}
+
bool AndroidCacheDatabase::CreateDatabase(const FilePath& db_name) {
db_name_ = db_name;
if (file_util::PathExists(db_name_))
@@ -158,6 +226,33 @@ bool AndroidCacheDatabase::CreateBookmarkCacheTable() {
return true;
}
+bool AndroidCacheDatabase::CreateSearchTermsTable() {
+ const char* name = "android_cache_db.search_terms";
+
+ // The table's column name matchs Android's definition.
+ std::string sql;
+ sql.append("CREATE TABLE ");
+ sql.append(name);
+ sql.append("("
+ "_id INTEGER PRIMARY KEY,"
+ "date INTEGER NOT NULL," // last visit time in millisecond.
+ "search LONGVARCHAR NOT NULL)"); // The actual search term.
+
+ if (!GetDB().Execute(sql.c_str())) {
+ LOG(ERROR) << GetDB().GetErrorMessage();
+ return false;
+ }
+
+ sql.assign("CREATE INDEX "
+ "android_cache_db.search_terms_term_idx ON "
+ "search_terms(search)");
+ if (!GetDB().Execute(sql.c_str())) {
+ LOG(ERROR) << GetDB().GetErrorMessage();
+ return false;
+ }
+ return true;
+}
+
bool AndroidCacheDatabase::Attach() {
// Commit all open transactions to make attach succeed.
if (GetDB().transaction_nesting())
diff --git a/chrome/browser/history/android/android_cache_database.h b/chrome/browser/history/android/android_cache_database.h
index 7363e06..99e3451 100644
--- a/chrome/browser/history/android/android_cache_database.h
+++ b/chrome/browser/history/android/android_cache_database.h
@@ -23,17 +23,19 @@ class AndroidCacheDatabase {
AndroidCacheDatabase();
virtual ~AndroidCacheDatabase();
- // Creates the database, deletes existing one if any; Also attach it to the
+ // Creates the database, deletes existing one if any; also attach it to the
// database returned by GetDB(). Returns sql::INIT_OK on success, otherwise
// sql::INIT_FAILURE returned.
sql::InitStatus InitAndroidCacheDatabase(const FilePath& db_name);
+ // The bookmark_cache table ------------------------------------------------
+ //
// Adds a row to the bookmark_cache table. Returns true on success.
bool AddBookmarkCacheRow(const base::Time& created_time,
const base::Time& last_visit_time,
URLID url_id);
- // Clears all rows in the bookmark_cache table; Returns true on success.
+ // Clears all rows in the bookmark_cache table; returns true on success.
bool ClearAllBookmarkCache();
// Marks the given |url_ids| as bookmarked; Returns true on success.
@@ -43,20 +45,38 @@ class AndroidCacheDatabase {
// success.
bool SetFaviconID(URLID url_id, FaviconID favicon_id);
+ // The search_terms table -------------------------------------------------
+ //
+ // Add a row in the search_term table with the given |term| and
+ // |last_visit_time|. Return the new row's id on success, otherwise 0 is
+ // returned.
+ SearchTermID AddSearchTerm(const string16& term,
+ const base::Time& last_visit_time);
+
+ // Updates the |id|'s row with the given |row|; returns true on success.
+ bool UpdateSearchTerm(SearchTermID id, const SearchTermRow& row);
+
+ // Get SearchTermRow of the given |term|; return the row id on success.
+ // otherwise 0 is returned.
+ // The found row is return in |row| if it is not NULL.
+ SearchTermID GetSearchTerm(const string16& term, SearchTermRow* row);
+
+ // Delete the search terms which don't exist in keyword_search_terms table.
+ bool DeleteUnusedSearchTerms();
+
protected:
// Returns the database for the functions in this interface. The decendent of
// this class implements these functions to return its objects.
virtual sql::Connection& GetDB() = 0;
private:
- FRIEND_TEST_ALL_PREFIXES(AndroidCacheDatabaseTest,
- CreateDatabase);
+ FRIEND_TEST_ALL_PREFIXES(AndroidCacheDatabaseTest, InitAndroidCacheDatabase);
- // Creates the database and make it ready for attaching; Returns true on
+ // Creates the database and make it ready for attaching; returns true on
// success.
bool CreateDatabase(const FilePath& db_name);
- // Creates the bookmark_cache table in attached DB; Returns true on success.
+ // Creates the bookmark_cache table in attached DB; returns true on success.
// The created_time, last_visit_time, favicon_id and bookmark are stored.
//
// The created_time and last_visit_time are cached because Android use the
@@ -71,7 +91,21 @@ class AndroidCacheDatabase {
// Bookmark column is used to indicate whether the url is bookmarked.
bool CreateBookmarkCacheTable();
- // Attachs to history database; Returns true on success.
+ // Creates the search_terms table in attached DB; returns true on success.
+ // This table has _id, search, and date fields which match the Android's
+ // definition.
+ //
+ // When Android Client require update the search term, the search term can't
+ // be updated as it always associated a URL. We simulate the update by
+ // deleting the old search term then inserting a new one, but the ID given
+ // to client can not be changed, so it appears to client as update. This
+ // table is used to mapping the ID given to client to the search term.
+ //
+ // The search term last visit time is stored in date as Android needs the time
+ // in milliseconds.
+ bool CreateSearchTermsTable();
+
+ // Attachs to history database; returns true on success.
bool Attach();
// Does the real attach. Returns true on success.
diff --git a/chrome/browser/history/android/android_cache_database_unittest.cc b/chrome/browser/history/android/android_cache_database_unittest.cc
index 7256785..84faf63 100644
--- a/chrome/browser/history/android/android_cache_database_unittest.cc
+++ b/chrome/browser/history/android/android_cache_database_unittest.cc
@@ -5,27 +5,17 @@
#include "base/file_path.h"
#include "base/file_util.h"
#include "base/scoped_temp_dir.h"
+#include "base/utf_string_conversions.h"
#include "chrome/browser/history/android/android_cache_database.h"
+#include "chrome/browser/history/android/android_time.h"
#include "chrome/browser/history/history_database.h"
#include "chrome/test/base/testing_profile.h"
#include "testing/gtest/include/gtest/gtest.h"
-namespace history {
-
-class SimpleAndroidCacheDatabase : public AndroidCacheDatabase {
- public:
- explicit SimpleAndroidCacheDatabase(sql::Connection* connection)
- :db_(connection) {
- }
-
- protected:
- sql::Connection& GetDB() {
- return *db_;
- }
+using base::Time;
+using base::TimeDelta;
- private:
- sql::Connection* db_;
-};
+namespace history {
class AndroidCacheDatabaseTest : public testing::Test {
public:
@@ -38,25 +28,69 @@ class AndroidCacheDatabaseTest : public testing::Test {
virtual void SetUp() {
// Get a temporary directory for the test DB files.
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
- main_db_name_ = temp_dir_.path().AppendASCII("history.db");
+ FilePath history_db_name_ = temp_dir_.path().AppendASCII("history.db");
android_cache_db_name_ = temp_dir_.path().AppendASCII(
"TestAndroidCache.db");
+ ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_,
+ temp_dir_.path()));
+ ASSERT_EQ(sql::INIT_OK,
+ history_db_.InitAndroidCacheDatabase(android_cache_db_name_));
}
ScopedTempDir temp_dir_;
FilePath android_cache_db_name_;
- FilePath main_db_name_;
+ HistoryDatabase history_db_;
};
TEST_F(AndroidCacheDatabaseTest, InitAndroidCacheDatabase) {
- sql::Connection connection;
- ASSERT_TRUE(connection.Open(main_db_name_));
- SimpleAndroidCacheDatabase android_cache_db(&connection);
- EXPECT_EQ(sql::INIT_OK,
- android_cache_db.InitAndroidCacheDatabase(android_cache_db_name_));
- // Try to run a sql against the table to verify it exists.
- EXPECT_TRUE(connection.Execute(
+ // Try to run a sql against the table to verify them exist.
+ AndroidCacheDatabase* cache_db =
+ static_cast<AndroidCacheDatabase*>(&history_db_);
+ EXPECT_TRUE(cache_db->GetDB().Execute(
"DELETE FROM android_cache_db.bookmark_cache"));
+ EXPECT_TRUE(cache_db->GetDB().Execute(
+ "DELETE FROM android_cache_db.search_terms"));
+}
+
+TEST_F(AndroidCacheDatabaseTest, SearchTermsTable) {
+ // Test AddSearchTerm.
+ Time search_time1 = Time::Now() - TimeDelta::FromDays(1);
+ string16 search_term1(UTF8ToUTF16("search term 1"));
+ SearchTermID id1 = history_db_.AddSearchTerm(search_term1, search_time1);
+ ASSERT_TRUE(id1);
+ SearchTermRow row1;
+ ASSERT_EQ(id1, history_db_.GetSearchTerm(search_term1, &row1));
+ EXPECT_EQ(search_term1, row1.term);
+ EXPECT_EQ(ToMilliseconds(search_time1),
+ ToMilliseconds(row1.last_visit_time));
+ EXPECT_EQ(id1, row1.id);
+
+ // Test UpdateSearchTerm.
+ SearchTermRow update_row1;
+ update_row1.term = (UTF8ToUTF16("update search term1"));
+ update_row1.last_visit_time = Time::Now();
+ ASSERT_TRUE(history_db_.UpdateSearchTerm(id1, update_row1));
+ EXPECT_EQ(id1, history_db_.GetSearchTerm(update_row1.term, &row1));
+ EXPECT_EQ(update_row1.term, row1.term);
+ EXPECT_EQ(ToMilliseconds(update_row1.last_visit_time),
+ ToMilliseconds(row1.last_visit_time));
+ EXPECT_EQ(id1, row1.id);
+
+ Time search_time2 = Time::Now() - TimeDelta::FromHours(1);
+ string16 search_term2(UTF8ToUTF16("search term 2"));
+ SearchTermID id2 = history_db_.AddSearchTerm(search_term2, search_time2);
+ ASSERT_TRUE(id2);
+ ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(1, 1, search_term2));
+ ASSERT_TRUE(history_db_.DeleteUnusedSearchTerms());
+
+ // The search_term1 was removed.
+ EXPECT_FALSE(history_db_.GetSearchTerm(update_row1.term, NULL));
+ // The search_term2 should still in the table.
+ ASSERT_EQ(id2, history_db_.GetSearchTerm(search_term2, &row1));
+ EXPECT_EQ(id2, row1.id);
+ EXPECT_EQ(ToMilliseconds(search_time2),
+ ToMilliseconds(row1.last_visit_time));
+ EXPECT_EQ(search_term2, row1.term);
}
} // namespace history
diff --git a/chrome/browser/history/android/android_history_types.cc b/chrome/browser/history/android/android_history_types.cc
index 29339fd..42d82b0 100644
--- a/chrome/browser/history/android/android_history_types.cc
+++ b/chrome/browser/history/android/android_history_types.cc
@@ -116,6 +116,21 @@ SearchRow::SearchColumnID SearchRow::GetSearchColumnID(
return i->second;
}
+AndroidURLRow::AndroidURLRow()
+ : id(0),
+ url_id(0) {
+}
+
+AndroidURLRow::~AndroidURLRow() {
+}
+
+SearchTermRow::SearchTermRow()
+ : id(0) {
+}
+
+SearchTermRow::~SearchTermRow() {
+}
+
AndroidStatement::AndroidStatement(sql::Statement* statement, int favicon_index)
: statement_(statement),
favicon_index_(favicon_index) {
diff --git a/chrome/browser/history/android/android_history_types.h b/chrome/browser/history/android/android_history_types.h
index ab99832..c09a5e1 100644
--- a/chrome/browser/history/android/android_history_types.h
+++ b/chrome/browser/history/android/android_history_types.h
@@ -19,6 +19,7 @@ class Statement;
namespace history {
typedef int64 AndroidURLID;
+typedef int64 SearchTermID;
// Wraps all columns needed to support android.provider.Browser.BookmarkColumns.
// It is used in insert() and update() to specify the columns need to insert or
@@ -206,6 +207,14 @@ class SearchRow {
static SearchColumnID GetSearchColumnID(const std::string& name);
+ SearchTermID id() const {
+ return id_;
+ }
+ void set_id(SearchTermID id) {
+ set_value_explicitly(SearchRow::ID);
+ id_ = id;
+ }
+
const string16& search_term() const {
return search_term_;
}
@@ -248,7 +257,7 @@ class SearchRow {
values_set_.insert(id);
}
- int64 id_;
+ SearchTermID id_;
string16 search_term_;
base::Time search_time_;
GURL url_;
@@ -262,10 +271,8 @@ class SearchRow {
// Defines the row stored in android_urls table.
struct AndroidURLRow {
- AndroidURLRow()
- :id(0),
- url_id(0) {
- }
+ AndroidURLRow();
+ ~AndroidURLRow();
// The unique id of the row
AndroidURLID id;
@@ -275,6 +282,19 @@ struct AndroidURLRow {
std::string raw_url;
};
+// Defines the row of keyword_cache table.
+struct SearchTermRow {
+ SearchTermRow();
+ ~SearchTermRow();
+
+ // The unique id of the row.
+ SearchTermID id;
+ // The keyword.
+ string16 term;
+ // The last visit time.
+ base::Time last_visit_time;
+};
+
// This class wraps the sql statement and favicon column index in statement if
// any. It is returned by AndroidProviderBackend::Query().
//
diff --git a/chrome/browser/history/android/android_provider_backend.cc b/chrome/browser/history/android/android_provider_backend.cc
index 028427a..139e867 100644
--- a/chrome/browser/history/android/android_provider_backend.cc
+++ b/chrome/browser/history/android/android_provider_backend.cc
@@ -45,6 +45,12 @@ const char * kURLUpdateClause =
"(SELECT url as visit_url, min(visit_time) as created_time"
" FROM visits GROUP BY url) ON (visit_url = urls.id) ";
+const char* kSearchTermUpdateClause =
+ "SELECT keyword_search_terms.term, max(urls.last_visit_time) "
+ "FROM keyword_search_terms JOIN urls ON "
+ "(keyword_search_terms.url_id = urls.id) "
+ "GROUP BY keyword_search_terms.term";
+
void BindStatement(const std::vector<string16>& selection_args,
sql::Statement* statement,
int* col_index) {
@@ -395,6 +401,165 @@ bool AndroidProviderBackend::DeleteBookmarks(
return true;
}
+AndroidStatement* AndroidProviderBackend::QuerySearchTerms(
+ const std::vector<SearchRow::SearchColumnID>& projections,
+ const std::string& selection,
+ const std::vector<string16>& selection_args,
+ const std::string& sort_order) {
+ if (projections.empty())
+ return NULL;
+
+ if (!EnsureInitializedAndUpdated())
+ return NULL;
+
+ std::string sql;
+ sql.append("SELECT ");
+ AppendSearchResultColumn(projections, &sql);
+ sql.append(" FROM android_cache_db.search_terms ");
+
+ if (!selection.empty()) {
+ sql.append(" WHERE ");
+ sql.append(selection);
+ }
+
+ if (!sort_order.empty()) {
+ sql.append(" ORDER BY ");
+ sql.append(sort_order);
+ }
+
+ scoped_ptr<sql::Statement> statement(new sql::Statement(
+ db_->GetUniqueStatement(sql.c_str())));
+ int count = 0;
+ BindStatement(selection_args, statement.get(), &count);
+ if (!statement->is_valid()) {
+ LOG(ERROR) << db_->GetErrorMessage();
+ return NULL;
+ }
+ sql::Statement* result = statement.release();
+ return new AndroidStatement(result, -1);
+}
+
+bool AndroidProviderBackend::UpdateSearchTerms(
+ const SearchRow& row,
+ const std::string& selection,
+ const std::vector<string16>& selection_args,
+ int* update_count) {
+ if (!EnsureInitializedAndUpdated())
+ return false;
+
+ SearchTerms search_terms;
+ if (!GetSelectedSearchTerms(selection, selection_args, &search_terms))
+ return false;
+
+ // We can not update search term if multiple row selected.
+ if (row.is_value_set_explicitly(SearchRow::SEARCH_TERM) &&
+ search_terms.size() > 1)
+ return false;
+
+ *update_count = search_terms.size();
+
+ if (search_terms.empty())
+ return true;
+
+ if (row.is_value_set_explicitly(SearchRow::SEARCH_TERM)) {
+ SearchTermRow search_term_row;
+ SearchRow search_row = row;
+ if (!history_db_->GetSearchTerm(search_terms[0], &search_term_row))
+ return false;
+
+ search_term_row.term = search_row.search_term();
+ if (!search_row.is_value_set_explicitly(SearchRow::SEARCH_TIME))
+ search_row.set_search_time(search_term_row.last_visit_time);
+ else
+ search_term_row.last_visit_time = search_row.search_time();
+
+ // Delete the original search term.
+ if (!history_db_->DeleteKeywordSearchTerm(search_terms[0]))
+ return false;
+
+ // Add the new one.
+ if (!AddSearchTerm(search_row))
+ return false;
+
+ // Update the cache table so the id will not be changed.
+ if (!history_db_->UpdateSearchTerm(search_term_row.id, search_term_row))
+ return false;
+
+ return true;
+ }
+
+ for (SearchTerms::const_iterator i = search_terms.begin();
+ i != search_terms.end(); ++i) {
+ SearchTermRow search_term_row;
+ if (!history_db_->GetSearchTerm(*i, &search_term_row))
+ return false;
+
+ // Check whether the given search time less than the existing one.
+ if (search_term_row.last_visit_time > row.search_time())
+ return false;
+
+ std::vector<KeywordSearchTermRow> search_term_rows;
+ if (!history_db_->GetKeywordSearchTermRows(*i, &search_term_rows) ||
+ search_term_rows.empty())
+ return false;
+
+ // Actually only search_time update. As there might multiple URLs
+ // asocciated with the keyword, Just update the first one's last_visit_time.
+ URLRow url_row;
+ if (!history_db_->GetURLRow(search_term_rows[0].url_id, &url_row))
+ return false;
+
+ BookmarkRow bookmark_row;
+ bookmark_row.set_last_visit_time(row.search_time());
+ TableIDRow table_id_row;
+ table_id_row.url_id = url_row.id();
+ TableIDRows table_id_rows;
+ table_id_rows.push_back(table_id_row);
+ if (!urls_handler_->Update(bookmark_row, table_id_rows))
+ return false;
+
+ if (!visit_handler_->Update(bookmark_row, table_id_rows))
+ return false;
+ }
+ return true;
+}
+
+SearchTermID AndroidProviderBackend::InsertSearchTerm(
+ const SearchRow& values) {
+ if (!EnsureInitializedAndUpdated())
+ return 0;
+
+ if (!AddSearchTerm(values))
+ return 0;
+
+ SearchTermID id = history_db_->GetSearchTerm(values.search_term(), NULL);
+ if (!id)
+ // Note the passed in Time() will be changed in UpdateSearchTermTable().
+ id = history_db_->AddSearchTerm(values.search_term(), Time());
+ return id;
+}
+
+bool AndroidProviderBackend::DeleteSearchTerms(
+ const std::string& selection,
+ const std::vector<string16>& selection_args,
+ int * deleted_count) {
+ SearchTerms rows;
+ if (!GetSelectedSearchTerms(selection, selection_args, &rows))
+ return false;
+
+ *deleted_count = rows.size();
+ if (rows.empty())
+ return true;
+
+ for (SearchTerms::const_iterator i = rows.begin(); i != rows.end(); ++i)
+ if (!history_db_->DeleteKeywordSearchTerm(*i))
+ return false;
+ // We don't delete the rows in search_terms table, as once the
+ // search_terms table is updated with keyword_search_terms, all
+ // keyword cache not found in the keyword_search_terms will be removed.
+ return true;
+}
+
bool AndroidProviderBackend::EnsureInitializedAndUpdated() {
if (!initialized_) {
if (!Init())
@@ -446,6 +611,11 @@ bool AndroidProviderBackend::UpdateTables() {
LOG(ERROR) << "Update of the icons failed";
return false;
}
+
+ if (!UpdateSearchTermTable()) {
+ LOG(ERROR) << "Update of the search_terms failed";
+ return false;
+ }
return true;
}
@@ -531,6 +701,29 @@ bool AndroidProviderBackend::UpdateFavicon() {
return true;
}
+bool AndroidProviderBackend::UpdateSearchTermTable() {
+ sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE,
+ kSearchTermUpdateClause));
+ while (statement.Step()) {
+ string16 term = statement.ColumnString16(0);
+ Time last_visit_time = Time::FromInternalValue(statement.ColumnInt64(1));
+ SearchTermRow search_term_row;
+ if (history_db_->GetSearchTerm(term, &search_term_row) &&
+ search_term_row.last_visit_time != last_visit_time) {
+ search_term_row.last_visit_time = last_visit_time;
+ if (!history_db_->UpdateSearchTerm(search_term_row.id, search_term_row))
+ return false;
+ } else {
+ if (!history_db_->AddSearchTerm(term, last_visit_time))
+ return false;
+ }
+ }
+ if (!history_db_->DeleteUnusedSearchTerms())
+ return false;
+
+ return true;
+}
+
int AndroidProviderBackend::AppendBookmarkResultColumn(
const std::vector<BookmarkRow::BookmarkColumnID>& projections,
std::string* result_column) {
@@ -584,6 +777,46 @@ bool AndroidProviderBackend::GetSelectedURLs(
return true;
}
+bool AndroidProviderBackend::GetSelectedSearchTerms(
+ const std::string& selection,
+ const std::vector<string16>& selection_args,
+ SearchTerms* rows) {
+ std::string sql("SELECT search "
+ "FROM android_cache_db.search_terms ");
+ if (!selection.empty()) {
+ sql.append(" WHERE ");
+ sql.append(selection);
+ }
+ sql::Statement statement(db_->GetUniqueStatement(sql.c_str()));
+ int count = 0;
+ BindStatement(selection_args, &statement, &count);
+ if (!statement.is_valid()) {
+ LOG(ERROR) << db_->GetErrorMessage();
+ return false;
+ }
+ while (statement.Step()) {
+ rows->push_back(statement.ColumnString16(0));
+ }
+ return true;
+}
+
+void AndroidProviderBackend::AppendSearchResultColumn(
+ const std::vector<SearchRow::SearchColumnID>& projections,
+ std::string* result_column) {
+ bool first = true;
+ int index = 0;
+ for (std::vector<SearchRow::SearchColumnID>::const_iterator i =
+ projections.begin(); i != projections.end(); ++i) {
+ if (first)
+ first = false;
+ else
+ result_column->append(", ");
+
+ result_column->append(SearchRow::GetAndroidName(*i));
+ index++;
+ }
+}
+
bool AndroidProviderBackend::SimulateUpdateURL(
const BookmarkRow& row,
const TableIDRows& ids,
@@ -750,4 +983,55 @@ void AndroidProviderBackend::BroadcastNotifications(
}
}
+bool AndroidProviderBackend::AddSearchTerm(const SearchRow& values) {
+ DCHECK(values.is_value_set_explicitly(SearchRow::SEARCH_TERM));
+ DCHECK(values.is_value_set_explicitly(SearchRow::TEMPLATE_URL));
+ DCHECK(values.is_value_set_explicitly(SearchRow::URL));
+
+ URLRow url_row;
+ BookmarkRow bookmark_row;
+ // Android CTS test BrowserTest.testAccessSearches allows insert the same
+ // seach term multiple times, and just search time need updated.
+ if (history_db_->GetRowForURL(values.url(), &url_row)) {
+ // Already exist, Add a visit.
+ if (values.is_value_set_explicitly(SearchRow::SEARCH_TIME))
+ bookmark_row.set_last_visit_time(values.search_time());
+ else
+ bookmark_row.set_visit_count(url_row.visit_count() + 1);
+ TableIDRows table_id_rows;
+ TableIDRow table_id_row;
+ table_id_row.url = values.url();
+ table_id_row.url_id = url_row.id();
+ table_id_rows.push_back(table_id_row);
+ if (!urls_handler_->Update(bookmark_row, table_id_rows))
+ return false;
+ if (!visit_handler_->Update(bookmark_row, table_id_rows))
+ return false;
+
+ if (!history_db_->GetKeywordSearchTermRow(url_row.id(), NULL))
+ if (!history_db_->SetKeywordSearchTermsForURL(url_row.id(),
+ values.template_url_id(), values.search_term()))
+ return false;
+ } else {
+ bookmark_row.set_raw_url(values.url().spec());
+ bookmark_row.set_url(values.url());
+ if (values.is_value_set_explicitly(SearchRow::SEARCH_TIME))
+ bookmark_row.set_last_visit_time(values.search_time());
+
+ if (!urls_handler_->Insert(&bookmark_row))
+ return false;
+
+ if (!visit_handler_->Insert(&bookmark_row))
+ return false;
+
+ if (!android_urls_handler_->Insert(&bookmark_row))
+ return false;
+
+ if (!history_db_->SetKeywordSearchTermsForURL(bookmark_row.url_id(),
+ values.template_url_id(), values.search_term()))
+ return false;
+ }
+ return true;
+}
+
} // namespace history
diff --git a/chrome/browser/history/android/android_provider_backend.h b/chrome/browser/history/android/android_provider_backend.h
index e8a4bed..a75a13f 100644
--- a/chrome/browser/history/android/android_provider_backend.h
+++ b/chrome/browser/history/android/android_provider_backend.h
@@ -92,11 +92,50 @@ class AndroidProviderBackend {
const std::vector<string16>& selection_args,
int* deleted_count);
+ // SearchTerms --------------------------------------------------------------
+ //
+ // Returns the result of the given query.
+ // |projections| specifies the result columns, can not be empty, otherwise
+ // NULL is returned.
+ // |selection| is the SQL WHERE clause without 'WHERE'.
+ // |selection_args| is the arguments for WHERE clause.
+ // |sort_order| the SQL ORDER clause.
+ AndroidStatement* QuerySearchTerms(
+ const std::vector<SearchRow::SearchColumnID>& projections,
+ const std::string& selection,
+ const std::vector<string16>& selection_args,
+ const std::string& sort_order);
+
+ // Runs the given update and returns the number of updated rows in
+ // |update_count| and return true, false returned if there is any error.
+ //
+ // |row| is the value need to update.
+ // |selection| is the SQL WHERE clause without 'WHERE'.
+ // |selection_args| is the arguments for WHERE clause.
+ bool UpdateSearchTerms(const SearchRow& row,
+ const std::string& selection,
+ const std::vector<string16>& selection_args,
+ int* update_count);
+
+ // Inserts the given valus and return the SearchTermID of inserted row.
+ SearchTermID InsertSearchTerm(const SearchRow& values);
+
+ // Deletes the matched rows and the number of deleted rows is returned in
+ // |deleted_count|.
+ // |selection| is the SQL WHERE clause without 'WHERE'.
+ // |selection_args| is the arguments for WHERE clause.
+ //
+ // if |selection| is empty all search be deleted.
+ bool DeleteSearchTerms(const std::string& selection,
+ const std::vector<string16>& selection_args,
+ int * deleted_count);
+
private:
friend class AndroidProviderBackendTest;
FRIEND_TEST_ALL_PREFIXES(AndroidProviderBackendTest, UpdateTables);
FRIEND_TEST_ALL_PREFIXES(AndroidProviderBackendTest, UpdateBookmarks);
+ FRIEND_TEST_ALL_PREFIXES(AndroidProviderBackendTest, UpdateSearchTermTable);
struct HistoryNotification {
HistoryNotification(int type, HistoryDetails* detail);
@@ -192,6 +231,9 @@ class AndroidProviderBackend {
// Update the bookmark_cache table for favicon.
bool UpdateFavicon();
+ // Update the search_term table
+ bool UpdateSearchTermTable();
+
// Append the specified result columns in |projections| to the given
// |result_column|.
// To support the lazy binding, the index of favicon column will be
@@ -200,6 +242,12 @@ class AndroidProviderBackend {
const std::vector<BookmarkRow::BookmarkColumnID>& projections,
std::string* result_column);
+ // Append the specified search result columns in |projections| to the given
+ // |result_column|.
+ void AppendSearchResultColumn(
+ const std::vector<SearchRow::SearchColumnID>& projections,
+ std::string* result_column);
+
// Runs the given query on |virtual_table| and returns true if succeeds, the
// selected URLID and url are returned in |rows|.
bool GetSelectedURLs(const std::string& selection,
@@ -207,6 +255,13 @@ class AndroidProviderBackend {
const char* virtual_table,
TableIDRows* rows);
+ // Runs the given query on search_terms table and returns true on success,
+ // The selected search term are returned in |rows|.
+ typedef std::vector<string16> SearchTerms;
+ bool GetSelectedSearchTerms(const std::string& selection,
+ const std::vector<string16>& selection_args,
+ SearchTerms* rows);
+
// Simulates update url by deleting the previous URL and creating a new one.
// Return true on success.
bool SimulateUpdateURL(const BookmarkRow& row,
@@ -223,6 +278,11 @@ class AndroidProviderBackend {
void BroadcastNotifications(const HistoryNotifications& notifications);
+ // Add the search term from the given |values|. It will add the values.url()
+ // in the urls table if it doesn't exist, insert visit in the visits table,
+ // also add keyword in keyword_search_term.
+ bool AddSearchTerm(const SearchRow& values);
+
// SQLHandlers for different tables.
scoped_ptr<SQLHandler> urls_handler_;
scoped_ptr<SQLHandler> visit_handler_;
diff --git a/chrome/browser/history/android/android_provider_backend_unittest.cc b/chrome/browser/history/android/android_provider_backend_unittest.cc
index 98bc89b..8c16df3 100644
--- a/chrome/browser/history/android/android_provider_backend_unittest.cc
+++ b/chrome/browser/history/android/android_provider_backend_unittest.cc
@@ -1081,4 +1081,365 @@ TEST_F(AndroidProviderBackendTest, UpdateFavicon) {
NULL));
}
+TEST_F(AndroidProviderBackendTest, UpdateSearchTermTable) {
+ ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_, bookmark_temp_));
+ ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_, NULL,
+ &history_db_));
+ scoped_ptr<AndroidProviderBackend> backend(
+ new AndroidProviderBackend(android_cache_db_name_, &history_db_,
+ &thumbnail_db_, &bookmark_model_, &delegate_));
+ // Insert a keyword search item to verify if the update succeeds.
+ BookmarkRow row1;
+ row1.set_raw_url("cnn.com");
+ row1.set_url(GURL("http://cnn.com"));
+ row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
+ row1.set_title(UTF8ToUTF16("cnn"));
+ ASSERT_TRUE(backend->InsertBookmark(row1));
+ string16 term = UTF8ToUTF16("Search term 1");
+ URLID url_id = history_db_.GetRowForURL(row1.url(), NULL);
+ ASSERT_TRUE(url_id);
+ ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id, 1, term));
+ ASSERT_TRUE(backend->UpdateSearchTermTable());
+ SearchTermRow keyword_cache;
+ SearchTermID id = history_db_.GetSearchTerm(term, &keyword_cache);
+ ASSERT_TRUE(id);
+ EXPECT_EQ(term, keyword_cache.term);
+ EXPECT_EQ(ToMilliseconds(row1.last_visit_time()),
+ ToMilliseconds(keyword_cache.last_visit_time));
+
+ // Add another row.
+ BookmarkRow row2;
+ row2.set_raw_url("google.com");
+ row2.set_url(GURL("http://google.com"));
+ row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(2));
+ row2.set_title(UTF8ToUTF16("cnn"));
+ ASSERT_TRUE(backend->InsertBookmark(row2));
+ url_id = history_db_.GetRowForURL(row2.url(), NULL);
+ ASSERT_TRUE(url_id);
+ string16 term2 = UTF8ToUTF16("Search term 2");
+ ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id, 1, term2));
+ ASSERT_TRUE(backend->UpdateSearchTermTable());
+ SearchTermID search_id1 = history_db_.GetSearchTerm(term,
+ &keyword_cache);
+ // The id shouldn't changed.
+ ASSERT_EQ(id, search_id1);
+ EXPECT_EQ(term, keyword_cache.term);
+ EXPECT_EQ(ToMilliseconds(row1.last_visit_time()),
+ ToMilliseconds(keyword_cache.last_visit_time));
+ // Verify the row just inserted.
+ SearchTermID id2 = history_db_.GetSearchTerm(term2, &keyword_cache);
+ ASSERT_TRUE(id2);
+ EXPECT_EQ(term2, keyword_cache.term);
+ EXPECT_EQ(ToMilliseconds(row2.last_visit_time()),
+ ToMilliseconds(keyword_cache.last_visit_time));
+
+ // Add 3rd row and associate it with term.
+ BookmarkRow row3;
+ row3.set_raw_url("search.com");
+ row3.set_url(GURL("http://search.com"));
+ row3.set_last_visit_time(Time::Now());
+ row3.set_title(UTF8ToUTF16("search"));
+ ASSERT_TRUE(backend->InsertBookmark(row3));
+ url_id = history_db_.GetRowForURL(row3.url(), NULL);
+ ASSERT_TRUE(url_id);
+ ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id, 1, term));
+ ASSERT_TRUE(backend->UpdateSearchTermTable());
+ // Verify id not changed and last_visit_time updated.
+ ASSERT_EQ(search_id1, history_db_.GetSearchTerm(term, &keyword_cache));
+ EXPECT_EQ(ToMilliseconds(row3.last_visit_time()),
+ ToMilliseconds(keyword_cache.last_visit_time));
+ // The id of term2 wasn't changed.
+ EXPECT_EQ(id2, history_db_.GetSearchTerm(term2, NULL));
+
+ // Remove the term.
+ ASSERT_TRUE(history_db_.DeleteKeywordSearchTerm(term));
+ ASSERT_TRUE(backend->UpdateSearchTermTable());
+ // The cache of term should removed.
+ ASSERT_FALSE(history_db_.GetSearchTerm(term, NULL));
+ // The id of term2 wasn't changed.
+ EXPECT_EQ(id2, history_db_.GetSearchTerm(term2, NULL));
+}
+
+TEST_F(AndroidProviderBackendTest, QuerySearchTerms) {
+ ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_, bookmark_temp_));
+ ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_, NULL,
+ &history_db_));
+ scoped_ptr<AndroidProviderBackend> backend(
+ new AndroidProviderBackend(android_cache_db_name_, &history_db_,
+ &thumbnail_db_, &bookmark_model_, &delegate_));
+ // Insert a keyword search item to verify if we can find it.
+ BookmarkRow row1;
+ row1.set_raw_url("cnn.com");
+ row1.set_url(GURL("http://cnn.com"));
+ row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
+ row1.set_title(UTF8ToUTF16("cnn"));
+ ASSERT_TRUE(backend->InsertBookmark(row1));
+ string16 term = UTF8ToUTF16("Search term 1");
+ URLID url_id = history_db_.GetRowForURL(row1.url(), NULL);
+ ASSERT_TRUE(url_id);
+ ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id, 1, term));
+
+ std::vector<SearchRow::SearchColumnID> projections;
+ projections.push_back(SearchRow::ID);
+ projections.push_back(SearchRow::SEARCH_TERM);
+ projections.push_back(SearchRow::SEARCH_TIME);
+ scoped_ptr<AndroidStatement> statement(backend->QuerySearchTerms(
+ projections, std::string(), std::vector<string16>(), std::string()));
+ ASSERT_TRUE(statement.get());
+ ASSERT_TRUE(statement->statement()->Step());
+ EXPECT_TRUE(statement->statement()->ColumnInt64(0));
+ EXPECT_EQ(term, statement->statement()->ColumnString16(1));
+ EXPECT_EQ(ToMilliseconds(row1.last_visit_time()),
+ statement->statement()->ColumnInt64(2));
+ EXPECT_FALSE(statement->statement()->Step());
+}
+
+TEST_F(AndroidProviderBackendTest, UpdateSearchTerms) {
+ ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_, bookmark_temp_));
+ ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_, NULL,
+ &history_db_));
+ scoped_ptr<AndroidProviderBackend> backend(
+ new AndroidProviderBackend(android_cache_db_name_, &history_db_,
+ &thumbnail_db_, &bookmark_model_, &delegate_));
+ // Insert a keyword.
+ BookmarkRow row1;
+ row1.set_raw_url("cnn.com");
+ row1.set_url(GURL("http://cnn.com"));
+ row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
+ row1.set_title(UTF8ToUTF16("cnn"));
+ ASSERT_TRUE(backend->InsertBookmark(row1));
+ string16 term = UTF8ToUTF16("Search term 1");
+ URLID url_id = history_db_.GetRowForURL(row1.url(), NULL);
+ ASSERT_TRUE(url_id);
+ ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id, 1, term));
+
+ // Get the SearchTermID of the row we just inserted.
+ std::vector<SearchRow::SearchColumnID> projections;
+ projections.push_back(SearchRow::ID);
+ projections.push_back(SearchRow::SEARCH_TIME);
+ projections.push_back(SearchRow::SEARCH_TERM);
+ std::vector<string16> args;
+ args.push_back(term);
+ scoped_ptr<AndroidStatement> statement(backend->QuerySearchTerms(
+ projections, "search = ?", args, std::string()));
+ ASSERT_TRUE(statement.get());
+ ASSERT_TRUE(statement->statement()->Step());
+ SearchTermID id = statement->statement()->ColumnInt64(0);
+ ASSERT_TRUE(id);
+ EXPECT_FALSE(statement->statement()->Step());
+
+ // Update the search term and time.
+ string16 update_term = UTF8ToUTF16("Update search term");
+ args.clear();
+ args.push_back(term);
+ SearchRow search_row;
+ search_row.set_search_term(update_term);
+ search_row.set_url(GURL("http://google.com"));
+ search_row.set_template_url_id(1);
+ search_row.set_search_time(Time::Now() - TimeDelta::FromHours(1));
+ int update_count = 0;
+ ASSERT_TRUE(backend->UpdateSearchTerms(search_row, "search = ?", args,
+ &update_count));
+ EXPECT_EQ(1, update_count);
+
+ // Verify if the search term updated.
+ // The origin term should be removed.
+ std::vector<KeywordSearchTermRow> rows;
+ ASSERT_TRUE(history_db_.GetKeywordSearchTermRows(term, &rows));
+ EXPECT_TRUE(rows.empty());
+ // The new term should be inserted.
+ ASSERT_TRUE(history_db_.GetKeywordSearchTermRows(update_term, &rows));
+ ASSERT_EQ(1u, rows.size());
+ // The history of urls shouldn't be removed.
+ ASSERT_TRUE(history_db_.GetRowForURL(row1.url(), NULL));
+ // The new URL is inserted.
+ ASSERT_TRUE(history_db_.GetRowForURL(search_row.url(), NULL));
+
+ // Verfiy the AndoridSearchID isn't changed.
+ args.clear();
+ args.push_back(update_term);
+ statement.reset(backend->QuerySearchTerms(projections, "search = ?", args,
+ std::string()));
+ ASSERT_TRUE(statement.get());
+ ASSERT_TRUE(statement->statement()->Step());
+ // The id didn't change.
+ EXPECT_EQ(id, statement->statement()->ColumnInt64(0));
+ // The search time was updated.
+ EXPECT_EQ(ToMilliseconds(search_row.search_time()),
+ statement->statement()->ColumnInt64(1));
+ // The search term was updated.
+ EXPECT_EQ(update_term, statement->statement()->ColumnString16(2));
+ EXPECT_FALSE(statement->statement()->Step());
+
+ // Only update the search time.
+ SearchRow update_time;
+ update_time.set_search_time(Time::Now());
+ // Update it by id.
+ args.clear();
+ std::ostringstream oss;
+ oss << id;
+ args.push_back(UTF8ToUTF16(oss.str()));
+ update_count = 0;
+ ASSERT_TRUE(backend->UpdateSearchTerms(update_time, "_id = ?", args,
+ &update_count));
+ EXPECT_EQ(1, update_count);
+
+ // Verify the update.
+ statement.reset(backend->QuerySearchTerms(projections, "_id = ?", args,
+ std::string()));
+ ASSERT_TRUE(statement.get());
+ ASSERT_TRUE(statement->statement()->Step());
+ // The id didn't change.
+ EXPECT_EQ(id, statement->statement()->ColumnInt64(0));
+ // The search time was updated.
+ EXPECT_EQ(ToMilliseconds(update_time.search_time()),
+ statement->statement()->ColumnInt64(1));
+ // The search term didn't change.
+ EXPECT_EQ(update_term, statement->statement()->ColumnString16(2));
+ EXPECT_FALSE(statement->statement()->Step());
+}
+
+TEST_F(AndroidProviderBackendTest, DeleteSearchTerms) {
+ ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_, bookmark_temp_));
+ ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_, NULL,
+ &history_db_));
+ scoped_ptr<AndroidProviderBackend> backend(
+ new AndroidProviderBackend(android_cache_db_name_, &history_db_,
+ &thumbnail_db_, &bookmark_model_, &delegate_));
+ // Insert a keyword.
+ BookmarkRow row1;
+ row1.set_raw_url("cnn.com");
+ row1.set_url(GURL("http://cnn.com"));
+ row1.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
+ row1.set_title(UTF8ToUTF16("cnn"));
+ ASSERT_TRUE(backend->InsertBookmark(row1));
+ string16 term = UTF8ToUTF16("Search term 1");
+ URLID url_id = history_db_.GetRowForURL(row1.url(), NULL);
+ ASSERT_TRUE(url_id);
+ ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id, 1, term));
+
+ // Get the SearchTermID of the row we just inserted.
+ std::vector<SearchRow::SearchColumnID> projections;
+ projections.push_back(SearchRow::ID);
+ projections.push_back(SearchRow::SEARCH_TIME);
+ projections.push_back(SearchRow::SEARCH_TERM);
+ std::vector<string16> args;
+ args.push_back(term);
+ scoped_ptr<AndroidStatement> statement(backend->QuerySearchTerms(
+ projections, "search = ?", args, std::string()));
+ ASSERT_TRUE(statement.get());
+ ASSERT_TRUE(statement->statement()->Step());
+ SearchTermID id1 = statement->statement()->ColumnInt64(0);
+ ASSERT_TRUE(id1);
+ EXPECT_FALSE(statement->statement()->Step());
+
+ // Insert a keyword.
+ BookmarkRow row2;
+ row2.set_raw_url("google.com");
+ row2.set_url(GURL("http://google.com"));
+ row2.set_last_visit_time(Time::Now() - TimeDelta::FromDays(1));
+ row2.set_title(UTF8ToUTF16("google"));
+ ASSERT_TRUE(backend->InsertBookmark(row2));
+ string16 term2 = UTF8ToUTF16("Search term 2");
+ URLID url_id2 = history_db_.GetRowForURL(row2.url(), NULL);
+ ASSERT_TRUE(url_id2);
+ ASSERT_TRUE(history_db_.SetKeywordSearchTermsForURL(url_id2, 1, term2));
+
+ // Get the SearchTermID of the row we just inserted.
+ projections.clear();
+ projections.push_back(SearchRow::ID);
+ projections.push_back(SearchRow::SEARCH_TIME);
+ projections.push_back(SearchRow::SEARCH_TERM);
+ args.clear();
+ args.push_back(term2);
+ statement.reset(backend->QuerySearchTerms(projections, "search = ?", args,
+ std::string()));
+ ASSERT_TRUE(statement.get());
+ ASSERT_TRUE(statement->statement()->Step());
+ SearchTermID id2 = statement->statement()->ColumnInt64(0);
+ ASSERT_TRUE(id2);
+ EXPECT_FALSE(statement->statement()->Step());
+
+ // Delete the first one.
+ args.clear();
+ args.push_back(term);
+ int deleted_count = 0;
+ ASSERT_TRUE(backend->DeleteSearchTerms("search = ?", args, &deleted_count));
+ EXPECT_EQ(1, deleted_count);
+ std::vector<KeywordSearchTermRow> rows;
+ ASSERT_TRUE(history_db_.GetKeywordSearchTermRows(term, &rows));
+ EXPECT_TRUE(rows.empty());
+ // Verify we can't get the first term.
+ args.clear();
+ std::ostringstream oss;
+ oss << id1;
+ args.push_back(UTF8ToUTF16(oss.str()));
+ statement.reset(backend->QuerySearchTerms(projections, "_id = ?", args,
+ std::string()));
+ ASSERT_TRUE(statement.get());
+ EXPECT_FALSE(statement->statement()->Step());
+
+ // The second one is still there.
+ args.clear();
+ std::ostringstream oss1;
+ oss1 << id2;
+ args.push_back(UTF8ToUTF16(oss1.str()));
+ statement.reset(backend->QuerySearchTerms(projections, "_id = ?", args,
+ std::string()));
+ ASSERT_TRUE(statement.get());
+ EXPECT_TRUE(statement->statement()->Step());
+ EXPECT_EQ(id2, statement->statement()->ColumnInt64(0));
+ EXPECT_FALSE(statement->statement()->Step());
+
+ // Remove all search terms in no condition.
+ deleted_count = 0;
+ args.clear();
+ ASSERT_TRUE(backend->DeleteSearchTerms(std::string(), args, &deleted_count));
+ EXPECT_EQ(1, deleted_count);
+
+ // Verify the second one was removed.
+ args.clear();
+ args.push_back(UTF8ToUTF16(oss1.str()));
+ statement.reset(backend->QuerySearchTerms(projections, "_id = ?", args,
+ std::string()));
+ ASSERT_TRUE(statement.get());
+ EXPECT_FALSE(statement->statement()->Step());
+}
+
+TEST_F(AndroidProviderBackendTest, InsertSearchTerm) {
+ ASSERT_EQ(sql::INIT_OK, history_db_.Init(history_db_name_, bookmark_temp_));
+ ASSERT_EQ(sql::INIT_OK, thumbnail_db_.Init(thumbnail_db_name_, NULL,
+ &history_db_));
+ scoped_ptr<AndroidProviderBackend> backend(
+ new AndroidProviderBackend(android_cache_db_name_, &history_db_,
+ &thumbnail_db_, &bookmark_model_, &delegate_));
+ SearchRow search_row;
+ search_row.set_search_term(UTF8ToUTF16("google"));
+ search_row.set_url(GURL("http://google.com"));
+ search_row.set_template_url_id(1);
+ search_row.set_search_time(Time::Now() - TimeDelta::FromHours(1));
+
+ SearchTermID id = backend->InsertSearchTerm(search_row);
+ ASSERT_TRUE(id);
+
+ std::vector<SearchRow::SearchColumnID> projections;
+ projections.push_back(SearchRow::ID);
+ projections.push_back(SearchRow::SEARCH_TIME);
+ projections.push_back(SearchRow::SEARCH_TERM);
+ std::vector<string16> args;
+ std::ostringstream oss;
+ oss << id;
+ args.push_back(UTF8ToUTF16(oss.str()));
+ scoped_ptr<AndroidStatement> statement(backend->QuerySearchTerms(
+ projections, "_id = ?", args, std::string()));
+ ASSERT_TRUE(statement.get());
+ ASSERT_TRUE(statement->statement()->Step());
+ EXPECT_EQ(id, statement->statement()->ColumnInt64(0));
+ EXPECT_EQ(ToMilliseconds(search_row.search_time()),
+ statement->statement()->ColumnInt64(1));
+ EXPECT_EQ(search_row.search_term(),
+ statement->statement()->ColumnString16(2));
+ EXPECT_FALSE(statement->statement()->Step());
+}
+
} // namespace history
diff --git a/chrome/browser/history/url_database.cc b/chrome/browser/history/url_database.cc
index 2b4ba52..4436a4e 100644
--- a/chrome/browser/history/url_database.cc
+++ b/chrome/browser/history/url_database.cc
@@ -386,6 +386,12 @@ bool URLDatabase::CreateKeywordSearchTermsIndices() {
return false;
}
+ // For query or deletion by term.
+ if (!GetDB().Execute(
+ "CREATE INDEX IF NOT EXISTS keyword_search_terms_index3 ON "
+ "keyword_search_terms (term)")) {
+ return false;
+ }
return true;
}
@@ -439,6 +445,26 @@ bool URLDatabase::GetKeywordSearchTermRow(URLID url_id,
return true;
}
+bool URLDatabase::GetKeywordSearchTermRows(
+ const string16& term,
+ std::vector<KeywordSearchTermRow>* rows) {
+ sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
+ "SELECT keyword_id, url_id FROM keyword_search_terms WHERE term=?"));
+ statement.BindString16(0, term);
+
+ if (!statement.is_valid())
+ return false;
+
+ while (statement.Step()) {
+ KeywordSearchTermRow row;
+ row.url_id = statement.ColumnInt64(1);
+ row.keyword_id = statement.ColumnInt64(0);
+ row.term = term;
+ rows->push_back(row);
+ }
+ return true;
+}
+
void URLDatabase::DeleteAllSearchTermsForKeyword(
TemplateURLID keyword_id) {
DCHECK(keyword_id);
@@ -488,6 +514,14 @@ void URLDatabase::GetMostRecentKeywordSearchTerms(
}
}
+bool URLDatabase::DeleteKeywordSearchTerm(const string16& term) {
+ sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
+ "DELETE FROM keyword_search_terms WHERE term=?"));
+ statement.BindString16(0, term);
+
+ return statement.Run();
+}
+
bool URLDatabase::DropStarredIDFromURLs() {
if (!GetDB().DoesColumnExist("urls", "starred_id"))
return true; // urls is already updated, no need to continue.
diff --git a/chrome/browser/history/url_database.h b/chrome/browser/history/url_database.h
index 2103ab77..57b7ec2 100644
--- a/chrome/browser/history/url_database.h
+++ b/chrome/browser/history/url_database.h
@@ -197,10 +197,15 @@ class URLDatabase {
TemplateURLID keyword_id,
const string16& term);
- // Looks up a keyword search term given a url id. Fills row with the data.
- // Returns true on success and false otherwise.
+ // Looks up a keyword search term given a url id. Returns all the search terms
+ // in |rows|. Returns true on success.
bool GetKeywordSearchTermRow(URLID url_id, KeywordSearchTermRow* row);
+ // Looks up all keyword search terms given a term, Fills the rows with data.
+ // Returns true on success and false otherwise.
+ bool GetKeywordSearchTermRows(const string16& term,
+ std::vector<KeywordSearchTermRow>* rows);
+
// Deletes all search terms for the specified keyword that have been added by
// way of SetKeywordSearchTermsForURL.
void DeleteAllSearchTermsForKeyword(TemplateURLID keyword_id);
@@ -213,6 +218,9 @@ class URLDatabase {
int max_count,
std::vector<KeywordSearchTermVisit>* matches);
+ // Deletes all searches matching |term|.
+ bool DeleteKeywordSearchTerm(const string16& term);
+
// Migration -----------------------------------------------------------------
// Do to a bug we were setting the favicon of about:blank. This forces
diff --git a/chrome/browser/history/url_database_unittest.cc b/chrome/browser/history/url_database_unittest.cc
index 6931963..485c940 100644
--- a/chrome/browser/history/url_database_unittest.cc
+++ b/chrome/browser/history/url_database_unittest.cc
@@ -267,4 +267,80 @@ TEST_F(URLDatabaseTest, IconMappingEnumerator) {
ASSERT_FALSE(e.GetNextIconMapping(&icon_mapping));
}
+// Test GetKeywordSearchTermRows and DeleteSearchTerm
+TEST_F(URLDatabaseTest, GetAndDeleteKeywordSearchTermByTerm) {
+ URLRow url_info1(GURL("http://www.google.com/"));
+ url_info1.set_title(UTF8ToUTF16("Google"));
+ url_info1.set_visit_count(4);
+ url_info1.set_typed_count(2);
+ url_info1.set_last_visit(Time::Now() - TimeDelta::FromDays(1));
+ url_info1.set_hidden(false);
+ URLID url_id1 = AddURL(url_info1);
+ ASSERT_NE(0, url_id1);
+
+ // Add a keyword visit.
+ TemplateURLID keyword_id = 100;
+ string16 keyword = UTF8ToUTF16("visit");
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(url_id1, keyword_id, keyword));
+
+ URLRow url_info2(GURL("https://www.google.com/"));
+ url_info2.set_title(UTF8ToUTF16("Google"));
+ url_info2.set_visit_count(4);
+ url_info2.set_typed_count(2);
+ url_info2.set_last_visit(Time::Now() - TimeDelta::FromDays(1));
+ url_info2.set_hidden(false);
+ URLID url_id2 = AddURL(url_info2);
+ ASSERT_NE(0, url_id2);
+ // Add the same keyword for url_info2.
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(url_id2, keyword_id, keyword));
+
+ // Add another URL for different keyword.
+ URLRow url_info3(GURL("https://www.google.com/search"));
+ url_info3.set_title(UTF8ToUTF16("Google"));
+ url_info3.set_visit_count(4);
+ url_info3.set_typed_count(2);
+ url_info3.set_last_visit(Time::Now() - TimeDelta::FromDays(1));
+ url_info3.set_hidden(false);
+ URLID url_id3 = AddURL(url_info3);
+ ASSERT_NE(0, url_id3);
+ string16 keyword2 = UTF8ToUTF16("Search");
+
+ ASSERT_TRUE(SetKeywordSearchTermsForURL(url_id3, keyword_id, keyword2));
+
+ // We should get 2 rows for |keyword|.
+ std::vector<KeywordSearchTermRow> rows;
+ ASSERT_TRUE(GetKeywordSearchTermRows(keyword, &rows));
+ ASSERT_EQ(2u, rows.size());
+ if (rows[0].url_id == url_id1) {
+ EXPECT_EQ(keyword, rows[0].term);
+ EXPECT_EQ(keyword, rows[1].term);
+ EXPECT_EQ(url_id2, rows[1].url_id);
+ } else {
+ EXPECT_EQ(keyword, rows[0].term);
+ EXPECT_EQ(url_id1, rows[1].url_id);
+ EXPECT_EQ(keyword, rows[1].term);
+ EXPECT_EQ(url_id2, rows[0].url_id);
+ }
+
+ // We should get 1 row for |keyword2|.
+ rows.clear();
+ ASSERT_TRUE(GetKeywordSearchTermRows(keyword2, &rows));
+ ASSERT_EQ(1u, rows.size());
+ EXPECT_EQ(keyword2, rows[0].term);
+ EXPECT_EQ(url_id3, rows[0].url_id);
+
+ // Delete all rows have keyword.
+ ASSERT_TRUE(DeleteKeywordSearchTerm(keyword));
+ rows.clear();
+ // We should still find keyword2.
+ ASSERT_TRUE(GetKeywordSearchTermRows(keyword2, &rows));
+ ASSERT_EQ(1u, rows.size());
+ EXPECT_EQ(keyword2, rows[0].term);
+ EXPECT_EQ(url_id3, rows[0].url_id);
+ rows.clear();
+ // No row for keyword.
+ ASSERT_TRUE(GetKeywordSearchTermRows(keyword, &rows));
+ EXPECT_TRUE(rows.empty());
+}
+
} // namespace history
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 1b517af..7378563 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1129,6 +1129,8 @@
'browser/history/android/bookmark_model_sql_handler.h',
'browser/history/android/favicon_sql_handler.cc',
'browser/history/android/favicon_sql_handler.h',
+ 'browser/history/android/sql_handler.cc',
+ 'browser/history/android/sql_handler.h',
'browser/history/android/urls_sql_handler.cc',
'browser/history/android/urls_sql_handler.h',
'browser/history/android/visit_sql_handler.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 510c48a..0214b1e 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1489,6 +1489,11 @@
'browser/google/google_util_unittest.cc',
'browser/gpu_blacklist_unittest.cc',
'browser/gpu_util_unittest.cc',
+ 'browser/history/android/android_cache_database_unittest.cc',
+ 'browser/history/android/android_history_types_unittest.cc',
+ 'browser/history/android/android_provider_backend_unittest.cc',
+ 'browser/history/android/urls_sql_handler_unittest.cc',
+ 'browser/history/android/visit_sql_handler_unittest.cc',
'browser/history/expire_history_backend_unittest.cc',
'browser/history/history_backend_unittest.cc',
'browser/history/history_querying_unittest.cc',