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