Home | History | Annotate | Download | only in history
      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/shortcuts_database.h"
      6 
      7 #include <string>
      8 #include <vector>
      9 
     10 #include "base/guid.h"
     11 #include "base/logging.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/time/time.h"
     14 #include "sql/statement.h"
     15 #include "sql/transaction.h"
     16 
     17 namespace {
     18 
     19 void BindShortcutToStatement(
     20     const history::ShortcutsBackend::Shortcut& shortcut,
     21     sql::Statement* s) {
     22   DCHECK(base::IsValidGUID(shortcut.id));
     23   s->BindString(0, shortcut.id);
     24   s->BindString16(1, shortcut.text);
     25   s->BindString16(2, shortcut.match_core.fill_into_edit);
     26   s->BindString(3, shortcut.match_core.destination_url.spec());
     27   s->BindString16(4, shortcut.match_core.contents);
     28   s->BindString(5, AutocompleteMatch::ClassificationsToString(
     29       shortcut.match_core.contents_class));
     30   s->BindString16(6, shortcut.match_core.description);
     31   s->BindString(7, AutocompleteMatch::ClassificationsToString(
     32       shortcut.match_core.description_class));
     33   s->BindInt(8, shortcut.match_core.transition);
     34   s->BindInt(9, shortcut.match_core.type);
     35   s->BindString16(10, shortcut.match_core.keyword);
     36   s->BindInt64(11, shortcut.last_access_time.ToInternalValue());
     37   s->BindInt(12, shortcut.number_of_hits);
     38 }
     39 
     40 bool DeleteShortcut(const char* field_name,
     41                     const std::string& id,
     42                     sql::Connection& db) {
     43   sql::Statement s(db.GetUniqueStatement(
     44       base::StringPrintf("DELETE FROM omni_box_shortcuts WHERE %s = ?",
     45                          field_name).c_str()));
     46   s.BindString(0, id);
     47   return s.Run();
     48 }
     49 
     50 }  // namespace
     51 
     52 namespace history {
     53 
     54 ShortcutsDatabase::ShortcutsDatabase(const base::FilePath& database_path)
     55     : database_path_(database_path) {
     56 }
     57 
     58 bool ShortcutsDatabase::Init() {
     59   db_.set_histogram_tag("Shortcuts");
     60 
     61   // Set the database page size to something a little larger to give us
     62   // better performance (we're typically seek rather than bandwidth limited).
     63   // This only has an effect before any tables have been created, otherwise
     64   // this is a NOP. Must be a power of 2 and a max of 8192.
     65   db_.set_page_size(4096);
     66 
     67   // Run the database in exclusive mode. Nobody else should be accessing the
     68   // database while we're running, and this will give somewhat improved perf.
     69   db_.set_exclusive_locking();
     70 
     71   // Attach the database to our index file.
     72   return db_.Open(database_path_) && EnsureTable();
     73 }
     74 
     75 bool ShortcutsDatabase::AddShortcut(
     76     const ShortcutsBackend::Shortcut& shortcut) {
     77   sql::Statement s(db_.GetCachedStatement(
     78       SQL_FROM_HERE,
     79       "INSERT INTO omni_box_shortcuts (id, text, fill_into_edit, url, "
     80           "contents, contents_class, description, description_class, "
     81           "transition, type, keyword, last_access_time, number_of_hits) "
     82           "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)"));
     83   BindShortcutToStatement(shortcut, &s);
     84   return s.Run();
     85 }
     86 
     87 bool ShortcutsDatabase::UpdateShortcut(
     88     const ShortcutsBackend::Shortcut& shortcut) {
     89   sql::Statement s(db_.GetCachedStatement(
     90       SQL_FROM_HERE,
     91       "UPDATE omni_box_shortcuts SET id=?, text=?, fill_into_edit=?, url=?, "
     92           "contents=?, contents_class=?, description=?, description_class=?, "
     93           "transition=?, type=?, keyword=?, last_access_time=?, "
     94           "number_of_hits=? WHERE id=?"));
     95   BindShortcutToStatement(shortcut, &s);
     96   s.BindString(13, shortcut.id);
     97   return s.Run();
     98 }
     99 
    100 bool ShortcutsDatabase::DeleteShortcutsWithIds(
    101     const std::vector<std::string>& shortcut_ids) {
    102   bool success = true;
    103   db_.BeginTransaction();
    104   for (std::vector<std::string>::const_iterator it(shortcut_ids.begin());
    105        it != shortcut_ids.end(); ++it) {
    106     success &= DeleteShortcut("id", *it, db_);
    107   }
    108   db_.CommitTransaction();
    109   return success;
    110 }
    111 
    112 bool ShortcutsDatabase::DeleteShortcutsWithUrl(
    113     const std::string& shortcut_url_spec) {
    114   return DeleteShortcut("url", shortcut_url_spec, db_);
    115 }
    116 
    117 bool ShortcutsDatabase::DeleteAllShortcuts() {
    118   if (!db_.Execute("DELETE FROM omni_box_shortcuts"))
    119     return false;
    120 
    121   ignore_result(db_.Execute("VACUUM"));
    122   return true;
    123 }
    124 
    125 void ShortcutsDatabase::LoadShortcuts(GuidToShortcutMap* shortcuts) {
    126   DCHECK(shortcuts);
    127   sql::Statement s(db_.GetCachedStatement(
    128       SQL_FROM_HERE,
    129       "SELECT id, text, fill_into_edit, url, contents, contents_class, "
    130           "description, description_class, transition, type, keyword, "
    131           "last_access_time, number_of_hits FROM omni_box_shortcuts"));
    132 
    133   shortcuts->clear();
    134   while (s.Step()) {
    135     shortcuts->insert(std::make_pair(
    136         s.ColumnString(0),
    137         ShortcutsBackend::Shortcut(
    138             s.ColumnString(0),            // id
    139             s.ColumnString16(1),          // text
    140             ShortcutsBackend::Shortcut::MatchCore(
    141                 s.ColumnString16(2),      // fill_into_edit
    142                 GURL(s.ColumnString(3)),  // destination_url
    143                 s.ColumnString16(4),      // contents
    144                 AutocompleteMatch::ClassificationsFromString(s.ColumnString(5)),
    145                                           // contents_class
    146                 s.ColumnString16(6),      // description
    147                 AutocompleteMatch::ClassificationsFromString(s.ColumnString(7)),
    148                                           // description_class
    149                 static_cast<content::PageTransition>(s.ColumnInt(8)),
    150                                           // transition
    151                 static_cast<AutocompleteMatch::Type>(s.ColumnInt(9)),
    152                                           // type
    153                 s.ColumnString16(10)),    // keyword
    154             base::Time::FromInternalValue(s.ColumnInt64(11)),
    155                                           // last_access_time
    156             s.ColumnInt(12))));           // number_of_hits
    157   }
    158 }
    159 
    160 ShortcutsDatabase::~ShortcutsDatabase() {
    161 }
    162 
    163 bool ShortcutsDatabase::EnsureTable() {
    164   if (!db_.DoesTableExist("omni_box_shortcuts")) {
    165     return db_.Execute(
    166         "CREATE TABLE omni_box_shortcuts (id VARCHAR PRIMARY KEY, "
    167             "text VARCHAR, fill_into_edit VARCHAR, url VARCHAR, "
    168             "contents VARCHAR, contents_class VARCHAR, description VARCHAR, "
    169             "description_class VARCHAR, transition INTEGER, type INTEGER, "
    170             "keyword VARCHAR, last_access_time INTEGER, "
    171             "number_of_hits INTEGER)");
    172   }
    173 
    174   // The first version of the shortcuts table lacked the fill_into_edit,
    175   // transition, type, and keyword columns.
    176   if (!db_.DoesColumnExist("omni_box_shortcuts", "fill_into_edit")) {
    177     // Perform the upgrade in a transaction to ensure it doesn't happen
    178     // incompletely.
    179     sql::Transaction transaction(&db_);
    180     transaction.Begin();
    181     return
    182         db_.Execute("ALTER TABLE omni_box_shortcuts "
    183             "ADD COLUMN fill_into_edit VARCHAR") &&
    184         db_.Execute("UPDATE omni_box_shortcuts SET fill_into_edit = url") &&
    185         db_.Execute("ALTER TABLE omni_box_shortcuts "
    186             "ADD COLUMN transition INTEGER") &&
    187         db_.Execute(base::StringPrintf(
    188             "UPDATE omni_box_shortcuts SET transition = %d",
    189             static_cast<int>(content::PAGE_TRANSITION_TYPED)).c_str()) &&
    190         db_.Execute("ALTER TABLE omni_box_shortcuts ADD COLUMN type INTEGER") &&
    191         db_.Execute(base::StringPrintf(
    192             "UPDATE omni_box_shortcuts SET type = %d",
    193             static_cast<int>(AutocompleteMatchType::HISTORY_TITLE)).c_str()) &&
    194         db_.Execute("ALTER TABLE omni_box_shortcuts "
    195             "ADD COLUMN keyword VARCHAR") &&
    196         transaction.Commit();
    197   }
    198 
    199   return true;
    200 }
    201 
    202 }  // namespace history
    203