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.
5 #include "chrome/browser/webdata/keyword_table.h"
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"
30 const char KeywordTable::kDefaultSearchProviderKey
[] =
31 "Default Search Provider ID";
35 // Keys used in the meta table.
36 const char kBuiltinKeywordVersion
[] = "Builtin Keyword Version";
38 const std::string
ColumnsForVersion(int version
, bool concatenated
) {
39 std::vector
<std::string
> columns
;
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");
55 // Columns removed after version 44.
56 columns
.push_back("autogenerate_keyword");
57 columns
.push_back("logo_id");
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");
64 // Column added in version 47.
65 columns
.push_back("alternate_urls");
68 // Column added in version 49.
69 columns
.push_back("search_terms_replacement_key");
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");
80 // Column added in version 53.
81 columns
.push_back("new_tab_url");
84 return JoinString(columns
, std::string(concatenated
? " || " : ", "));
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
,
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 base::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
);
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
) :
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
) :
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
);
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
);
146 KeywordTable::KeywordTable() {
149 KeywordTable::~KeywordTable() {}
151 KeywordTable
* KeywordTable::FromWebDatabase(WebDatabase
* db
) {
152 return static_cast<KeywordTable
*>(db
->GetTable(GetKey()));
155 WebDatabaseTable::TypeKey
KeywordTable::GetTypeKey() const {
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,"
180 "alternate_urls VARCHAR,"
181 "search_terms_replacement_key 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)");
190 bool KeywordTable::IsSyncable() {
194 bool KeywordTable::MigrateToVersion(int version
,
195 bool* update_compatible_version
) {
196 // Migrate if necessary.
199 *update_compatible_version
= true;
200 return MigrateToVersion21AutoGenerateKeywordColumn();
202 *update_compatible_version
= true;
203 return MigrateToVersion25AddLogoIDColumn();
205 *update_compatible_version
= true;
206 return MigrateToVersion26AddCreatedByPolicyColumn();
208 *update_compatible_version
= true;
209 return MigrateToVersion28SupportsInstantColumn();
211 *update_compatible_version
= true;
212 return MigrateToVersion29InstantURLToSupportsInstant();
214 *update_compatible_version
= true;
215 return MigrateToVersion38AddLastModifiedColumn();
217 *update_compatible_version
= true;
218 return MigrateToVersion39AddSyncGUIDColumn();
220 *update_compatible_version
= true;
221 return MigrateToVersion44AddDefaultSearchProviderBackup();
223 *update_compatible_version
= true;
224 return MigrateToVersion45RemoveLogoIDAndAutogenerateColumns();
226 *update_compatible_version
= true;
227 return MigrateToVersion47AddAlternateURLsColumn();
229 *update_compatible_version
= true;
230 return MigrateToVersion48RemoveKeywordsBackup();
232 *update_compatible_version
= true;
233 return MigrateToVersion49AddSearchTermsReplacementKeyColumn();
235 *update_compatible_version
= true;
236 return MigrateToVersion52AddImageSearchAndPOSTSupport();
238 *update_compatible_version
= true;
239 return MigrateToVersion53AddNewTabURLColumn();
245 bool KeywordTable::AddKeyword(const TemplateURLData
& data
) {
247 std::string
query("INSERT INTO keywords (" + GetKeywordColumns() + ") "
248 "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
250 sql::Statement
s(db_
->GetUniqueStatement(query
.c_str()));
251 BindURLToStatement(data
, &s
, 0, 1);
256 bool KeywordTable::RemoveKeyword(TemplateURLID id
) {
259 db_
->GetUniqueStatement("DELETE FROM keywords WHERE id = ?"));
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()));
270 std::set
<TemplateURLID
> bad_entries
;
272 keywords
->push_back(TemplateURLData());
273 if (!GetKeywordDataFromStatement(s
, &keywords
->back())) {
274 bad_entries
.insert(s
.ColumnInt64(0));
275 keywords
->pop_back();
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
);
285 bool KeywordTable::UpdateKeyword(const TemplateURLData
& data
) {
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=? "
296 BindURLToStatement(data
, &s
, 24, 0); // "24" binds id() as the last item.
301 bool KeywordTable::SetDefaultSearchProviderID(int64 id
) {
302 return meta_table_
->SetValue(kDefaultSearchProviderKey
, id
);
305 int64
KeywordTable::GetDefaultSearchProviderID() {
306 int64 value
= kInvalidTemplateURLID
;
307 meta_table_
->GetValue(kDefaultSearchProviderKey
, &value
);
311 bool KeywordTable::SetBuiltinKeywordVersion(int version
) {
312 return meta_table_
->SetValue(kBuiltinKeywordVersion
, version
);
315 int KeywordTable::GetBuiltinKeywordVersion() {
317 return meta_table_
->GetValue(kBuiltinKeywordVersion
, &version
) ? version
: 0;
321 std::string
KeywordTable::GetKeywordColumns() {
322 return ColumnsForVersion(WebDatabase::kCurrentVersionNumber
, false);
325 bool KeywordTable::MigrateToVersion21AutoGenerateKeywordColumn() {
326 return db_
->Execute("ALTER TABLE keywords ADD COLUMN autogenerate_keyword "
327 "INTEGER DEFAULT 0");
330 bool KeywordTable::MigrateToVersion25AddLogoIDColumn() {
332 "ALTER TABLE keywords ADD COLUMN logo_id INTEGER DEFAULT 0");
335 bool KeywordTable::MigrateToVersion26AddCreatedByPolicyColumn() {
336 return db_
->Execute("ALTER TABLE keywords ADD COLUMN created_by_policy "
337 "INTEGER DEFAULT 0");
340 bool KeywordTable::MigrateToVersion28SupportsInstantColumn() {
341 return db_
->Execute("ALTER TABLE keywords ADD COLUMN supports_instant "
342 "INTEGER DEFAULT 0");
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();
378 bool KeywordTable::MigrateToVersion38AddLastModifiedColumn() {
380 "ALTER TABLE keywords ADD COLUMN last_modified INTEGER DEFAULT 0");
383 bool KeywordTable::MigrateToVersion39AddSyncGUIDColumn() {
384 return db_
->Execute("ALTER TABLE keywords ADD COLUMN sync_guid VARCHAR");
387 bool KeywordTable::MigrateToVersion44AddDefaultSearchProviderBackup() {
388 sql::Transaction
transaction(db_
);
389 if (!transaction
.Begin())
392 int64 default_search_id
= GetDefaultSearchProviderID();
393 if (!meta_table_
->SetValue("Default Search Provider ID Backup",
397 // Backup of all keywords.
398 if (db_
->DoesTableExist("keywords_backup") &&
399 !db_
->Execute("DROP TABLE keywords_backup"))
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()))
407 return transaction
.Commit();
410 bool KeywordTable::MigrateToVersion45RemoveLogoIDAndAutogenerateColumns() {
411 sql::Transaction
transaction(db_
);
412 if (!transaction
.Begin())
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");
420 if (!MigrateKeywordsTableForVersion45("keywords"))
423 // Migrate the keywords backup table as well.
424 if (!MigrateKeywordsTableForVersion45("keywords_backup") ||
425 !meta_table_
->SetValue("Default Search Provider ID Backup Signature",
429 return transaction
.Commit();
432 bool KeywordTable::MigrateToVersion47AddAlternateURLsColumn() {
433 sql::Transaction
transaction(db_
);
435 // Fill the |alternate_urls| column with empty strings, otherwise it breaks
436 // code relying on GetTableContents that concatenates the strings from all
438 if (!transaction
.Begin() ||
439 !db_
->Execute("ALTER TABLE keywords ADD COLUMN "
440 "alternate_urls VARCHAR DEFAULT ''"))
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",
450 return transaction
.Commit();
453 bool KeywordTable::MigrateToVersion48RemoveKeywordsBackup() {
454 sql::Transaction
transaction(db_
);
455 if (!transaction
.Begin())
458 if (!meta_table_
->DeleteKey("Default Search Provider ID Backup") ||
459 !meta_table_
->DeleteKey("Default Search Provider ID Backup Signature"))
462 if (!db_
->Execute("DROP TABLE keywords_backup"))
465 return transaction
.Commit();
468 bool KeywordTable::MigrateToVersion49AddSearchTermsReplacementKeyColumn() {
469 sql::Transaction
transaction(db_
);
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 ''"))
478 return transaction
.Commit();
481 bool KeywordTable::MigrateToVersion52AddImageSearchAndPOSTSupport() {
482 sql::Transaction
transaction(db_
);
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();
499 bool KeywordTable::MigrateToVersion53AddNewTabURLColumn() {
500 sql::Transaction
transaction(db_
);
502 return transaction
.Begin() &&
503 db_
->Execute("ALTER TABLE keywords ADD COLUMN new_tab_url "
504 "VARCHAR DEFAULT ''") &&
505 transaction
.Commit();
509 bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement
& s
,
510 TemplateURLData
* data
) {
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())
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);
543 data
->alternate_urls
.clear();
544 base::JSONReader json_reader
;
545 scoped_ptr
<base::Value
> value(json_reader
.ReadToValue(s
.ColumnString(17)));
546 base::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
);
555 data
->search_terms_replacement_key
= s
.ColumnString(18);
560 bool KeywordTable::GetTableContents(const char* table_name
,
562 std::string
* contents
) {
565 if (!db_
->DoesTableExist(table_name
))
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()));
575 *contents
+= s
.ColumnString(0);
576 return s
.Succeeded();
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()));
589 LOG_IF(WARNING
, s
.Succeeded()) << "No keyword with id: " << id
597 *result
= s
.ColumnString(0);
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)"))
622 std::string
sql("INSERT INTO keywords_temp SELECT " +
623 ColumnsForVersion(46, false) + " FROM " + name
);
624 if (!db_
->Execute(sql
.c_str()))
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
+
631 sql::Statement
s(db_
->GetUniqueStatement(sql
.c_str()));
632 base::string16
placeholder_keyword(base::ASCIIToUTF16("dummy"));
633 std::set
<base::string16
> keywords
;
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
,
652 if (!url
.is_valid()) {
655 // Ensure autogenerated keywords are unique.
656 keyword
= TemplateURLService::GenerateKeyword(url
);
657 while (keywords
.count(keyword
))
658 keyword
.append(base::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));
668 sql::Statement
u(db_
->GetUniqueStatement(
669 "DELETE FROM keywords_temp WHERE id=?"));
670 u
.BindInt64(0, s
.ColumnInt64(0));
674 keywords
.insert(keyword
);
678 // Replace the old table with the new one.
679 sql
= "DROP TABLE " + name
;
680 if (!db_
->Execute(sql
.c_str()))
682 sql
= "ALTER TABLE keywords_temp RENAME TO " + name
;
683 return db_
->Execute(sql
.c_str());