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