Home | History | Annotate | Download | only in android
      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/android/android_cache_database.h"
      6 
      7 #include "base/files/file_util.h"
      8 #include "chrome/browser/history/android/android_time.h"
      9 #include "sql/statement.h"
     10 
     11 using base::Time;
     12 using base::TimeDelta;
     13 
     14 namespace history {
     15 
     16 AndroidCacheDatabase::AndroidCacheDatabase() {
     17 }
     18 
     19 AndroidCacheDatabase::~AndroidCacheDatabase() {
     20 }
     21 
     22 sql::InitStatus AndroidCacheDatabase::InitAndroidCacheDatabase(
     23     const base::FilePath& db_name) {
     24   if (!CreateDatabase(db_name))
     25     return sql::INIT_FAILURE;
     26 
     27   if (!Attach())
     28     return sql::INIT_FAILURE;
     29 
     30   if (!CreateBookmarkCacheTable())
     31     return sql::INIT_FAILURE;
     32 
     33   if (!CreateSearchTermsTable())
     34     return sql::INIT_FAILURE;
     35 
     36   return sql::INIT_OK;
     37 }
     38 
     39 bool AndroidCacheDatabase::AddBookmarkCacheRow(const Time& created_time,
     40                                                const Time& last_visit_time,
     41                                                URLID url_id) {
     42   sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
     43       "INSERT INTO android_cache_db.bookmark_cache (created_time, "
     44       "last_visit_time, url_id) VALUES (?, ?, ?)"));
     45 
     46   statement.BindInt64(0, ToDatabaseTime(created_time));
     47   statement.BindInt64(1, ToDatabaseTime(last_visit_time));
     48   statement.BindInt64(2, url_id);
     49 
     50   if (!statement.Run()) {
     51     LOG(ERROR) << GetDB().GetErrorMessage();
     52     return false;
     53   }
     54 
     55   return true;
     56 }
     57 
     58 bool AndroidCacheDatabase::ClearAllBookmarkCache() {
     59   sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
     60       "DELETE FROM android_cache_db.bookmark_cache"));
     61   if (!statement.Run()) {
     62     LOG(ERROR) << GetDB().GetErrorMessage();
     63     return false;
     64   }
     65   return true;
     66 }
     67 
     68 bool AndroidCacheDatabase::MarkURLsAsBookmarked(
     69     const std::vector<URLID>& url_ids) {
     70   bool has_id = false;
     71   std::ostringstream oss;
     72   for (std::vector<URLID>::const_iterator i = url_ids.begin();
     73       i != url_ids.end(); ++i) {
     74     if (has_id)
     75       oss << ", ";
     76     else
     77       has_id = true;
     78     oss << *i;
     79   }
     80 
     81   if (!has_id)
     82     return true;
     83 
     84   std::string sql("UPDATE android_cache_db.bookmark_cache "
     85                   "SET bookmark = 1 WHERE url_id in (");
     86   sql.append(oss.str());
     87   sql.append(")");
     88   if (!GetDB().Execute(sql.c_str())) {
     89     LOG(ERROR) << GetDB().GetErrorMessage();
     90     return false;
     91   }
     92   return true;
     93 }
     94 
     95 bool AndroidCacheDatabase::SetFaviconID(URLID url_id,
     96                                         favicon_base::FaviconID favicon_id) {
     97   sql::Statement update_statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
     98       "UPDATE android_cache_db.bookmark_cache "
     99       "SET favicon_id = ? WHERE url_id = ? "));
    100 
    101   update_statement.BindInt64(0, favicon_id);
    102   update_statement.BindInt64(1, url_id);
    103   if (!update_statement.Run()) {
    104     LOG(ERROR) << GetDB().GetErrorMessage();
    105     return false;
    106   }
    107   return true;
    108 }
    109 
    110 SearchTermID AndroidCacheDatabase::AddSearchTerm(
    111     const base::string16& term,
    112     const base::Time& last_visit_time) {
    113   sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
    114       "INSERT INTO android_cache_db.search_terms (search, "
    115       "date) VALUES (?, ?)"));
    116 
    117   statement.BindString16(0, term);
    118   statement.BindInt64(1, ToDatabaseTime(last_visit_time));
    119 
    120   if (!statement.Run()) {
    121     LOG(ERROR) << GetDB().GetErrorMessage();
    122     return 0;
    123   }
    124 
    125   return GetDB().GetLastInsertRowId();
    126 }
    127 
    128 bool AndroidCacheDatabase::UpdateSearchTerm(SearchTermID id,
    129                                             const SearchTermRow& row) {
    130   sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
    131       "UPDATE android_cache_db.search_terms "
    132       "SET search = ?, date = ? "
    133       "WHERE _id = ?"
    134       ));
    135   statement.BindString16(0, row.term);
    136   statement.BindInt64(1, ToDatabaseTime(row.last_visit_time));
    137   statement.BindInt64(2, id);
    138 
    139   return statement.Run();
    140 }
    141 
    142 SearchTermID AndroidCacheDatabase::GetSearchTerm(const base::string16& term,
    143                                                  SearchTermRow* row) {
    144   sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
    145       "SELECT _id, search, date "
    146       "FROM android_cache_db.search_terms "
    147       "WHERE search = ?"
    148       ));
    149   if (!statement.is_valid()) {
    150     LOG(ERROR) << GetDB().GetErrorMessage();
    151     return 0;
    152   }
    153   statement.BindString16(0, term);
    154   if (!statement.Step())
    155     return 0;
    156 
    157   if (row) {
    158     row->id = statement.ColumnInt64(0);
    159     row->term = statement.ColumnString16(1);
    160     row->last_visit_time = FromDatabaseTime(statement.ColumnInt64(2));
    161   }
    162   return statement.ColumnInt64(0);
    163 }
    164 
    165 bool AndroidCacheDatabase::DeleteUnusedSearchTerms() {
    166   sql::Statement statement(GetDB().GetCachedStatement(SQL_FROM_HERE,
    167       "DELETE FROM android_cache_db.search_terms "
    168       "WHERE search NOT IN (SELECT DISTINCT term FROM keyword_search_terms)"
    169       ));
    170   if (!statement.is_valid())
    171     return false;
    172   return statement.Run();
    173 }
    174 
    175 bool AndroidCacheDatabase::CreateDatabase(const base::FilePath& db_name) {
    176   db_name_ = db_name;
    177   sql::Connection::Delete(db_name_);
    178 
    179   // Using a new connection, otherwise we can not create the database.
    180   sql::Connection connection;
    181 
    182   // The db doesn't store too much data, so we don't need that big a page
    183   // size or cache.
    184   connection.set_page_size(2048);
    185   connection.set_cache_size(32);
    186 
    187   // Run the database in exclusive mode. Nobody else should be accessing the
    188   // database while we're running, and this will give somewhat improved perf.
    189   connection.set_exclusive_locking();
    190 
    191   if (!connection.Open(db_name_)) {
    192     LOG(ERROR) << connection.GetErrorMessage();
    193     return false;
    194   }
    195   connection.Close();
    196   return true;
    197 }
    198 
    199 bool AndroidCacheDatabase::CreateBookmarkCacheTable() {
    200   const char* name = "android_cache_db.bookmark_cache";
    201   DCHECK(!GetDB().DoesTableExist(name));
    202 
    203   std::string sql;
    204   sql.append("CREATE TABLE ");
    205   sql.append(name);
    206   sql.append("("
    207              "id INTEGER PRIMARY KEY,"
    208              "created_time INTEGER NOT NULL,"     // Time in millisecond.
    209              "last_visit_time INTEGER NOT NULL,"  // Time in millisecond.
    210              "url_id INTEGER NOT NULL,"           // url id in urls table.
    211              "favicon_id INTEGER DEFAULT NULL,"   // favicon id.
    212              "bookmark INTEGER DEFAULT 0"         // whether is bookmark.
    213              ")");
    214   if (!GetDB().Execute(sql.c_str())) {
    215     LOG(ERROR) << GetDB().GetErrorMessage();
    216     return false;
    217   }
    218 
    219   sql.assign("CREATE INDEX ");
    220   sql.append("android_cache_db.bookmark_cache_url_id_idx ON "
    221              "bookmark_cache(url_id)");
    222   if (!GetDB().Execute(sql.c_str())) {
    223     LOG(ERROR) << GetDB().GetErrorMessage();
    224     return false;
    225   }
    226   return true;
    227 }
    228 
    229 bool AndroidCacheDatabase::CreateSearchTermsTable() {
    230   const char* name = "android_cache_db.search_terms";
    231 
    232   // The table's column name matchs Android's definition.
    233   std::string sql;
    234   sql.append("CREATE TABLE ");
    235   sql.append(name);
    236   sql.append("("
    237              "_id INTEGER PRIMARY KEY,"
    238              "date INTEGER NOT NULL,"   // last visit time in millisecond.
    239              "search LONGVARCHAR NOT NULL)");   // The actual search term.
    240 
    241   if (!GetDB().Execute(sql.c_str())) {
    242     LOG(ERROR) << GetDB().GetErrorMessage();
    243     return false;
    244   }
    245 
    246   sql.assign("CREATE INDEX "
    247              "android_cache_db.search_terms_term_idx ON "
    248              "search_terms(search)");
    249   if (!GetDB().Execute(sql.c_str())) {
    250     LOG(ERROR) << GetDB().GetErrorMessage();
    251     return false;
    252   }
    253   return true;
    254 }
    255 
    256 bool AndroidCacheDatabase::Attach() {
    257   // Commit all open transactions to make attach succeed.
    258   int transaction_nesting = GetDB().transaction_nesting();
    259   int count = transaction_nesting;
    260   while (count--)
    261     GetDB().CommitTransaction();
    262 
    263   bool result = DoAttach();
    264 
    265   // No matter whether the attach succeeded or not, we need to create the
    266   // transaction stack again.
    267   count = transaction_nesting;
    268   while (count--)
    269     GetDB().BeginTransaction();
    270   return result;
    271 }
    272 
    273 bool AndroidCacheDatabase::DoAttach() {
    274   std::string sql("ATTACH ? AS android_cache_db");
    275   sql::Statement attach(GetDB().GetUniqueStatement(sql.c_str()));
    276   if (!attach.is_valid())
    277     // Keep the transaction open, even though we failed.
    278     return false;
    279 
    280   attach.BindString(0, db_name_.value());
    281   if (!attach.Run()) {
    282     LOG(ERROR) << GetDB().GetErrorMessage();
    283     return false;
    284   }
    285 
    286   return true;
    287 }
    288 
    289 }  // namespace history
    290