| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/history/android/android_cache_database.h" |
| |
| #include "base/file_util.h" |
| #include "chrome/browser/history/android/android_time.h" |
| #include "sql/statement.h" |
| |
| using base::Time; |
| using base::TimeDelta; |
| |
| namespace history { |
| |
| AndroidCacheDatabase::AndroidCacheDatabase() { |
| } |
| |
| AndroidCacheDatabase::~AndroidCacheDatabase() { |
| } |
| |
| sql::InitStatus AndroidCacheDatabase::InitAndroidCacheDatabase( |
| const base::FilePath& db_name) { |
| if (!CreateDatabase(db_name)) |
| return sql::INIT_FAILURE; |
| |
| if (!Attach()) |
| return sql::INIT_FAILURE; |
| |
| if (!CreateBookmarkCacheTable()) |
| return sql::INIT_FAILURE; |
| |
| if (!CreateSearchTermsTable()) |
| return sql::INIT_FAILURE; |
| |
| return sql::INIT_OK; |
| } |
| |
| bool AndroidCacheDatabase::AddBookmarkCacheRow(const Time& created_time, |
| const Time& last_visit_time, |
| URLID url_id) { |
| sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| "INSERT INTO android_cache_db.bookmark_cache (created_time, " |
| "last_visit_time, url_id) VALUES (?, ?, ?)")); |
| |
| statement.BindInt64(0, ToDatabaseTime(created_time)); |
| statement.BindInt64(1, ToDatabaseTime(last_visit_time)); |
| statement.BindInt64(2, url_id); |
| |
| if (!statement.Run()) { |
| LOG(ERROR) << GetDB().GetErrorMessage(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AndroidCacheDatabase::ClearAllBookmarkCache() { |
| sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| "DELETE FROM android_cache_db.bookmark_cache")); |
| if (!statement.Run()) { |
| LOG(ERROR) << GetDB().GetErrorMessage(); |
| return false; |
| } |
| return true; |
| } |
| |
| bool AndroidCacheDatabase::MarkURLsAsBookmarked( |
| const std::vector<URLID>& url_ids) { |
| bool has_id = false; |
| std::ostringstream oss; |
| for (std::vector<URLID>::const_iterator i = url_ids.begin(); |
| i != url_ids.end(); ++i) { |
| if (has_id) |
| oss << ", "; |
| else |
| has_id = true; |
| oss << *i; |
| } |
| |
| if (!has_id) |
| return true; |
| |
| std::string sql("UPDATE android_cache_db.bookmark_cache " |
| "SET bookmark = 1 WHERE url_id in ("); |
| sql.append(oss.str()); |
| sql.append(")"); |
| if (!GetDB().Execute(sql.c_str())) { |
| LOG(ERROR) << GetDB().GetErrorMessage(); |
| return false; |
| } |
| return true; |
| } |
| |
| bool AndroidCacheDatabase::SetFaviconID(URLID url_id, |
| chrome::FaviconID favicon_id) { |
| sql::Statement update_statement(GetDB().GetCachedStatement(SQL_FROM_HERE, |
| "UPDATE android_cache_db.bookmark_cache " |
| "SET favicon_id = ? WHERE url_id = ? ")); |
| |
| update_statement.BindInt64(0, favicon_id); |
| update_statement.BindInt64(1, url_id); |
| if (!update_statement.Run()) { |
| LOG(ERROR) << GetDB().GetErrorMessage(); |
| return false; |
| } |
| return true; |
| } |
| |
| SearchTermID AndroidCacheDatabase::AddSearchTerm( |
| const base::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, ToDatabaseTime(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, ToDatabaseTime(row.last_visit_time)); |
| statement.BindInt64(2, id); |
| |
| return statement.Run(); |
| } |
| |
| SearchTermID AndroidCacheDatabase::GetSearchTerm(const base::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 = FromDatabaseTime(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 base::FilePath& db_name) { |
| db_name_ = db_name; |
| sql::Connection::Delete(db_name_); |
| |
| // Using a new connection, otherwise we can not create the database. |
| sql::Connection connection; |
| |
| // The db doesn't store too much data, so we don't need that big a page |
| // size or cache. |
| connection.set_page_size(2048); |
| connection.set_cache_size(32); |
| |
| // Run the database in exclusive mode. Nobody else should be accessing the |
| // database while we're running, and this will give somewhat improved perf. |
| connection.set_exclusive_locking(); |
| |
| if (!connection.Open(db_name_)) { |
| LOG(ERROR) << connection.GetErrorMessage(); |
| return false; |
| } |
| connection.Close(); |
| return true; |
| } |
| |
| bool AndroidCacheDatabase::CreateBookmarkCacheTable() { |
| const char* name = "android_cache_db.bookmark_cache"; |
| DCHECK(!GetDB().DoesTableExist(name)); |
| |
| std::string sql; |
| sql.append("CREATE TABLE "); |
| sql.append(name); |
| sql.append("(" |
| "id INTEGER PRIMARY KEY," |
| "created_time INTEGER NOT NULL," // Time in millisecond. |
| "last_visit_time INTEGER NOT NULL," // Time in millisecond. |
| "url_id INTEGER NOT NULL," // url id in urls table. |
| "favicon_id INTEGER DEFAULT NULL," // favicon id. |
| "bookmark INTEGER DEFAULT 0" // whether is bookmark. |
| ")"); |
| if (!GetDB().Execute(sql.c_str())) { |
| LOG(ERROR) << GetDB().GetErrorMessage(); |
| return false; |
| } |
| |
| sql.assign("CREATE INDEX "); |
| sql.append("android_cache_db.bookmark_cache_url_id_idx ON " |
| "bookmark_cache(url_id)"); |
| if (!GetDB().Execute(sql.c_str())) { |
| LOG(ERROR) << GetDB().GetErrorMessage(); |
| return false; |
| } |
| 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. |
| int transaction_nesting = GetDB().transaction_nesting(); |
| int count = transaction_nesting; |
| while (count--) |
| GetDB().CommitTransaction(); |
| |
| bool result = DoAttach(); |
| |
| // No matter whether the attach succeeded or not, we need to create the |
| // transaction stack again. |
| count = transaction_nesting; |
| while (count--) |
| GetDB().BeginTransaction(); |
| return result; |
| } |
| |
| bool AndroidCacheDatabase::DoAttach() { |
| std::string sql("ATTACH ? AS android_cache_db"); |
| sql::Statement attach(GetDB().GetUniqueStatement(sql.c_str())); |
| if (!attach.is_valid()) |
| // Keep the transaction open, even though we failed. |
| return false; |
| |
| attach.BindString(0, db_name_.value()); |
| if (!attach.Run()) { |
| LOG(ERROR) << GetDB().GetErrorMessage(); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace history |