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.
5 #include "components/search_engines/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 "components/history/core/browser/url_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"
29 const char KeywordTable::kDefaultSearchProviderKey
[] =
30 "Default Search Provider ID";
34 // Keys used in the meta table.
35 const char kBuiltinKeywordVersion
[] = "Builtin Keyword Version";
37 const std::string
ColumnsForVersion(int version
, bool concatenated
) {
38 std::vector
<std::string
> columns
;
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");
54 // Columns removed after version 44.
55 columns
.push_back("autogenerate_keyword");
56 columns
.push_back("logo_id");
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");
63 // Column added in version 47.
64 columns
.push_back("alternate_urls");
67 // Column added in version 49.
68 columns
.push_back("search_terms_replacement_key");
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");
79 // Column added in version 53.
80 columns
.push_back("new_tab_url");
83 return JoinString(columns
, std::string(concatenated
? " || " : ", "));
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
,
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
);
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::URLDatabase::GURLToDatabaseURL(data
.favicon_url
) :
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::URLDatabase::GURLToDatabaseURL(data
.originating_url
) :
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
);
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
);
145 KeywordTable::KeywordTable() {
148 KeywordTable::~KeywordTable() {}
150 KeywordTable
* KeywordTable::FromWebDatabase(WebDatabase
* db
) {
151 return static_cast<KeywordTable
*>(db
->GetTable(GetKey()));
154 WebDatabaseTable::TypeKey
KeywordTable::GetTypeKey() const {
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,"
178 "alternate_urls VARCHAR,"
179 "search_terms_replacement_key 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)");
188 bool KeywordTable::IsSyncable() {
192 bool KeywordTable::MigrateToVersion(int version
,
193 bool* update_compatible_version
) {
194 // Migrate if necessary.
197 *update_compatible_version
= true;
198 return MigrateToVersion21AutoGenerateKeywordColumn();
200 *update_compatible_version
= true;
201 return MigrateToVersion25AddLogoIDColumn();
203 *update_compatible_version
= true;
204 return MigrateToVersion26AddCreatedByPolicyColumn();
206 *update_compatible_version
= true;
207 return MigrateToVersion28SupportsInstantColumn();
209 *update_compatible_version
= true;
210 return MigrateToVersion29InstantURLToSupportsInstant();
212 *update_compatible_version
= true;
213 return MigrateToVersion38AddLastModifiedColumn();
215 *update_compatible_version
= true;
216 return MigrateToVersion39AddSyncGUIDColumn();
218 *update_compatible_version
= true;
219 return MigrateToVersion44AddDefaultSearchProviderBackup();
221 *update_compatible_version
= true;
222 return MigrateToVersion45RemoveLogoIDAndAutogenerateColumns();
224 *update_compatible_version
= true;
225 return MigrateToVersion47AddAlternateURLsColumn();
227 *update_compatible_version
= true;
228 return MigrateToVersion48RemoveKeywordsBackup();
230 *update_compatible_version
= true;
231 return MigrateToVersion49AddSearchTermsReplacementKeyColumn();
233 *update_compatible_version
= true;
234 return MigrateToVersion52AddImageSearchAndPOSTSupport();
236 *update_compatible_version
= true;
237 return MigrateToVersion53AddNewTabURLColumn();
239 *update_compatible_version
= true;
240 return MigrateToVersion59RemoveExtensionKeywords();
246 bool KeywordTable::PerformOperations(const Operations
& operations
) {
247 sql::Transaction
transaction(db_
);
248 if (!transaction
.Begin())
251 for (Operations::const_iterator
i(operations
.begin()); i
!= operations
.end();
255 if (!AddKeyword(i
->second
))
260 if (!RemoveKeyword(i
->second
.id
))
265 if (!UpdateKeyword(i
->second
))
271 return transaction
.Commit();
274 bool KeywordTable::GetKeywords(Keywords
* keywords
) {
275 std::string
query("SELECT " + GetKeywordColumns() +
276 " FROM keywords ORDER BY id ASC");
277 sql::Statement
s(db_
->GetUniqueStatement(query
.c_str()));
279 std::set
<TemplateURLID
> bad_entries
;
281 keywords
->push_back(TemplateURLData());
282 if (!GetKeywordDataFromStatement(s
, &keywords
->back())) {
283 bad_entries
.insert(s
.ColumnInt64(0));
284 keywords
->pop_back();
287 bool succeeded
= s
.Succeeded();
288 for (std::set
<TemplateURLID
>::const_iterator
i(bad_entries
.begin());
289 i
!= bad_entries
.end(); ++i
)
290 succeeded
&= RemoveKeyword(*i
);
294 bool KeywordTable::SetDefaultSearchProviderID(int64 id
) {
295 return meta_table_
->SetValue(kDefaultSearchProviderKey
, id
);
298 int64
KeywordTable::GetDefaultSearchProviderID() {
299 int64 value
= kInvalidTemplateURLID
;
300 meta_table_
->GetValue(kDefaultSearchProviderKey
, &value
);
304 bool KeywordTable::SetBuiltinKeywordVersion(int version
) {
305 return meta_table_
->SetValue(kBuiltinKeywordVersion
, version
);
308 int KeywordTable::GetBuiltinKeywordVersion() {
310 return meta_table_
->GetValue(kBuiltinKeywordVersion
, &version
) ? version
: 0;
314 std::string
KeywordTable::GetKeywordColumns() {
315 return ColumnsForVersion(WebDatabase::kCurrentVersionNumber
, false);
318 bool KeywordTable::MigrateToVersion21AutoGenerateKeywordColumn() {
319 return db_
->Execute("ALTER TABLE keywords ADD COLUMN autogenerate_keyword "
320 "INTEGER DEFAULT 0");
323 bool KeywordTable::MigrateToVersion25AddLogoIDColumn() {
325 "ALTER TABLE keywords ADD COLUMN logo_id INTEGER DEFAULT 0");
328 bool KeywordTable::MigrateToVersion26AddCreatedByPolicyColumn() {
329 return db_
->Execute("ALTER TABLE keywords ADD COLUMN created_by_policy "
330 "INTEGER DEFAULT 0");
333 bool KeywordTable::MigrateToVersion28SupportsInstantColumn() {
334 return db_
->Execute("ALTER TABLE keywords ADD COLUMN supports_instant "
335 "INTEGER DEFAULT 0");
338 bool KeywordTable::MigrateToVersion29InstantURLToSupportsInstant() {
339 sql::Transaction
transaction(db_
);
340 return transaction
.Begin() &&
341 db_
->Execute("ALTER TABLE keywords ADD COLUMN instant_url VARCHAR") &&
342 db_
->Execute("CREATE TABLE keywords_temp ("
343 "id INTEGER PRIMARY KEY,"
344 "short_name VARCHAR NOT NULL,"
345 "keyword VARCHAR NOT NULL,"
346 "favicon_url VARCHAR NOT NULL,"
347 "url VARCHAR NOT NULL,"
348 "safe_for_autoreplace INTEGER,"
349 "originating_url VARCHAR,"
350 "date_created INTEGER DEFAULT 0,"
351 "usage_count INTEGER DEFAULT 0,"
352 "input_encodings VARCHAR,"
353 "show_in_default_list INTEGER,"
354 "suggest_url VARCHAR,"
355 "prepopulate_id INTEGER DEFAULT 0,"
356 "autogenerate_keyword INTEGER DEFAULT 0,"
357 "logo_id INTEGER DEFAULT 0,"
358 "created_by_policy INTEGER DEFAULT 0,"
359 "instant_url VARCHAR)") &&
360 db_
->Execute("INSERT INTO keywords_temp SELECT id, short_name, keyword, "
361 "favicon_url, url, safe_for_autoreplace, originating_url, "
362 "date_created, usage_count, input_encodings, "
363 "show_in_default_list, suggest_url, prepopulate_id, "
364 "autogenerate_keyword, logo_id, created_by_policy, "
365 "instant_url FROM keywords") &&
366 db_
->Execute("DROP TABLE keywords") &&
367 db_
->Execute("ALTER TABLE keywords_temp RENAME TO keywords") &&
368 transaction
.Commit();
371 bool KeywordTable::MigrateToVersion38AddLastModifiedColumn() {
373 "ALTER TABLE keywords ADD COLUMN last_modified INTEGER DEFAULT 0");
376 bool KeywordTable::MigrateToVersion39AddSyncGUIDColumn() {
377 return db_
->Execute("ALTER TABLE keywords ADD COLUMN sync_guid VARCHAR");
380 bool KeywordTable::MigrateToVersion44AddDefaultSearchProviderBackup() {
381 std::string
query("CREATE TABLE keywords_backup AS SELECT " +
382 ColumnsForVersion(44, false) + " FROM keywords ORDER BY id ASC");
383 sql::Transaction
transaction(db_
);
384 return transaction
.Begin() &&
385 meta_table_
->SetValue("Default Search Provider ID Backup",
386 GetDefaultSearchProviderID()) &&
387 (!db_
->DoesTableExist("keywords_backup") ||
388 db_
->Execute("DROP TABLE keywords_backup")) &&
389 db_
->Execute(query
.c_str()) &&
390 transaction
.Commit();
393 bool KeywordTable::MigrateToVersion45RemoveLogoIDAndAutogenerateColumns() {
394 sql::Transaction
transaction(db_
);
395 if (!transaction
.Begin())
398 // The version 43 migration should have been written to do this, but since it
399 // wasn't, we'll do it now. Unfortunately a previous change deleted this for
400 // some users, so we can't be sure this will succeed (so don't bail on error).
401 meta_table_
->DeleteKey("Default Search Provider Backup");
403 return MigrateKeywordsTableForVersion45("keywords") &&
404 MigrateKeywordsTableForVersion45("keywords_backup") &&
405 meta_table_
->SetValue("Default Search Provider ID Backup Signature",
407 transaction
.Commit();
410 bool KeywordTable::MigrateToVersion47AddAlternateURLsColumn() {
411 sql::Transaction
transaction(db_
);
412 return transaction
.Begin() &&
413 db_
->Execute("ALTER TABLE keywords ADD COLUMN "
414 "alternate_urls VARCHAR DEFAULT ''") &&
415 db_
->Execute("ALTER TABLE keywords_backup ADD COLUMN "
416 "alternate_urls VARCHAR DEFAULT ''") &&
417 meta_table_
->SetValue("Default Search Provider ID Backup Signature",
419 transaction
.Commit();
422 bool KeywordTable::MigrateToVersion48RemoveKeywordsBackup() {
423 sql::Transaction
transaction(db_
);
424 return transaction
.Begin() &&
425 meta_table_
->DeleteKey("Default Search Provider ID Backup") &&
426 meta_table_
->DeleteKey("Default Search Provider ID Backup Signature") &&
427 db_
->Execute("DROP TABLE keywords_backup") &&
428 transaction
.Commit();
431 bool KeywordTable::MigrateToVersion49AddSearchTermsReplacementKeyColumn() {
432 return db_
->Execute("ALTER TABLE keywords ADD COLUMN "
433 "search_terms_replacement_key VARCHAR DEFAULT ''");
436 bool KeywordTable::MigrateToVersion52AddImageSearchAndPOSTSupport() {
437 sql::Transaction
transaction(db_
);
438 return transaction
.Begin() &&
439 db_
->Execute("ALTER TABLE keywords ADD COLUMN image_url "
440 "VARCHAR DEFAULT ''") &&
441 db_
->Execute("ALTER TABLE keywords ADD COLUMN search_url_post_params "
442 "VARCHAR DEFAULT ''") &&
443 db_
->Execute("ALTER TABLE keywords ADD COLUMN suggest_url_post_params "
444 "VARCHAR DEFAULT ''") &&
445 db_
->Execute("ALTER TABLE keywords ADD COLUMN instant_url_post_params "
446 "VARCHAR DEFAULT ''") &&
447 db_
->Execute("ALTER TABLE keywords ADD COLUMN image_url_post_params "
448 "VARCHAR DEFAULT ''") &&
449 transaction
.Commit();
452 bool KeywordTable::MigrateToVersion53AddNewTabURLColumn() {
453 return db_
->Execute("ALTER TABLE keywords ADD COLUMN new_tab_url "
454 "VARCHAR DEFAULT ''");
457 bool KeywordTable::MigrateToVersion59RemoveExtensionKeywords() {
458 return db_
->Execute("DELETE FROM keywords "
459 "WHERE url LIKE 'chrome-extension://%'");
463 bool KeywordTable::GetKeywordDataFromStatement(const sql::Statement
& s
,
464 TemplateURLData
* data
) {
467 data
->short_name
= s
.ColumnString16(1);
468 data
->SetKeyword(s
.ColumnString16(2));
469 // Due to past bugs, we might have persisted entries with empty URLs. Avoid
470 // reading these out. (GetKeywords() will delete these entries on return.)
471 // NOTE: This code should only be needed as long as we might be reading such
472 // potentially-old data and can be removed afterward.
473 if (s
.ColumnString(4).empty())
475 data
->SetURL(s
.ColumnString(4));
476 data
->suggestions_url
= s
.ColumnString(11);
477 data
->instant_url
= s
.ColumnString(14);
478 data
->image_url
= s
.ColumnString(19);
479 data
->new_tab_url
= s
.ColumnString(24);
480 data
->search_url_post_params
= s
.ColumnString(20);
481 data
->suggestions_url_post_params
= s
.ColumnString(21);
482 data
->instant_url_post_params
= s
.ColumnString(22);
483 data
->image_url_post_params
= s
.ColumnString(23);
484 data
->favicon_url
= GURL(s
.ColumnString(3));
485 data
->originating_url
= GURL(s
.ColumnString(6));
486 data
->show_in_default_list
= s
.ColumnBool(10);
487 data
->safe_for_autoreplace
= s
.ColumnBool(5);
488 base::SplitString(s
.ColumnString(9), ';', &data
->input_encodings
);
489 data
->id
= s
.ColumnInt64(0);
490 data
->date_created
= Time::FromTimeT(s
.ColumnInt64(7));
491 data
->last_modified
= Time::FromTimeT(s
.ColumnInt64(15));
492 data
->created_by_policy
= s
.ColumnBool(13);
493 data
->usage_count
= s
.ColumnInt(8);
494 data
->prepopulate_id
= s
.ColumnInt(12);
495 data
->sync_guid
= s
.ColumnString(16);
497 data
->alternate_urls
.clear();
498 base::JSONReader json_reader
;
499 scoped_ptr
<base::Value
> value(json_reader
.ReadToValue(s
.ColumnString(17)));
500 base::ListValue
* alternate_urls_value
;
501 if (value
.get() && value
->GetAsList(&alternate_urls_value
)) {
502 std::string alternate_url
;
503 for (size_t i
= 0; i
< alternate_urls_value
->GetSize(); ++i
) {
504 if (alternate_urls_value
->GetString(i
, &alternate_url
))
505 data
->alternate_urls
.push_back(alternate_url
);
509 data
->search_terms_replacement_key
= s
.ColumnString(18);
514 bool KeywordTable::AddKeyword(const TemplateURLData
& data
) {
516 std::string
query("INSERT INTO keywords (" + GetKeywordColumns() + ") "
517 "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
519 sql::Statement
s(db_
->GetCachedStatement(SQL_FROM_HERE
, query
.c_str()));
520 BindURLToStatement(data
, &s
, 0, 1);
525 bool KeywordTable::RemoveKeyword(TemplateURLID id
) {
527 sql::Statement
s(db_
->GetCachedStatement(
528 SQL_FROM_HERE
, "DELETE FROM keywords WHERE id = ?"));
534 bool KeywordTable::UpdateKeyword(const TemplateURLData
& data
) {
536 sql::Statement
s(db_
->GetCachedStatement(
538 "UPDATE keywords SET short_name=?, keyword=?, favicon_url=?, url=?, "
539 "safe_for_autoreplace=?, originating_url=?, date_created=?, "
540 "usage_count=?, input_encodings=?, show_in_default_list=?, "
541 "suggest_url=?, prepopulate_id=?, created_by_policy=?, instant_url=?, "
542 "last_modified=?, sync_guid=?, alternate_urls=?, "
543 "search_terms_replacement_key=?, image_url=?, search_url_post_params=?, "
544 "suggest_url_post_params=?, instant_url_post_params=?, "
545 "image_url_post_params=?, new_tab_url=? WHERE id=?"));
546 BindURLToStatement(data
, &s
, 24, 0); // "24" binds id() as the last item.
551 bool KeywordTable::GetKeywordAsString(TemplateURLID id
,
552 const std::string
& table_name
,
553 std::string
* result
) {
554 std::string
query("SELECT " +
555 ColumnsForVersion(WebDatabase::kCurrentVersionNumber
, true) +
556 " FROM " + table_name
+ " WHERE id=?");
557 sql::Statement
s(db_
->GetUniqueStatement(query
.c_str()));
561 LOG_IF(WARNING
, s
.Succeeded()) << "No keyword with id: " << id
569 *result
= s
.ColumnString(0);
573 bool KeywordTable::MigrateKeywordsTableForVersion45(const std::string
& name
) {
574 // Create a new table without the columns we're dropping.
575 if (!db_
->Execute("CREATE TABLE keywords_temp ("
576 "id INTEGER PRIMARY KEY,"
577 "short_name VARCHAR NOT NULL,"
578 "keyword VARCHAR NOT NULL,"
579 "favicon_url VARCHAR NOT NULL,"
580 "url VARCHAR NOT NULL,"
581 "safe_for_autoreplace INTEGER,"
582 "originating_url VARCHAR,"
583 "date_created INTEGER DEFAULT 0,"
584 "usage_count INTEGER DEFAULT 0,"
585 "input_encodings VARCHAR,"
586 "show_in_default_list INTEGER,"
587 "suggest_url VARCHAR,"
588 "prepopulate_id INTEGER DEFAULT 0,"
589 "created_by_policy INTEGER DEFAULT 0,"
590 "instant_url VARCHAR,"
591 "last_modified INTEGER DEFAULT 0,"
592 "sync_guid VARCHAR)"))
594 std::string
sql("INSERT INTO keywords_temp SELECT " +
595 ColumnsForVersion(46, false) + " FROM " + name
);
596 if (!db_
->Execute(sql
.c_str()))
599 // NOTE: The ORDER BY here ensures that the uniquing process for keywords will
600 // happen identically on both the normal and backup tables.
601 sql
= "SELECT id, keyword, url, autogenerate_keyword FROM " + name
+
603 sql::Statement
s(db_
->GetUniqueStatement(sql
.c_str()));
604 base::string16
placeholder_keyword(base::ASCIIToUTF16("dummy"));
605 std::set
<base::string16
> keywords
;
607 base::string16
keyword(s
.ColumnString16(1));
608 bool generate_keyword
= keyword
.empty() || s
.ColumnBool(3);
609 if (generate_keyword
)
610 keyword
= placeholder_keyword
;
611 TemplateURLData data
;
612 data
.SetKeyword(keyword
);
613 data
.SetURL(s
.ColumnString(2));
614 TemplateURL
turl(data
);
615 // Don't persist extension keywords to disk. These will get added to the
616 // TemplateURLService as the extensions are loaded.
617 bool delete_entry
= turl
.GetType() == TemplateURL::OMNIBOX_API_EXTENSION
;
618 if (!delete_entry
&& generate_keyword
) {
619 // Explicitly generate keywords for all rows with the autogenerate bit set
620 // or where the keyword is empty.
621 SearchTermsData terms_data
;
622 GURL
url(turl
.GenerateSearchURL(terms_data
));
623 if (!url
.is_valid()) {
626 // Ensure autogenerated keywords are unique.
627 keyword
= TemplateURL::GenerateKeyword(url
);
628 while (keywords
.count(keyword
))
629 keyword
.append(base::ASCIIToUTF16("_"));
630 sql::Statement
u(db_
->GetUniqueStatement(
631 "UPDATE keywords_temp SET keyword=? WHERE id=?"));
632 u
.BindString16(0, keyword
);
633 u
.BindInt64(1, s
.ColumnInt64(0));
639 sql::Statement
u(db_
->GetUniqueStatement(
640 "DELETE FROM keywords_temp WHERE id=?"));
641 u
.BindInt64(0, s
.ColumnInt64(0));
645 keywords
.insert(keyword
);
649 // Replace the old table with the new one.
650 sql
= "DROP TABLE " + name
;
651 if (!db_
->Execute(sql
.c_str()))
653 sql
= "ALTER TABLE keywords_temp RENAME TO " + name
;
654 return db_
->Execute(sql
.c_str());