| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2014 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/webdata/keyword_table.h" |
| 6 |
| 7 #include <set> |
| 8 |
| 9 #include "base/json/json_reader.h" |
| 10 #include "base/json/json_writer.h" |
| 11 #include "base/logging.h" |
| 12 #include "base/memory/scoped_ptr.h" |
| 13 #include "base/strings/string_number_conversions.h" |
| 14 #include "base/strings/string_split.h" |
| 15 #include "base/strings/string_util.h" |
| 16 #include "base/strings/utf_string_conversions.h" |
| 17 #include "base/values.h" |
| 18 #include "chrome/browser/history/history_database.h" |
| 19 #include "components/search_engines/search_terms_data.h" |
| 20 #include "components/search_engines/template_url.h" |
| 21 #include "components/webdata/common/web_database.h" |
| 22 #include "sql/statement.h" |
| 23 #include "sql/transaction.h" |
| 24 #include "url/gurl.h" |
| 25 |
| 26 using base::Time; |
| 27 |
| 28 // static |
| 29 const char KeywordTable::kDefaultSearchProviderKey[] = |
| 30 "Default Search Provider ID"; |
| 31 |
| 32 namespace { |
| 33 |
| 34 // Keys used in the meta table. |
| 35 const char kBuiltinKeywordVersion[] = "Builtin Keyword Version"; |
| 36 |
| 37 const std::string ColumnsForVersion(int version, bool concatenated) { |
| 38 std::vector<std::string> columns; |
| 39 |
| 40 columns.push_back("id"); |
| 41 columns.push_back("short_name"); |
| 42 columns.push_back("keyword"); |
| 43 columns.push_back("favicon_url"); |
| 44 columns.push_back("url"); |
| 45 columns.push_back("safe_for_autoreplace"); |
| 46 columns.push_back("originating_url"); |
| 47 columns.push_back("date_created"); |
| 48 columns.push_back("usage_count"); |
| 49 columns.push_back("input_encodings"); |
| 50 columns.push_back("show_in_default_list"); |
| 51 columns.push_back("suggest_url"); |
| 52 columns.push_back("prepopulate_id"); |
| 53 if (version <= 44) { |
| 54 // Columns removed after version 44. |
| 55 columns.push_back("autogenerate_keyword"); |
| 56 columns.push_back("logo_id"); |
| 57 } |
| 58 columns.push_back("created_by_policy"); |
| 59 columns.push_back("instant_url"); |
| 60 columns.push_back("last_modified"); |
| 61 columns.push_back("sync_guid"); |
| 62 if (version >= 47) { |
| 63 // Column added in version 47. |
| 64 columns.push_back("alternate_urls"); |
| 65 } |
| 66 if (version >= 49) { |
| 67 // Column added in version 49. |
| 68 columns.push_back("search_terms_replacement_key"); |
| 69 } |
| 70 if (version >= 52) { |
| 71 // Column added in version 52. |
| 72 columns.push_back("image_url"); |
| 73 columns.push_back("search_url_post_params"); |
| 74 columns.push_back("suggest_url_post_params"); |
| 75 columns.push_back("instant_url_post_params"); |
| 76 columns.push_back("image_url_post_params"); |
| 77 } |
| 78 if (version >= 53) { |
| 79 // Column added in version 53. |
| 80 columns.push_back("new_tab_url"); |
| 81 } |
| 82 |
| 83 return JoinString(columns, std::string(concatenated ? " || " : ", ")); |
| 84 } |
| 85 |
| 86 |
| 87 // Inserts the data from |data| into |s|. |s| is assumed to have slots for all |
| 88 // the columns in the keyword table. |id_column| is the slot number to bind |
| 89 // |data|'s |id| to; |starting_column| is the slot number of the first of a |
| 90 // contiguous set of slots to bind all the other fields to. |
| 91 void BindURLToStatement(const TemplateURLData& data, |
| 92 sql::Statement* s, |
| 93 int id_column, |
| 94 int starting_column) { |
| 95 // Serialize |alternate_urls| to JSON. |
| 96 // TODO(beaudoin): Check what it would take to use a new table to store |
| 97 // alternate_urls while keeping backups and table signature in a good state. |
| 98 // See: crbug.com/153520 |
| 99 base::ListValue alternate_urls_value; |
| 100 for (size_t i = 0; i < data.alternate_urls.size(); ++i) |
| 101 alternate_urls_value.AppendString(data.alternate_urls[i]); |
| 102 std::string alternate_urls; |
| 103 base::JSONWriter::Write(&alternate_urls_value, &alternate_urls); |
| 104 |
| 105 s->BindInt64(id_column, data.id); |
| 106 s->BindString16(starting_column, data.short_name); |
| 107 s->BindString16(starting_column + 1, data.keyword()); |
| 108 s->BindString(starting_column + 2, data.favicon_url.is_valid() ? |
| 109 history::HistoryDatabase::GURLToDatabaseURL(data.favicon_url) : |
| 110 std::string()); |
| 111 s->BindString(starting_column + 3, data.url()); |
| 112 s->BindBool(starting_column + 4, data.safe_for_autoreplace); |
| 113 s->BindString(starting_column + 5, data.originating_url.is_valid() ? |
| 114 history::HistoryDatabase::GURLToDatabaseURL(data.originating_url) : |
| 115 std::string()); |
| 116 s->BindInt64(starting_column + 6, data.date_created.ToTimeT()); |
| 117 s->BindInt(starting_column + 7, data.usage_count); |
| 118 s->BindString(starting_column + 8, JoinString(data.input_encodings, ';')); |
| 119 s->BindBool(starting_column + 9, data.show_in_default_list); |
| 120 s->BindString(starting_column + 10, data.suggestions_url); |
| 121 s->BindInt(starting_column + 11, data.prepopulate_id); |
| 122 s->BindBool(starting_column + 12, data.created_by_policy); |
| 123 s->BindString(starting_column + 13, data.instant_url); |
| 124 s->BindInt64(starting_column + 14, data.last_modified.ToTimeT()); |
| 125 s->BindString(starting_column + 15, data.sync_guid); |
| 126 s->BindString(starting_column + 16, alternate_urls); |
| 127 s->BindString(starting_column + 17, data.search_terms_replacement_key); |
| 128 s->BindString(starting_column + 18, data.image_url); |
| 129 s->BindString(starting_column + 19, data.search_url_post_params); |
| 130 s->BindString(starting_column + 20, data.suggestions_url_post_params); |
| 131 s->BindString(starting_column + 21, data.instant_url_post_params); |
| 132 s->BindString(starting_column + 22, data.image_url_post_params); |
| 133 s->BindString(starting_column + 23, data.new_tab_url); |
| 134 } |
| 135 |
| 136 WebDatabaseTable::TypeKey GetKey() { |
| 137 // We just need a unique constant. Use the address of a static that |
| 138 // COMDAT folding won't touch in an optimizing linker. |
| 139 static int table_key = 0; |
| 140 return reinterpret_cast<void*>(&table_key); |
| 141 } |
| 142 |
| 143 } // namespace |
| 144 |
| 145 KeywordTable::KeywordTable() { |
| 146 } |
| 147 |
| 148 KeywordTable::~KeywordTable() {} |
| 149 |
| 150 KeywordTable* KeywordTable::FromWebDatabase(WebDatabase* db) { |
| 151 return static_cast<KeywordTable*>(db->GetTable(GetKey())); |
| 152 } |
| 153 |
| 154 WebDatabaseTable::TypeKey KeywordTable::GetTypeKey() const { |
| 155 return GetKey(); |
| 156 } |
| 157 |
| 158 bool KeywordTable::CreateTablesIfNecessary() { |
| 159 return db_->DoesTableExist("keywords") || |
| 160 db_->Execute("CREATE TABLE keywords (" |
| 161 "id INTEGER PRIMARY KEY," |
| 162 "short_name VARCHAR NOT NULL," |
| 163 "keyword VARCHAR NOT NULL," |
| 164 "favicon_url VARCHAR NOT NULL," |
| 165 "url VARCHAR NOT NULL," |
| 166 "safe_for_autoreplace INTEGER," |
| 167 "originating_url VARCHAR," |
| 168 "date_created INTEGER DEFAULT 0," |
| 169 "usage_count INTEGER DEFAULT 0," |
| 170 "input_encodings VARCHAR," |
| 171 "show_in_default_list INTEGER," |
| 172 "suggest_url VARCHAR," |
| 173 "prepopulate_id INTEGER DEFAULT 0," |
| 174 "created_by_policy INTEGER DEFAULT 0," |
| 175 "instant_url VARCHAR," |
| 176 "last_modified INTEGER DEFAULT 0," |
| 177 "sync_guid VARCHAR," |
| 178 "alternate_urls VARCHAR," |
| 179 "search_terms_replacement_key VARCHAR," |
| 180 "image_url VARCHAR," |
| 181 "search_url_post_params VARCHAR," |
| 182 "suggest_url_post_params VARCHAR," |
| 183 "instant_url_post_params VARCHAR," |
| 184 "image_url_post_params VARCHAR," |
| 185 "new_tab_url VARCHAR)"); |
| 186 } |
| 187 |
| 188 bool KeywordTable::IsSyncable() { |
| 189 return true; |
| 190 } |
| 191 |
| 192 bool KeywordTable::MigrateToVersion(int version, |
| 193 bool* update_compatible_version) { |
| 194 // Migrate if necessary. |
| 195 switch (version) { |
| 196 case 21: |
| 197 *update_compatible_version = true; |
| 198 return MigrateToVersion21AutoGenerateKeywordColumn(); |
| 199 case 25: |
| 200 *update_compatible_version = true; |
| 201 return MigrateToVersion25AddLogoIDColumn(); |
| 202 case 26: |
| 203 *update_compatible_version = true; |
| 204 return MigrateToVersion26AddCreatedByPolicyColumn(); |
| 205 case 28: |
| 206 *update_compatible_version = true; |
| 207 return MigrateToVersion28SupportsInstantColumn(); |
| 208 case 29: |
| 209 *update_compatible_version = true; |
| 210 return MigrateToVersion29InstantURLToSupportsInstant(); |
| 211 case 38: |
| 212 *update_compatible_version = true; |
| 213 return MigrateToVersion38AddLastModifiedColumn(); |
| 214 case 39: |
| 215 *update_compatible_version = true; |
| 216 return MigrateToVersion39AddSyncGUIDColumn(); |
| 217 case 44: |
| 218 *update_compatible_version = true; |
| 219 return MigrateToVersion44AddDefaultSearchProviderBackup(); |
| 220 case 45: |
| 221 *update_compatible_version = true; |
| 222 return MigrateToVersion45RemoveLogoIDAndAutogenerateColumns(); |
| 223 case 47: |
| 224 *update_compatible_version = true; |
| 225 return MigrateToVersion47AddAlternateURLsColumn(); |
| 226 case 48: |
| 227 *update_compatible_version = true; |
| 228 return MigrateToVersion48RemoveKeywordsBackup(); |
| 229 case 49: |
| 230 *update_compatible_version = true; |
| 231 return MigrateToVersion49AddSearchTermsReplacementKeyColumn(); |
| 232 case 52: |
| 233 *update_compatible_version = true; |
| 234 return MigrateToVersion52AddImageSearchAndPOSTSupport(); |
| 235 case 53: |
| 236 *update_compatible_version = true; |
| 237 return MigrateToVersion53AddNewTabURLColumn(); |
| 238 } |
| 239 |
| 240 return true; |
| 241 } |
| 242 |
| 243 bool KeywordTable::PerformOperations(const Operations& operations) { |
| 244 sql::Transaction transaction(db_); |
| 245 if (!transaction.Begin()) |
| 246 return false; |
| 247 |
| 248 for (Operations::const_iterator i(operations.begin()); i != operations.end(); |
| 249 ++i) { |
| 250 switch (i->first) { |
| 251 case ADD: |
| 252 if (!AddKeyword(i->second)) |
| 253 return false; |
| 254 break; |
| 255 |
| 256 case REMOVE: |
| 257 if (!RemoveKeyword(i->second.id)) |
| 258 return false; |
| 259 break; |
| 260 |
| 261 case UPDATE: |
| 262 if (!UpdateKeyword(i->second)) |
| 263 return false; |
| 264 break; |
| 265 } |
| 266 } |
| 267 |
| 268 return transaction.Commit(); |
| 269 } |
| 270 |
| 271 bool KeywordTable::GetKeywords(Keywords* keywords) { |
| 272 std::string query("SELECT " + GetKeywordColumns() + |
| 273 " FROM keywords ORDER BY id ASC"); |
| 274 sql::Statement s(db_->GetUniqueStatement(query.c_str())); |
| 275 |
| 276 std::set<TemplateURLID> bad_entries; |
| 277 while (s.Step()) { |
| 278 keywords->push_back(TemplateURLData()); |
| 279 if (!GetKeywordDataFromStatement(s, &keywords->back())) { |
| 280 bad_entries.insert(s.ColumnInt64(0)); |
| 281 keywords->pop_back(); |
| 282 } |
| 283 } |
| 284 bool succeeded = s.Succeeded(); |
| 285 for (std::set<TemplateURLID>::const_iterator i(bad_entries.begin()); |
| 286 i != bad_entries.end(); ++i) |
| 287 succeeded &= RemoveKeyword(*i); |
| 288 return succeeded; |
| 289 } |
| 290 |
| 291 bool KeywordTable::SetDefaultSearchProviderID(int64 id) { |
| 292 return meta_table_->SetValue(kDefaultSearchProviderKey, id); |
| 293 } |
| 294 |
| 295 int64 KeywordTable::GetDefaultSearchProviderID() { |
| 296 int64 value = kInvalidTemplateURLID; |
| 297 meta_table_->GetValue(kDefaultSearchProviderKey, &value); |
| 298 return value; |
| 299 } |
| 300 |
| 301 bool KeywordTable::SetBuiltinKeywordVersion(int version) { |
| 302 return meta_table_->SetValue(kBuiltinKeywordVersion, version); |
| 303 } |
| 304 |
| 305 int KeywordTable::GetBuiltinKeywordVersion() { |
| 306 int version = 0; |
| 307 return meta_table_->GetValue(kBuiltinKeywordVersion, &version) ? version : 0; |
| 308 } |
| 309 |
| 310 // static |
| 311 std::string KeywordTable::GetKeywordColumns() { |
| 312 return ColumnsForVersion(WebDatabase::kCurrentVersionNumber, false); |
| 313 } |
| 314 |
| 315 bool KeywordTable::MigrateToVersion21AutoGenerateKeywordColumn() { |
| 316 return db_->Execute("ALTER TABLE keywords ADD COLUMN autogenerate_keyword " |
| 317 "INTEGER DEFAULT 0"); |
| 318 } |
| 319 |
| 320 bool KeywordTable::MigrateToVersion25AddLogoIDColumn() { |
| 321 return db_->Execute( |
| 322 "ALTER TABLE keywords ADD COLUMN logo_id INTEGER DEFAULT 0"); |
| 323 } |
| 324 |
| 325 bool KeywordTable::MigrateToVersion26AddCreatedByPolicyColumn() { |
| 326 return db_->Execute("ALTER TABLE keywords ADD COLUMN created_by_policy " |
| 327 "INTEGER DEFAULT 0"); |
| 328 } |
| 329 |
| 330 bool KeywordTable::MigrateToVersion28SupportsInstantColumn() { |
| 331 return db_->Execute("ALTER TABLE keywords ADD COLUMN supports_instant " |
| 332 "INTEGER DEFAULT 0"); |
| 333 } |
| 334 |
| 335 bool KeywordTable::MigrateToVersion29InstantURLToSupportsInstant() { |
| 336 sql::Transaction transaction(db_); |
| 337 return transaction.Begin() && |
| 338 db_->Execute("ALTER TABLE keywords ADD COLUMN instant_url VARCHAR") && |
| 339 db_->Execute("CREATE TABLE keywords_temp (" |
| 340 "id INTEGER PRIMARY KEY," |
| 341 "short_name VARCHAR NOT NULL," |
| 342 "keyword VARCHAR NOT NULL," |
| 343 "favicon_url VARCHAR NOT NULL," |
| 344 "url VARCHAR NOT NULL," |
| 345 "safe_for_autoreplace INTEGER," |
| 346 "originating_url VARCHAR," |
| 347 "date_created INTEGER DEFAULT 0," |
| 348 "usage_count INTEGER DEFAULT 0," |
| 349 "input_encodings VARCHAR," |
| 350 "show_in_default_list INTEGER," |
| 351 "suggest_url VARCHAR," |
| 352 "prepopulate_id INTEGER DEFAULT 0," |
| 353 "autogenerate_keyword INTEGER DEFAULT 0," |
| 354 "logo_id INTEGER DEFAULT 0," |
| 355 "created_by_policy INTEGER DEFAULT 0," |
| 356 "instant_url VARCHAR)") && |
| 357 db_->Execute("INSERT INTO keywords_temp SELECT id, short_name, keyword, " |
| 358 "favicon_url, url, safe_for_autoreplace, originating_url, " |
| 359 "date_created, usage_count, input_encodings, " |
| 360 "show_in_default_list, suggest_url, prepopulate_id, " |
| 361 "autogenerate_keyword, logo_id, created_by_policy, " |
| 362 "instant_url FROM keywords") && |
| 363 db_->Execute("DROP TABLE keywords") && |
| 364 db_->Execute("ALTER TABLE keywords_temp RENAME TO keywords") && |
| 365 transaction.Commit(); |
| 366 } |
| 367 |
| 368 bool KeywordTable::MigrateToVersion38AddLastModifiedColumn() { |
| 369 return db_->Execute( |
| 370 "ALTER TABLE keywords ADD COLUMN last_modified INTEGER DEFAULT 0"); |
| 371 } |
| 372 |
| 373 bool KeywordTable::MigrateToVersion39AddSyncGUIDColumn() { |
| 374 return db_->Execute("ALTER TABLE keywords ADD COLUMN sync_guid VARCHAR"); |
| 375 } |
| 376 |
| 377 bool KeywordTable::MigrateToVersion44AddDefaultSearchProviderBackup() { |
| 378 std::string query("CREATE TABLE keywords_backup AS SELECT " + |
| 379 ColumnsForVersion(44, false) + " FROM keywords ORDER BY id ASC"); |
| 380 sql::Transaction transaction(db_); |
| 381 return transaction.Begin() && |
| 382 meta_table_->SetValue("Default Search Provider ID Backup", |
| 383 GetDefaultSearchProviderID()) && |
| 384 (!db_->DoesTableExist("keywords_backup") || |
| 385 db_->Execute("DROP TABLE keywords_backup")) && |
| 386 db_->Execute(query.c_str()) && |
| 387 transaction.Commit(); |
| 388 } |
| 389 |
| 390 bool KeywordTable::MigrateToVersion45RemoveLogoIDAndAutogenerateColumns() { |
| 391 sql::Transaction transaction(db_); |
| 392 if (!transaction.Begin()) |
| 393 return false; |
| 394 |
| 395 // The version 43 migration should have been written to do this, but since it |
| 396 // wasn't, we'll do it now. Unfortunately a previous change deleted this for |
| 397 // some users, so we can't be sure this will succeed (so don't bail on error). |
| 398 meta_table_->DeleteKey("Default Search Provider Backup"); |
| 399 |
| 400 return MigrateKeywordsTableForVersion45("keywords") && |
| 401 MigrateKeywordsTableForVersion45("keywords_backup") && |
| 402 meta_table_->SetValue("Default Search Provider ID Backup Signature", |
| 403 std::string()) && |
| 404 transaction.Commit(); |
| 405 } |
| 406 |
| 407 bool KeywordTable::MigrateToVersion47AddAlternateURLsColumn() { |
| 408 sql::Transaction transaction(db_); |
| 409 return transaction.Begin() && |
| 410 db_->Execute("ALTER TABLE keywords ADD COLUMN " |
| 411 "alternate_urls VARCHAR DEFAULT ''") && |
| 412 db_->Execute("ALTER TABLE keywords_backup ADD COLUMN " |
| 413 "alternate_urls VARCHAR DEFAULT ''") && |
| 414 meta_table_->SetValue("Default Search Provider ID Backup Signature", |
| 415 std::string()) && |
| 416 transaction.Commit(); |
| 417 } |
| 418 |
| 419 bool KeywordTable::MigrateToVersion48RemoveKeywordsBackup() { |
| 420 sql::Transaction transaction(db_); |
| 421 return transaction.Begin() && |
| 422 meta_table_->DeleteKey("Default Search Provider ID Backup") && |
| 423 meta_table_->DeleteKey("Default Search Provider ID Backup Signature") && |
| 424 db_->Execute("DROP TABLE keywords_backup") && |
| 425 transaction.Commit(); |
| 426 } |
| 427 |
| 428 bool KeywordTable::MigrateToVersion49AddSearchTermsReplacementKeyColumn() { |
| 429 return db_->Execute("ALTER TABLE keywords ADD COLUMN " |
| 430 "search_terms_replacement_key VARCHAR DEFAULT ''"); |
| 431 } |
| 432 |
| 433 bool KeywordTable::MigrateToVersion52AddImageSearchAndPOSTSupport() { |
| 434 sql::Transaction transaction(db_); |
| 435 return transaction.Begin() && |
| 436 db_->Execute("ALTER TABLE keywords ADD COLUMN image_url " |
| 437 "VARCHAR DEFAULT ''") && |
| 438 db_->Execute("ALTER TABLE keywords ADD COLUMN search_url_post_params " |
| 439 "VARCHAR DEFAULT ''") && |
| 440 db_->Execute("ALTER TABLE keywords ADD COLUMN suggest_url_post_params " |
| 441 "VARCHAR DEFAULT ''") && |
| 442 db_->Execute("ALTER TABLE keywords ADD COLUMN instant_url_post_params " |
| 443 "VARCHAR DEFAULT ''") && |
| 444 db_->Execute("ALTER TABLE keywords ADD COLUMN image_url_post_params " |
| 445 "VARCHAR DEFAULT ''") && |
| 446 transaction.Commit(); |
| 447 } |
| 448 |
| 449 bool KeywordTable::MigrateToVersion53AddNewTabURLColumn() { |
| 450 return db_->Execute("ALTER TABLE keywords ADD COLUMN new_tab_url " |
| 451 "VARCHAR DEFAULT ''"); |
| 452 } |
| 453 |
| 454 // static |
| 455 bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement& s, |
| 456 TemplateURLData* data) { |
| 457 DCHECK(data); |
| 458 |
| 459 data->short_name = s.ColumnString16(1); |
| 460 data->SetKeyword(s.ColumnString16(2)); |
| 461 // Due to past bugs, we might have persisted entries with empty URLs. Avoid |
| 462 // reading these out. (GetKeywords() will delete these entries on return.) |
| 463 // NOTE: This code should only be needed as long as we might be reading such |
| 464 // potentially-old data and can be removed afterward. |
| 465 if (s.ColumnString(4).empty()) |
| 466 return false; |
| 467 data->SetURL(s.ColumnString(4)); |
| 468 data->suggestions_url = s.ColumnString(11); |
| 469 data->instant_url = s.ColumnString(14); |
| 470 data->image_url = s.ColumnString(19); |
| 471 data->new_tab_url = s.ColumnString(24); |
| 472 data->search_url_post_params = s.ColumnString(20); |
| 473 data->suggestions_url_post_params = s.ColumnString(21); |
| 474 data->instant_url_post_params = s.ColumnString(22); |
| 475 data->image_url_post_params = s.ColumnString(23); |
| 476 data->favicon_url = GURL(s.ColumnString(3)); |
| 477 data->originating_url = GURL(s.ColumnString(6)); |
| 478 data->show_in_default_list = s.ColumnBool(10); |
| 479 data->safe_for_autoreplace = s.ColumnBool(5); |
| 480 base::SplitString(s.ColumnString(9), ';', &data->input_encodings); |
| 481 data->id = s.ColumnInt64(0); |
| 482 data->date_created = Time::FromTimeT(s.ColumnInt64(7)); |
| 483 data->last_modified = Time::FromTimeT(s.ColumnInt64(15)); |
| 484 data->created_by_policy = s.ColumnBool(13); |
| 485 data->usage_count = s.ColumnInt(8); |
| 486 data->prepopulate_id = s.ColumnInt(12); |
| 487 data->sync_guid = s.ColumnString(16); |
| 488 |
| 489 data->alternate_urls.clear(); |
| 490 base::JSONReader json_reader; |
| 491 scoped_ptr<base::Value> value(json_reader.ReadToValue(s.ColumnString(17))); |
| 492 base::ListValue* alternate_urls_value; |
| 493 if (value.get() && value->GetAsList(&alternate_urls_value)) { |
| 494 std::string alternate_url; |
| 495 for (size_t i = 0; i < alternate_urls_value->GetSize(); ++i) { |
| 496 if (alternate_urls_value->GetString(i, &alternate_url)) |
| 497 data->alternate_urls.push_back(alternate_url); |
| 498 } |
| 499 } |
| 500 |
| 501 data->search_terms_replacement_key = s.ColumnString(18); |
| 502 |
| 503 return true; |
| 504 } |
| 505 |
| 506 bool KeywordTable::AddKeyword(const TemplateURLData& data) { |
| 507 DCHECK(data.id); |
| 508 std::string query("INSERT INTO keywords (" + GetKeywordColumns() + ") " |
| 509 "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?," |
| 510 " ?)"); |
| 511 sql::Statement s(db_->GetCachedStatement(SQL_FROM_HERE, query.c_str())); |
| 512 BindURLToStatement(data, &s, 0, 1); |
| 513 |
| 514 return s.Run(); |
| 515 } |
| 516 |
| 517 bool KeywordTable::RemoveKeyword(TemplateURLID id) { |
| 518 DCHECK(id); |
| 519 sql::Statement s(db_->GetCachedStatement( |
| 520 SQL_FROM_HERE, "DELETE FROM keywords WHERE id = ?")); |
| 521 s.BindInt64(0, id); |
| 522 |
| 523 return s.Run(); |
| 524 } |
| 525 |
| 526 bool KeywordTable::UpdateKeyword(const TemplateURLData& data) { |
| 527 DCHECK(data.id); |
| 528 sql::Statement s(db_->GetCachedStatement( |
| 529 SQL_FROM_HERE, |
| 530 "UPDATE keywords SET short_name=?, keyword=?, favicon_url=?, url=?, " |
| 531 "safe_for_autoreplace=?, originating_url=?, date_created=?, " |
| 532 "usage_count=?, input_encodings=?, show_in_default_list=?, " |
| 533 "suggest_url=?, prepopulate_id=?, created_by_policy=?, instant_url=?, " |
| 534 "last_modified=?, sync_guid=?, alternate_urls=?, " |
| 535 "search_terms_replacement_key=?, image_url=?, search_url_post_params=?, " |
| 536 "suggest_url_post_params=?, instant_url_post_params=?, " |
| 537 "image_url_post_params=?, new_tab_url=? WHERE id=?")); |
| 538 BindURLToStatement(data, &s, 24, 0); // "24" binds id() as the last item. |
| 539 |
| 540 return s.Run(); |
| 541 } |
| 542 |
| 543 bool KeywordTable::GetKeywordAsString(TemplateURLID id, |
| 544 const std::string& table_name, |
| 545 std::string* result) { |
| 546 std::string query("SELECT " + |
| 547 ColumnsForVersion(WebDatabase::kCurrentVersionNumber, true) + |
| 548 " FROM " + table_name + " WHERE id=?"); |
| 549 sql::Statement s(db_->GetUniqueStatement(query.c_str())); |
| 550 s.BindInt64(0, id); |
| 551 |
| 552 if (!s.Step()) { |
| 553 LOG_IF(WARNING, s.Succeeded()) << "No keyword with id: " << id |
| 554 << ", ignoring."; |
| 555 return true; |
| 556 } |
| 557 |
| 558 if (!s.Succeeded()) |
| 559 return false; |
| 560 |
| 561 *result = s.ColumnString(0); |
| 562 return true; |
| 563 } |
| 564 |
| 565 bool KeywordTable::MigrateKeywordsTableForVersion45(const std::string& name) { |
| 566 // Create a new table without the columns we're dropping. |
| 567 if (!db_->Execute("CREATE TABLE keywords_temp (" |
| 568 "id INTEGER PRIMARY KEY," |
| 569 "short_name VARCHAR NOT NULL," |
| 570 "keyword VARCHAR NOT NULL," |
| 571 "favicon_url VARCHAR NOT NULL," |
| 572 "url VARCHAR NOT NULL," |
| 573 "safe_for_autoreplace INTEGER," |
| 574 "originating_url VARCHAR," |
| 575 "date_created INTEGER DEFAULT 0," |
| 576 "usage_count INTEGER DEFAULT 0," |
| 577 "input_encodings VARCHAR," |
| 578 "show_in_default_list INTEGER," |
| 579 "suggest_url VARCHAR," |
| 580 "prepopulate_id INTEGER DEFAULT 0," |
| 581 "created_by_policy INTEGER DEFAULT 0," |
| 582 "instant_url VARCHAR," |
| 583 "last_modified INTEGER DEFAULT 0," |
| 584 "sync_guid VARCHAR)")) |
| 585 return false; |
| 586 std::string sql("INSERT INTO keywords_temp SELECT " + |
| 587 ColumnsForVersion(46, false) + " FROM " + name); |
| 588 if (!db_->Execute(sql.c_str())) |
| 589 return false; |
| 590 |
| 591 // NOTE: The ORDER BY here ensures that the uniquing process for keywords will |
| 592 // happen identically on both the normal and backup tables. |
| 593 sql = "SELECT id, keyword, url, autogenerate_keyword FROM " + name + |
| 594 " ORDER BY id ASC"; |
| 595 sql::Statement s(db_->GetUniqueStatement(sql.c_str())); |
| 596 base::string16 placeholder_keyword(base::ASCIIToUTF16("dummy")); |
| 597 std::set<base::string16> keywords; |
| 598 while (s.Step()) { |
| 599 base::string16 keyword(s.ColumnString16(1)); |
| 600 bool generate_keyword = keyword.empty() || s.ColumnBool(3); |
| 601 if (generate_keyword) |
| 602 keyword = placeholder_keyword; |
| 603 TemplateURLData data; |
| 604 data.SetKeyword(keyword); |
| 605 data.SetURL(s.ColumnString(2)); |
| 606 TemplateURL turl(data); |
| 607 // Don't persist extension keywords to disk. These will get added to the |
| 608 // TemplateURLService as the extensions are loaded. |
| 609 bool delete_entry = turl.GetType() == TemplateURL::OMNIBOX_API_EXTENSION; |
| 610 if (!delete_entry && generate_keyword) { |
| 611 // Explicitly generate keywords for all rows with the autogenerate bit set |
| 612 // or where the keyword is empty. |
| 613 SearchTermsData terms_data; |
| 614 GURL url(turl.GenerateSearchURL(terms_data)); |
| 615 if (!url.is_valid()) { |
| 616 delete_entry = true; |
| 617 } else { |
| 618 // Ensure autogenerated keywords are unique. |
| 619 keyword = TemplateURL::GenerateKeyword(url); |
| 620 while (keywords.count(keyword)) |
| 621 keyword.append(base::ASCIIToUTF16("_")); |
| 622 sql::Statement u(db_->GetUniqueStatement( |
| 623 "UPDATE keywords_temp SET keyword=? WHERE id=?")); |
| 624 u.BindString16(0, keyword); |
| 625 u.BindInt64(1, s.ColumnInt64(0)); |
| 626 if (!u.Run()) |
| 627 return false; |
| 628 } |
| 629 } |
| 630 if (delete_entry) { |
| 631 sql::Statement u(db_->GetUniqueStatement( |
| 632 "DELETE FROM keywords_temp WHERE id=?")); |
| 633 u.BindInt64(0, s.ColumnInt64(0)); |
| 634 if (!u.Run()) |
| 635 return false; |
| 636 } else { |
| 637 keywords.insert(keyword); |
| 638 } |
| 639 } |
| 640 |
| 641 // Replace the old table with the new one. |
| 642 sql = "DROP TABLE " + name; |
| 643 if (!db_->Execute(sql.c_str())) |
| 644 return false; |
| 645 sql = "ALTER TABLE keywords_temp RENAME TO " + name; |
| 646 return db_->Execute(sql.c_str()); |
| 647 } |
| OLD | NEW |