Home | History | Annotate | Download | only in webdata
      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