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/history/thumbnail_database.h"
10 #include "base/bind.h"
11 #include "base/debug/alias.h"
12 #include "base/debug/dump_without_crashing.h"
13 #include "base/files/file_util.h"
14 #include "base/format_macros.h"
15 #include "base/memory/ref_counted_memory.h"
16 #include "base/metrics/histogram.h"
17 #include "base/rand_util.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/time/time.h"
21 #include "components/history/core/browser/history_client.h"
22 #include "components/history/core/browser/url_database.h"
23 #include "sql/recovery.h"
24 #include "sql/statement.h"
25 #include "sql/transaction.h"
26 #include "third_party/sqlite/sqlite3.h"
28 #if defined(OS_MACOSX)
29 #include "base/mac/mac_util.h"
32 // Description of database tables:
36 // page_url Page URL which has one or more associated favicons.
37 // icon_id The ID of favicon that this mapping maps to.
39 // favicons This table associates a row to each favicon for a
40 // |page_url| in the |icon_mapping| table. This is the
41 // default favicon |page_url|/favicon.ico plus any favicons
42 // associated via <link rel="icon_type" href="url">.
43 // The |id| matches the |icon_id| field in the appropriate
44 // row in the icon_mapping table.
47 // url The URL at which the favicon file is located.
48 // icon_type The type of the favicon specified in the rel attribute of
49 // the link tag. The FAVICON type is used for the default
50 // favicon.ico favicon.
52 // favicon_bitmaps This table contains the PNG encoded bitmap data of the
53 // favicons. There is a separate row for every size in a
54 // multi resolution bitmap. The bitmap data is associated
55 // to the favicon via the |icon_id| field which matches
56 // the |id| field in the appropriate row in the |favicons|
60 // icon_id The ID of the favicon that the bitmap is associated to.
61 // last_updated The time at which this favicon was inserted into the
62 // table. This is used to determine if it needs to be
63 // redownloaded from the web.
64 // image_data PNG encoded data of the favicon.
65 // width Pixel width of |image_data|.
66 // height Pixel height of |image_data|.
70 // For this database, schema migrations are deprecated after two
71 // years. This means that the oldest non-deprecated version should be
72 // two years old or greater (thus the migrations to get there are
73 // older). Databases containing deprecated versions will be cleared
74 // at startup. Since this database is a cache, losing old data is not
75 // fatal (in fact, very old data may be expired immediately at startup
78 // Version 7: 911a634d/r209424 by qsr@chromium.org on 2013-07-01
79 // Version 6: 610f923b/r152367 by pkotwicz@chromium.org on 2012-08-20
80 // Version 5: e2ee8ae9/r105004 by groby@chromium.org on 2011-10-12
81 // Version 4: 5f104d76/r77288 by sky@chromium.org on 2011-03-08 (deprecated)
82 // Version 3: 09911bf3/r15 by initial.commit on 2008-07-26 (deprecated)
84 // Version number of the database.
85 // NOTE(shess): When changing the version, add a new golden file for
86 // the new version and a test to verify that Init() works with it.
87 const int kCurrentVersionNumber = 7;
88 const int kCompatibleVersionNumber = 7;
89 const int kDeprecatedVersionNumber = 4; // and earlier.
91 void FillIconMapping(const sql::Statement& statement,
93 history::IconMapping* icon_mapping) {
94 icon_mapping->mapping_id = statement.ColumnInt64(0);
95 icon_mapping->icon_id = statement.ColumnInt64(1);
96 icon_mapping->icon_type =
97 static_cast<favicon_base::IconType>(statement.ColumnInt(2));
98 icon_mapping->icon_url = GURL(statement.ColumnString(3));
99 icon_mapping->page_url = page_url;
102 enum InvalidStructureType {
103 // NOTE(shess): Intentionally skip bucket 0 to account for
104 // conversion from a boolean histogram.
105 STRUCTURE_EVENT_FAVICON = 1,
106 STRUCTURE_EVENT_VERSION4,
107 STRUCTURE_EVENT_VERSION5,
109 // Always keep this at the end.
113 void RecordInvalidStructure(InvalidStructureType invalid_type) {
114 UMA_HISTOGRAM_ENUMERATION("History.InvalidFaviconsDBStructure",
115 invalid_type, STRUCTURE_EVENT_MAX);
118 // Attempt to pass 2000 bytes of |debug_info| into a crash dump.
119 void DumpWithoutCrashing2000(const std::string& debug_info) {
120 char debug_buf[2000];
121 base::strlcpy(debug_buf, debug_info.c_str(), arraysize(debug_buf));
122 base::debug::Alias(&debug_buf);
124 base::debug::DumpWithoutCrashing();
127 void ReportCorrupt(sql::Connection* db, size_t startup_kb) {
128 // Buffer for accumulating debugging info about the error. Place
129 // more-relevant information earlier, in case things overflow the
130 // fixed-size buffer.
131 std::string debug_info;
133 base::StringAppendF(&debug_info, "SQLITE_CORRUPT, integrity_check:\n");
135 // Check files up to 8M to keep things from blocking too long.
136 const size_t kMaxIntegrityCheckSize = 8192;
137 if (startup_kb > kMaxIntegrityCheckSize) {
138 base::StringAppendF(&debug_info, "too big %" PRIuS "\n", startup_kb);
140 std::vector<std::string> messages;
142 const base::TimeTicks before = base::TimeTicks::Now();
143 db->FullIntegrityCheck(&messages);
144 base::StringAppendF(&debug_info, "# %" PRIx64 " ms, %" PRIuS " records\n",
145 (base::TimeTicks::Now() - before).InMilliseconds(),
148 // SQLite returns up to 100 messages by default, trim deeper to
149 // keep close to the 2000-character size limit for dumping.
151 // TODO(shess): If the first 20 tend to be actionable, test if
152 // passing the count to integrity_check makes it exit earlier. In
153 // that case it may be possible to greatly ease the size
155 const size_t kMaxMessages = 20;
156 for (size_t i = 0; i < kMaxMessages && i < messages.size(); ++i) {
157 base::StringAppendF(&debug_info, "%s\n", messages[i].c_str());
161 DumpWithoutCrashing2000(debug_info);
164 void ReportError(sql::Connection* db, int error) {
165 // Buffer for accumulating debugging info about the error. Place
166 // more-relevant information earlier, in case things overflow the
167 // fixed-size buffer.
168 std::string debug_info;
170 // The error message from the failed operation.
171 base::StringAppendF(&debug_info, "db error: %d/%s\n",
172 db->GetErrorCode(), db->GetErrorMessage());
174 // System errno information.
175 base::StringAppendF(&debug_info, "errno: %d\n", db->GetLastErrno());
177 // SQLITE_ERROR reports seem to be attempts to upgrade invalid
178 // schema, try to log that info.
179 if (error == SQLITE_ERROR) {
180 const char* kVersionSql = "SELECT value FROM meta WHERE key = 'version'";
181 if (db->IsSQLValid(kVersionSql)) {
182 sql::Statement statement(db->GetUniqueStatement(kVersionSql));
183 if (statement.Step()) {
184 debug_info += "version: ";
185 debug_info += statement.ColumnString(0);
187 } else if (statement.Succeeded()) {
188 debug_info += "version: none\n";
190 debug_info += "version: error\n";
193 debug_info += "version: invalid\n";
196 debug_info += "schema:\n";
198 // sqlite_master has columns:
199 // type - "index" or "table".
200 // name - name of created element.
201 // tbl_name - name of element, or target table in case of index.
202 // rootpage - root page of the element in database file.
203 // sql - SQL to create the element.
204 // In general, the |sql| column is sufficient to derive the other
205 // columns. |rootpage| is not interesting for debugging, without
206 // the contents of the database. The COALESCE is because certain
207 // automatic elements will have a |name| but no |sql|,
208 const char* kSchemaSql = "SELECT COALESCE(sql, name) FROM sqlite_master";
209 sql::Statement statement(db->GetUniqueStatement(kSchemaSql));
210 while (statement.Step()) {
211 debug_info += statement.ColumnString(0);
214 if (!statement.Succeeded())
215 debug_info += "error\n";
218 // TODO(shess): Think of other things to log. Not logging the
219 // statement text because the backtrace should suffice in most
220 // cases. The database schema is a possibility, but the
221 // likelihood of recursive error callbacks makes that risky (same
222 // reasoning applies to other data fetched from the database).
224 DumpWithoutCrashing2000(debug_info);
227 // TODO(shess): If this proves out, perhaps lift the code out to
228 // chrome/browser/diagnostics/sqlite_diagnostics.{h,cc}.
229 void GenerateDiagnostics(sql::Connection* db,
231 int extended_error) {
232 int error = (extended_error & 0xFF);
234 // Infrequently report information about the error up to the crash
236 static const uint64 kReportsPerMillion = 50000;
238 // Since some/most errors will not resolve themselves, only report
239 // once per Chrome run.
240 static bool reported = false;
244 uint64 rand = base::RandGenerator(1000000);
245 if (error == SQLITE_CORRUPT) {
246 // Once the database is known to be corrupt, it will generate a
247 // stream of errors until someone fixes it, so give one chance.
248 // Set first in case of errors in generating the report.
251 // Corrupt cases currently dominate, report them very infrequently.
252 static const uint64 kCorruptReportsPerMillion = 10000;
253 if (rand < kCorruptReportsPerMillion)
254 ReportCorrupt(db, startup_kb);
255 } else if (error == SQLITE_READONLY) {
256 // SQLITE_READONLY appears similar to SQLITE_CORRUPT - once it
257 // is seen, it is almost guaranteed to be seen again.
260 if (rand < kReportsPerMillion)
261 ReportError(db, extended_error);
263 // Only set the flag when making a report. This should allow
264 // later (potentially different) errors in a stream of errors to
267 // TODO(shess): Would it be worthwile to audit for which cases
268 // want once-only handling? Sqlite.Error.Thumbnail shows
269 // CORRUPT and READONLY as almost 95% of all reports on these
270 // channels, so probably easier to just harvest from the field.
271 if (rand < kReportsPerMillion) {
273 ReportError(db, extended_error);
278 // NOTE(shess): Schema modifications must consider initial creation in
279 // |InitImpl()|, recovery in |RecoverDatabaseOrRaze()|, and history pruning in
280 // |RetainDataForPageUrls()|.
281 bool InitTables(sql::Connection* db) {
282 const char kIconMappingSql[] =
283 "CREATE TABLE IF NOT EXISTS icon_mapping"
285 "id INTEGER PRIMARY KEY,"
286 "page_url LONGVARCHAR NOT NULL,"
289 if (!db->Execute(kIconMappingSql))
292 const char kFaviconsSql[] =
293 "CREATE TABLE IF NOT EXISTS favicons"
295 "id INTEGER PRIMARY KEY,"
296 "url LONGVARCHAR NOT NULL,"
297 // default icon_type FAVICON to be consistent with past migration.
298 "icon_type INTEGER DEFAULT 1"
300 if (!db->Execute(kFaviconsSql))
303 const char kFaviconBitmapsSql[] =
304 "CREATE TABLE IF NOT EXISTS favicon_bitmaps"
306 "id INTEGER PRIMARY KEY,"
307 "icon_id INTEGER NOT NULL,"
308 "last_updated INTEGER DEFAULT 0,"
310 "width INTEGER DEFAULT 0,"
311 "height INTEGER DEFAULT 0"
313 if (!db->Execute(kFaviconBitmapsSql))
319 // NOTE(shess): Schema modifications must consider initial creation in
320 // |InitImpl()|, recovery in |RecoverDatabaseOrRaze()|, and history pruning in
321 // |RetainDataForPageUrls()|.
322 bool InitIndices(sql::Connection* db) {
323 const char kIconMappingUrlIndexSql[] =
324 "CREATE INDEX IF NOT EXISTS icon_mapping_page_url_idx"
325 " ON icon_mapping(page_url)";
326 const char kIconMappingIdIndexSql[] =
327 "CREATE INDEX IF NOT EXISTS icon_mapping_icon_id_idx"
328 " ON icon_mapping(icon_id)";
329 if (!db->Execute(kIconMappingUrlIndexSql) ||
330 !db->Execute(kIconMappingIdIndexSql)) {
334 const char kFaviconsIndexSql[] =
335 "CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)";
336 if (!db->Execute(kFaviconsIndexSql))
339 const char kFaviconBitmapsIndexSql[] =
340 "CREATE INDEX IF NOT EXISTS favicon_bitmaps_icon_id ON "
341 "favicon_bitmaps(icon_id)";
342 if (!db->Execute(kFaviconBitmapsIndexSql))
348 enum RecoveryEventType {
349 RECOVERY_EVENT_RECOVERED = 0,
350 RECOVERY_EVENT_FAILED_SCOPER,
351 RECOVERY_EVENT_FAILED_META_VERSION_ERROR, // obsolete
352 RECOVERY_EVENT_FAILED_META_VERSION_NONE, // obsolete
353 RECOVERY_EVENT_FAILED_META_WRONG_VERSION6, // obsolete
354 RECOVERY_EVENT_FAILED_META_WRONG_VERSION5, // obsolete
355 RECOVERY_EVENT_FAILED_META_WRONG_VERSION,
356 RECOVERY_EVENT_FAILED_RECOVER_META, // obsolete
357 RECOVERY_EVENT_FAILED_META_INSERT, // obsolete
358 RECOVERY_EVENT_FAILED_INIT,
359 RECOVERY_EVENT_FAILED_RECOVER_FAVICONS, // obsolete
360 RECOVERY_EVENT_FAILED_FAVICONS_INSERT, // obsolete
361 RECOVERY_EVENT_FAILED_RECOVER_FAVICON_BITMAPS, // obsolete
362 RECOVERY_EVENT_FAILED_FAVICON_BITMAPS_INSERT, // obsolete
363 RECOVERY_EVENT_FAILED_RECOVER_ICON_MAPPING, // obsolete
364 RECOVERY_EVENT_FAILED_ICON_MAPPING_INSERT, // obsolete
365 RECOVERY_EVENT_RECOVERED_VERSION6, // obsolete
366 RECOVERY_EVENT_FAILED_META_INIT,
367 RECOVERY_EVENT_FAILED_META_VERSION,
368 RECOVERY_EVENT_DEPRECATED,
369 RECOVERY_EVENT_FAILED_V5_INITSCHEMA, // obsolete
370 RECOVERY_EVENT_FAILED_V5_AUTORECOVER_FAVICONS, // obsolete
371 RECOVERY_EVENT_FAILED_V5_AUTORECOVER_ICON_MAPPING, // obsolete
372 RECOVERY_EVENT_RECOVERED_VERSION5, // obsolete
373 RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICONS,
374 RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICON_BITMAPS,
375 RECOVERY_EVENT_FAILED_AUTORECOVER_ICON_MAPPING,
376 RECOVERY_EVENT_FAILED_COMMIT,
378 // Always keep this at the end.
382 void RecordRecoveryEvent(RecoveryEventType recovery_event) {
383 UMA_HISTOGRAM_ENUMERATION("History.FaviconsRecovery",
384 recovery_event, RECOVERY_EVENT_MAX);
387 // Recover the database to the extent possible, razing it if recovery
389 // TODO(shess): This is mostly just a safe proof of concept. In the
390 // real world, this database is probably not worthwhile recovering, as
391 // opposed to just razing it and starting over whenever corruption is
392 // detected. So this database is a good test subject.
393 void RecoverDatabaseOrRaze(sql::Connection* db, const base::FilePath& db_path) {
394 // NOTE(shess): This code is currently specific to the version
395 // number. I am working on simplifying things to loosen the
396 // dependency, meanwhile contact me if you need to bump the version.
397 DCHECK_EQ(7, kCurrentVersionNumber);
399 // TODO(shess): Reset back after?
400 db->reset_error_callback();
402 // For histogram purposes.
403 size_t favicons_rows_recovered = 0;
404 size_t favicon_bitmaps_rows_recovered = 0;
405 size_t icon_mapping_rows_recovered = 0;
406 int64 original_size = 0;
407 base::GetFileSize(db_path, &original_size);
409 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path);
411 // TODO(shess): Unable to create recovery connection. This
412 // implies something substantial is wrong. At this point |db| has
413 // been poisoned so there is nothing really to do.
415 // Possible responses are unclear. If the failure relates to a
416 // problem somehow specific to the temporary file used to back the
417 // database, then an in-memory database could possibly be used.
418 // This could potentially allow recovering the main database, and
419 // might be simple to implement w/in Begin().
420 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_SCOPER);
424 // Setup the meta recovery table and fetch the version number from
425 // the corrupt database.
427 if (!recovery->SetupMeta() || !recovery->GetMetaVersionNumber(&version)) {
428 // TODO(shess): Prior histograms indicate all failures are in
429 // creating the recover virtual table for corrupt.meta. The table
430 // may not exist, or the database may be too far gone. Either
431 // way, unclear how to resolve.
432 sql::Recovery::Rollback(recovery.Pass());
433 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_VERSION);
437 // This code may be able to fetch version information that the regular
438 // deprecation path cannot.
439 // NOTE(shess): v5 and v6 are currently not deprecated in the normal Init()
440 // path, but are deprecated in the recovery path in the interest of keeping
441 // the code simple. http://crbug.com/327485 for numbers.
442 DCHECK_LE(kDeprecatedVersionNumber, 6);
444 sql::Recovery::Unrecoverable(recovery.Pass());
445 RecordRecoveryEvent(RECOVERY_EVENT_DEPRECATED);
449 // Earlier versions have been handled or deprecated, later versions should be
452 sql::Recovery::Unrecoverable(recovery.Pass());
453 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_WRONG_VERSION);
457 // Recover to current schema version.
458 sql::MetaTable recover_meta_table;
459 if (!recover_meta_table.Init(recovery->db(), kCurrentVersionNumber,
460 kCompatibleVersionNumber)) {
461 sql::Recovery::Rollback(recovery.Pass());
462 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_INIT);
466 // Create a fresh version of the database. The recovery code uses
467 // conflict-resolution to handle duplicates, so the indices are
469 if (!InitTables(recovery->db()) || !InitIndices(recovery->db())) {
470 // TODO(shess): Unable to create the new schema in the new
471 // database. The new database should be a temporary file, so
472 // being unable to work with it is pretty unclear.
474 // What are the potential responses, even? The recovery database
475 // could be opened as in-memory. If the temp database had a
476 // filesystem problem and the temp filesystem differs from the
477 // main database, then that could fix it.
478 sql::Recovery::Rollback(recovery.Pass());
479 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_INIT);
483 if (!recovery->AutoRecoverTable("favicons", 0, &favicons_rows_recovered)) {
484 sql::Recovery::Rollback(recovery.Pass());
485 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICONS);
488 if (!recovery->AutoRecoverTable("favicon_bitmaps", 0,
489 &favicon_bitmaps_rows_recovered)) {
490 sql::Recovery::Rollback(recovery.Pass());
491 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICON_BITMAPS);
494 if (!recovery->AutoRecoverTable("icon_mapping", 0,
495 &icon_mapping_rows_recovered)) {
496 sql::Recovery::Rollback(recovery.Pass());
497 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_ICON_MAPPING);
501 // TODO(shess): Is it possible/likely to have broken foreign-key
502 // issues with the tables?
503 // - icon_mapping.icon_id maps to no favicons.id
504 // - favicon_bitmaps.icon_id maps to no favicons.id
505 // - favicons.id is referenced by no icon_mapping.icon_id
506 // - favicons.id is referenced by no favicon_bitmaps.icon_id
507 // This step is possibly not worth the effort necessary to develop
508 // and sequence the statements, as it is basically a form of garbage
511 if (!sql::Recovery::Recovered(recovery.Pass())) {
512 RecordRecoveryEvent(RECOVERY_EVENT_FAILED_COMMIT);
516 // Track the size of the recovered database relative to the size of
517 // the input database. The size should almost always be smaller,
518 // unless the input database was empty to start with. If the
519 // percentage results are very low, something is awry.
520 int64 final_size = 0;
521 if (original_size > 0 &&
522 base::GetFileSize(db_path, &final_size) &&
524 int percentage = static_cast<int>(original_size * 100 / final_size);
525 UMA_HISTOGRAM_PERCENTAGE("History.FaviconsRecoveredPercentage",
526 std::max(100, percentage));
529 // Using 10,000 because these cases mostly care about "none
530 // recovered" and "lots recovered". More than 10,000 rows recovered
531 // probably means there's something wrong with the profile.
532 UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsFavicons",
533 favicons_rows_recovered);
534 UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsFaviconBitmaps",
535 favicon_bitmaps_rows_recovered);
536 UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsIconMapping",
537 icon_mapping_rows_recovered);
539 RecordRecoveryEvent(RECOVERY_EVENT_RECOVERED);
542 void DatabaseErrorCallback(sql::Connection* db,
543 const base::FilePath& db_path,
545 history::HistoryClient* history_client,
547 sql::Statement* stmt) {
548 // TODO(shess): Assert that this is running on a safe thread.
549 // AFAICT, should be the history thread, but at this level I can't
550 // see how to reach that.
552 if (history_client && history_client->ShouldReportDatabaseError()) {
553 GenerateDiagnostics(db, startup_kb, extended_error);
556 // Attempt to recover corrupt databases.
557 int error = (extended_error & 0xFF);
558 if (error == SQLITE_CORRUPT ||
559 error == SQLITE_CANTOPEN ||
560 error == SQLITE_NOTADB) {
561 RecoverDatabaseOrRaze(db, db_path);
564 // The default handling is to assert on debug and to ignore on release.
565 if (!sql::Connection::ShouldIgnoreSqliteError(extended_error))
566 DLOG(FATAL) << db->GetErrorMessage();
573 ThumbnailDatabase::IconMappingEnumerator::IconMappingEnumerator() {
576 ThumbnailDatabase::IconMappingEnumerator::~IconMappingEnumerator() {
579 bool ThumbnailDatabase::IconMappingEnumerator::GetNextIconMapping(
580 IconMapping* icon_mapping) {
581 if (!statement_.Step())
583 FillIconMapping(statement_, GURL(statement_.ColumnString(4)), icon_mapping);
587 ThumbnailDatabase::ThumbnailDatabase(HistoryClient* history_client)
588 : history_client_(history_client) {
591 ThumbnailDatabase::~ThumbnailDatabase() {
592 // The DBCloseScoper will delete the DB and the cache.
595 sql::InitStatus ThumbnailDatabase::Init(const base::FilePath& db_name) {
596 // TODO(shess): Consider separating database open from schema setup.
597 // With that change, this code could Raze() from outside the
598 // transaction, rather than needing RazeAndClose() in InitImpl().
600 // Retry failed setup in case the recovery system fixed things.
601 const size_t kAttempts = 2;
603 sql::InitStatus status = sql::INIT_FAILURE;
604 for (size_t i = 0; i < kAttempts; ++i) {
605 status = InitImpl(db_name);
606 if (status == sql::INIT_OK)
615 void ThumbnailDatabase::ComputeDatabaseMetrics() {
616 sql::Statement favicon_count(
617 db_.GetCachedStatement(SQL_FROM_HERE, "SELECT COUNT(*) FROM favicons"));
618 UMA_HISTOGRAM_COUNTS_10000(
619 "History.NumFaviconsInDB",
620 favicon_count.Step() ? favicon_count.ColumnInt(0) : 0);
623 void ThumbnailDatabase::BeginTransaction() {
624 db_.BeginTransaction();
627 void ThumbnailDatabase::CommitTransaction() {
628 db_.CommitTransaction();
631 void ThumbnailDatabase::RollbackTransaction() {
632 db_.RollbackTransaction();
635 void ThumbnailDatabase::Vacuum() {
636 DCHECK(db_.transaction_nesting() == 0) <<
637 "Can not have a transaction when vacuuming.";
638 ignore_result(db_.Execute("VACUUM"));
641 void ThumbnailDatabase::TrimMemory(bool aggressively) {
642 db_.TrimMemory(aggressively);
645 bool ThumbnailDatabase::GetFaviconBitmapIDSizes(
646 favicon_base::FaviconID icon_id,
647 std::vector<FaviconBitmapIDSize>* bitmap_id_sizes) {
649 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
650 "SELECT id, width, height FROM favicon_bitmaps WHERE icon_id=?"));
651 statement.BindInt64(0, icon_id);
654 while (statement.Step()) {
656 if (!bitmap_id_sizes)
659 FaviconBitmapIDSize bitmap_id_size;
660 bitmap_id_size.bitmap_id = statement.ColumnInt64(0);
661 bitmap_id_size.pixel_size = gfx::Size(statement.ColumnInt(1),
662 statement.ColumnInt(2));
663 bitmap_id_sizes->push_back(bitmap_id_size);
668 bool ThumbnailDatabase::GetFaviconBitmaps(
669 favicon_base::FaviconID icon_id,
670 std::vector<FaviconBitmap>* favicon_bitmaps) {
672 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
673 "SELECT id, last_updated, image_data, width, height FROM favicon_bitmaps "
675 statement.BindInt64(0, icon_id);
678 while (statement.Step()) {
680 if (!favicon_bitmaps)
683 FaviconBitmap favicon_bitmap;
684 favicon_bitmap.bitmap_id = statement.ColumnInt64(0);
685 favicon_bitmap.icon_id = icon_id;
686 favicon_bitmap.last_updated =
687 base::Time::FromInternalValue(statement.ColumnInt64(1));
688 if (statement.ColumnByteLength(2) > 0) {
689 scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes());
690 statement.ColumnBlobAsVector(2, &data->data());
691 favicon_bitmap.bitmap_data = data;
693 favicon_bitmap.pixel_size = gfx::Size(statement.ColumnInt(3),
694 statement.ColumnInt(4));
695 favicon_bitmaps->push_back(favicon_bitmap);
700 bool ThumbnailDatabase::GetFaviconBitmap(
701 FaviconBitmapID bitmap_id,
702 base::Time* last_updated,
703 scoped_refptr<base::RefCountedMemory>* png_icon_data,
704 gfx::Size* pixel_size) {
706 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
707 "SELECT last_updated, image_data, width, height FROM favicon_bitmaps "
709 statement.BindInt64(0, bitmap_id);
711 if (!statement.Step())
715 *last_updated = base::Time::FromInternalValue(statement.ColumnInt64(0));
717 if (png_icon_data && statement.ColumnByteLength(1) > 0) {
718 scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes());
719 statement.ColumnBlobAsVector(1, &data->data());
720 *png_icon_data = data;
724 *pixel_size = gfx::Size(statement.ColumnInt(2),
725 statement.ColumnInt(3));
730 FaviconBitmapID ThumbnailDatabase::AddFaviconBitmap(
731 favicon_base::FaviconID icon_id,
732 const scoped_refptr<base::RefCountedMemory>& icon_data,
734 const gfx::Size& pixel_size) {
736 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
737 "INSERT INTO favicon_bitmaps (icon_id, image_data, last_updated, width, "
738 "height) VALUES (?, ?, ?, ?, ?)"));
739 statement.BindInt64(0, icon_id);
740 if (icon_data.get() && icon_data->size()) {
741 statement.BindBlob(1, icon_data->front(),
742 static_cast<int>(icon_data->size()));
744 statement.BindNull(1);
746 statement.BindInt64(2, time.ToInternalValue());
747 statement.BindInt(3, pixel_size.width());
748 statement.BindInt(4, pixel_size.height());
750 if (!statement.Run())
752 return db_.GetLastInsertRowId();
755 bool ThumbnailDatabase::SetFaviconBitmap(
756 FaviconBitmapID bitmap_id,
757 scoped_refptr<base::RefCountedMemory> bitmap_data,
760 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
761 "UPDATE favicon_bitmaps SET image_data=?, last_updated=? WHERE id=?"));
762 if (bitmap_data.get() && bitmap_data->size()) {
763 statement.BindBlob(0, bitmap_data->front(),
764 static_cast<int>(bitmap_data->size()));
766 statement.BindNull(0);
768 statement.BindInt64(1, time.ToInternalValue());
769 statement.BindInt64(2, bitmap_id);
771 return statement.Run();
774 bool ThumbnailDatabase::SetFaviconBitmapLastUpdateTime(
775 FaviconBitmapID bitmap_id,
778 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
779 "UPDATE favicon_bitmaps SET last_updated=? WHERE id=?"));
780 statement.BindInt64(0, time.ToInternalValue());
781 statement.BindInt64(1, bitmap_id);
782 return statement.Run();
785 bool ThumbnailDatabase::DeleteFaviconBitmap(FaviconBitmapID bitmap_id) {
786 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
787 "DELETE FROM favicon_bitmaps WHERE id=?"));
788 statement.BindInt64(0, bitmap_id);
789 return statement.Run();
792 bool ThumbnailDatabase::SetFaviconOutOfDate(favicon_base::FaviconID icon_id) {
793 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
794 "UPDATE favicon_bitmaps SET last_updated=? WHERE icon_id=?"));
795 statement.BindInt64(0, 0);
796 statement.BindInt64(1, icon_id);
798 return statement.Run();
801 favicon_base::FaviconID ThumbnailDatabase::GetFaviconIDForFaviconURL(
802 const GURL& icon_url,
803 int required_icon_type,
804 favicon_base::IconType* icon_type) {
805 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
806 "SELECT id, icon_type FROM favicons WHERE url=? AND (icon_type & ? > 0) "
807 "ORDER BY icon_type DESC"));
808 statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url));
809 statement.BindInt(1, required_icon_type);
811 if (!statement.Step())
812 return 0; // not cached
815 *icon_type = static_cast<favicon_base::IconType>(statement.ColumnInt(1));
816 return statement.ColumnInt64(0);
819 bool ThumbnailDatabase::GetFaviconHeader(favicon_base::FaviconID icon_id,
821 favicon_base::IconType* icon_type) {
824 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
825 "SELECT url, icon_type FROM favicons WHERE id=?"));
826 statement.BindInt64(0, icon_id);
828 if (!statement.Step())
829 return false; // No entry for the id.
832 *icon_url = GURL(statement.ColumnString(0));
834 *icon_type = static_cast<favicon_base::IconType>(statement.ColumnInt(1));
839 favicon_base::FaviconID ThumbnailDatabase::AddFavicon(
840 const GURL& icon_url,
841 favicon_base::IconType icon_type) {
843 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
844 "INSERT INTO favicons (url, icon_type) VALUES (?, ?)"));
845 statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url));
846 statement.BindInt(1, icon_type);
848 if (!statement.Run())
850 return db_.GetLastInsertRowId();
853 favicon_base::FaviconID ThumbnailDatabase::AddFavicon(
854 const GURL& icon_url,
855 favicon_base::IconType icon_type,
856 const scoped_refptr<base::RefCountedMemory>& icon_data,
858 const gfx::Size& pixel_size) {
859 favicon_base::FaviconID icon_id = AddFavicon(icon_url, icon_type);
860 if (!icon_id || !AddFaviconBitmap(icon_id, icon_data, time, pixel_size))
866 bool ThumbnailDatabase::DeleteFavicon(favicon_base::FaviconID id) {
867 sql::Statement statement;
868 statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
869 "DELETE FROM favicons WHERE id = ?"));
870 statement.BindInt64(0, id);
871 if (!statement.Run())
874 statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
875 "DELETE FROM favicon_bitmaps WHERE icon_id = ?"));
876 statement.BindInt64(0, id);
877 return statement.Run();
880 bool ThumbnailDatabase::GetIconMappingsForPageURL(
881 const GURL& page_url,
882 int required_icon_types,
883 std::vector<IconMapping>* filtered_mapping_data) {
884 std::vector<IconMapping> mapping_data;
885 if (!GetIconMappingsForPageURL(page_url, &mapping_data))
889 for (std::vector<IconMapping>::iterator m = mapping_data.begin();
890 m != mapping_data.end(); ++m) {
891 if (m->icon_type & required_icon_types) {
893 if (!filtered_mapping_data)
896 // Restrict icon type of subsequent matches to |m->icon_type|.
897 // |m->icon_type| is the largest IconType in |mapping_data| because
898 // |mapping_data| is sorted in descending order of IconType.
899 required_icon_types = m->icon_type;
901 filtered_mapping_data->push_back(*m);
907 bool ThumbnailDatabase::GetIconMappingsForPageURL(
908 const GURL& page_url,
909 std::vector<IconMapping>* mapping_data) {
910 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
911 "SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type, "
914 "INNER JOIN favicons "
915 "ON icon_mapping.icon_id = favicons.id "
916 "WHERE icon_mapping.page_url=? "
917 "ORDER BY favicons.icon_type DESC"));
918 statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
921 while (statement.Step()) {
926 IconMapping icon_mapping;
927 FillIconMapping(statement, page_url, &icon_mapping);
928 mapping_data->push_back(icon_mapping);
933 IconMappingID ThumbnailDatabase::AddIconMapping(
934 const GURL& page_url,
935 favicon_base::FaviconID icon_id) {
937 "INSERT INTO icon_mapping (page_url, icon_id) VALUES (?, ?)";
938 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSql));
939 statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
940 statement.BindInt64(1, icon_id);
942 if (!statement.Run())
945 return db_.GetLastInsertRowId();
948 bool ThumbnailDatabase::UpdateIconMapping(IconMappingID mapping_id,
949 favicon_base::FaviconID icon_id) {
950 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
951 "UPDATE icon_mapping SET icon_id=? WHERE id=?"));
952 statement.BindInt64(0, icon_id);
953 statement.BindInt64(1, mapping_id);
955 return statement.Run();
958 bool ThumbnailDatabase::DeleteIconMappings(const GURL& page_url) {
959 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
960 "DELETE FROM icon_mapping WHERE page_url = ?"));
961 statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
963 return statement.Run();
966 bool ThumbnailDatabase::DeleteIconMapping(IconMappingID mapping_id) {
967 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
968 "DELETE FROM icon_mapping WHERE id=?"));
969 statement.BindInt64(0, mapping_id);
971 return statement.Run();
974 bool ThumbnailDatabase::HasMappingFor(favicon_base::FaviconID id) {
975 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
976 "SELECT id FROM icon_mapping "
978 statement.BindInt64(0, id);
980 return statement.Step();
983 bool ThumbnailDatabase::CloneIconMappings(const GURL& old_page_url,
984 const GURL& new_page_url) {
985 sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
986 "SELECT icon_id FROM icon_mapping "
987 "WHERE page_url=?"));
988 if (!statement.is_valid())
991 // Do nothing if there are existing bindings
992 statement.BindString(0, URLDatabase::GURLToDatabaseURL(new_page_url));
993 if (statement.Step())
996 statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
997 "INSERT INTO icon_mapping (page_url, icon_id) "
998 "SELECT ?, icon_id FROM icon_mapping "
999 "WHERE page_url = ?"));
1001 statement.BindString(0, URLDatabase::GURLToDatabaseURL(new_page_url));
1002 statement.BindString(1, URLDatabase::GURLToDatabaseURL(old_page_url));
1003 return statement.Run();
1006 bool ThumbnailDatabase::InitIconMappingEnumerator(
1007 favicon_base::IconType type,
1008 IconMappingEnumerator* enumerator) {
1009 DCHECK(!enumerator->statement_.is_valid());
1010 enumerator->statement_.Assign(db_.GetCachedStatement(
1012 "SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type, "
1013 "favicons.url, icon_mapping.page_url "
1014 "FROM icon_mapping JOIN favicons ON ("
1015 "icon_mapping.icon_id = favicons.id) "
1016 "WHERE favicons.icon_type = ?"));
1017 enumerator->statement_.BindInt(0, type);
1018 return enumerator->statement_.is_valid();
1021 bool ThumbnailDatabase::RetainDataForPageUrls(
1022 const std::vector<GURL>& urls_to_keep) {
1023 sql::Transaction transaction(&db_);
1024 if (!transaction.Begin())
1027 // temp.icon_id_mapping generates new icon ids as consecutive
1028 // integers starting from 1, and maps them to the old icon ids.
1030 const char kIconMappingCreate[] =
1031 "CREATE TEMP TABLE icon_id_mapping "
1033 "new_icon_id INTEGER PRIMARY KEY,"
1034 "old_icon_id INTEGER NOT NULL UNIQUE"
1036 if (!db_.Execute(kIconMappingCreate))
1039 // Insert the icon ids for retained urls, skipping duplicates.
1040 const char kIconMappingSql[] =
1041 "INSERT OR IGNORE INTO temp.icon_id_mapping (old_icon_id) "
1042 "SELECT icon_id FROM icon_mapping WHERE page_url = ?";
1043 sql::Statement statement(db_.GetUniqueStatement(kIconMappingSql));
1044 for (std::vector<GURL>::const_iterator
1045 i = urls_to_keep.begin(); i != urls_to_keep.end(); ++i) {
1046 statement.BindString(0, URLDatabase::GURLToDatabaseURL(*i));
1047 if (!statement.Run())
1049 statement.Reset(true);
1053 const char kRenameIconMappingTable[] =
1054 "ALTER TABLE icon_mapping RENAME TO old_icon_mapping";
1055 const char kCopyIconMapping[] =
1056 "INSERT INTO icon_mapping (page_url, icon_id) "
1057 "SELECT old.page_url, mapping.new_icon_id "
1058 "FROM old_icon_mapping AS old "
1059 "JOIN temp.icon_id_mapping AS mapping "
1060 "ON (old.icon_id = mapping.old_icon_id)";
1061 const char kDropOldIconMappingTable[] = "DROP TABLE old_icon_mapping";
1063 const char kRenameFaviconsTable[] =
1064 "ALTER TABLE favicons RENAME TO old_favicons";
1065 const char kCopyFavicons[] =
1066 "INSERT INTO favicons (id, url, icon_type) "
1067 "SELECT mapping.new_icon_id, old.url, old.icon_type "
1068 "FROM old_favicons AS old "
1069 "JOIN temp.icon_id_mapping AS mapping "
1070 "ON (old.id = mapping.old_icon_id)";
1071 const char kDropOldFaviconsTable[] = "DROP TABLE old_favicons";
1073 const char kRenameFaviconBitmapsTable[] =
1074 "ALTER TABLE favicon_bitmaps RENAME TO old_favicon_bitmaps";
1075 const char kCopyFaviconBitmaps[] =
1076 "INSERT INTO favicon_bitmaps "
1077 " (icon_id, last_updated, image_data, width, height) "
1078 "SELECT mapping.new_icon_id, old.last_updated, "
1079 " old.image_data, old.width, old.height "
1080 "FROM old_favicon_bitmaps AS old "
1081 "JOIN temp.icon_id_mapping AS mapping "
1082 "ON (old.icon_id = mapping.old_icon_id)";
1083 const char kDropOldFaviconBitmapsTable[] =
1084 "DROP TABLE old_favicon_bitmaps";
1086 // Rename existing tables to new location.
1087 if (!db_.Execute(kRenameIconMappingTable) ||
1088 !db_.Execute(kRenameFaviconsTable) ||
1089 !db_.Execute(kRenameFaviconBitmapsTable)) {
1093 // Initialize the replacement tables. At this point the old indices
1094 // still exist (pointing to the old_* tables), so do not initialize
1096 if (!InitTables(&db_))
1099 // Copy all of the data over.
1100 if (!db_.Execute(kCopyIconMapping) ||
1101 !db_.Execute(kCopyFavicons) ||
1102 !db_.Execute(kCopyFaviconBitmaps)) {
1106 // Drop the old_* tables, which also drops the indices.
1107 if (!db_.Execute(kDropOldIconMappingTable) ||
1108 !db_.Execute(kDropOldFaviconsTable) ||
1109 !db_.Execute(kDropOldFaviconBitmapsTable)) {
1113 // Recreate the indices.
1114 // TODO(shess): UNIQUE indices could fail due to duplication. This
1115 // could happen in case of corruption.
1116 if (!InitIndices(&db_))
1119 const char kIconMappingDrop[] = "DROP TABLE temp.icon_id_mapping";
1120 if (!db_.Execute(kIconMappingDrop))
1123 return transaction.Commit();
1126 sql::InitStatus ThumbnailDatabase::OpenDatabase(sql::Connection* db,
1127 const base::FilePath& db_name) {
1128 size_t startup_kb = 0;
1130 if (base::GetFileSize(db_name, &size_64))
1131 startup_kb = static_cast<size_t>(size_64 / 1024);
1133 db->set_histogram_tag("Thumbnail");
1134 db->set_error_callback(base::Bind(&DatabaseErrorCallback,
1135 db, db_name, startup_kb, history_client_));
1137 // Thumbnails db now only stores favicons, so we don't need that big a page
1139 db->set_page_size(2048);
1140 db->set_cache_size(32);
1142 // Run the database in exclusive mode. Nobody else should be accessing the
1143 // database while we're running, and this will give somewhat improved perf.
1144 db->set_exclusive_locking();
1146 if (!db->Open(db_name))
1147 return sql::INIT_FAILURE;
1149 return sql::INIT_OK;
1152 sql::InitStatus ThumbnailDatabase::InitImpl(const base::FilePath& db_name) {
1153 sql::InitStatus status = OpenDatabase(&db_, db_name);
1154 if (status != sql::INIT_OK)
1157 // Clear databases which are too old to process.
1158 DCHECK_LT(kDeprecatedVersionNumber, kCurrentVersionNumber);
1159 sql::MetaTable::RazeIfDeprecated(&db_, kDeprecatedVersionNumber);
1161 // TODO(shess): Sqlite.Version.Thumbnail shows versions 22, 23, and
1162 // 25. Future versions are not destroyed because that could lead to
1163 // data loss if the profile is opened by a later channel, but
1164 // perhaps a heuristic like >kCurrentVersionNumber+3 could be used.
1166 // Scope initialization in a transaction so we can't be partially initialized.
1167 sql::Transaction transaction(&db_);
1168 if (!transaction.Begin())
1169 return sql::INIT_FAILURE;
1171 // TODO(shess): Failing Begin() implies that something serious is
1172 // wrong with the database. Raze() may be in order.
1174 #if defined(OS_MACOSX)
1175 // Exclude the thumbnails file from backups.
1176 base::mac::SetFileBackupExclusion(db_name);
1179 // thumbnails table has been obsolete for a long time, remove any
1181 ignore_result(db_.Execute("DROP TABLE IF EXISTS thumbnails"));
1183 // At some point, operations involving temporary tables weren't done
1184 // atomically and users have been stranded. Drop those tables and
1186 // TODO(shess): Prove it? Audit all cases and see if it's possible
1187 // that this implies non-atomic update, and should thus be handled
1188 // via the corruption handler.
1189 ignore_result(db_.Execute("DROP TABLE IF EXISTS temp_favicons"));
1190 ignore_result(db_.Execute("DROP TABLE IF EXISTS temp_favicon_bitmaps"));
1191 ignore_result(db_.Execute("DROP TABLE IF EXISTS temp_icon_mapping"));
1193 // Create the tables.
1194 if (!meta_table_.Init(&db_, kCurrentVersionNumber,
1195 kCompatibleVersionNumber) ||
1196 !InitTables(&db_) ||
1197 !InitIndices(&db_)) {
1198 return sql::INIT_FAILURE;
1201 // Version check. We should not encounter a database too old for us to handle
1202 // in the wild, so we try to continue in that case.
1203 if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
1204 LOG(WARNING) << "Thumbnail database is too new.";
1205 return sql::INIT_TOO_NEW;
1208 int cur_version = meta_table_.GetVersionNumber();
1210 if (!db_.DoesColumnExist("favicons", "icon_type")) {
1211 LOG(ERROR) << "Raze because of missing favicon.icon_type";
1212 RecordInvalidStructure(STRUCTURE_EVENT_VERSION4);
1215 return sql::INIT_FAILURE;
1218 if (cur_version < 7 && !db_.DoesColumnExist("favicons", "sizes")) {
1219 LOG(ERROR) << "Raze because of missing favicon.sizes";
1220 RecordInvalidStructure(STRUCTURE_EVENT_VERSION5);
1223 return sql::INIT_FAILURE;
1226 if (cur_version == 5) {
1228 if (!UpgradeToVersion6())
1229 return CantUpgradeToVersion(cur_version);
1232 if (cur_version == 6) {
1234 if (!UpgradeToVersion7())
1235 return CantUpgradeToVersion(cur_version);
1238 LOG_IF(WARNING, cur_version < kCurrentVersionNumber) <<
1239 "Thumbnail database version " << cur_version << " is too old to handle.";
1241 // Initialization is complete.
1242 if (!transaction.Commit())
1243 return sql::INIT_FAILURE;
1245 // Raze the database if the structure of the favicons database is not what
1246 // it should be. This error cannot be detected via the SQL error code because
1247 // the error code for running SQL statements against a database with missing
1248 // columns is SQLITE_ERROR which is not unique enough to act upon.
1249 // TODO(pkotwicz): Revisit this in M27 and see if the razing can be removed.
1250 // (crbug.com/166453)
1251 if (IsFaviconDBStructureIncorrect()) {
1252 LOG(ERROR) << "Raze because of invalid favicon db structure.";
1253 RecordInvalidStructure(STRUCTURE_EVENT_FAVICON);
1256 return sql::INIT_FAILURE;
1259 return sql::INIT_OK;
1262 sql::InitStatus ThumbnailDatabase::CantUpgradeToVersion(int cur_version) {
1263 LOG(WARNING) << "Unable to update to thumbnail database to version " <<
1266 return sql::INIT_FAILURE;
1269 bool ThumbnailDatabase::UpgradeToVersion6() {
1270 // Move bitmap data from favicons to favicon_bitmaps.
1272 db_.Execute("INSERT INTO favicon_bitmaps (icon_id, last_updated, "
1273 "image_data, width, height)"
1274 "SELECT id, last_updated, image_data, 0, 0 FROM favicons") &&
1275 db_.Execute("CREATE TABLE temp_favicons ("
1276 "id INTEGER PRIMARY KEY,"
1277 "url LONGVARCHAR NOT NULL,"
1278 "icon_type INTEGER DEFAULT 1,"
1279 // default icon_type FAVICON to be consistent with
1281 "sizes LONGVARCHAR)") &&
1282 db_.Execute("INSERT INTO temp_favicons (id, url, icon_type) "
1283 "SELECT id, url, icon_type FROM favicons") &&
1284 db_.Execute("DROP TABLE favicons") &&
1285 db_.Execute("ALTER TABLE temp_favicons RENAME TO favicons");
1286 // NOTE(shess): v7 will re-create the index.
1290 meta_table_.SetVersionNumber(6);
1291 meta_table_.SetCompatibleVersionNumber(std::min(6, kCompatibleVersionNumber));
1295 bool ThumbnailDatabase::UpgradeToVersion7() {
1296 // Sizes column was never used, remove it.
1298 db_.Execute("CREATE TABLE temp_favicons ("
1299 "id INTEGER PRIMARY KEY,"
1300 "url LONGVARCHAR NOT NULL,"
1301 // default icon_type FAVICON to be consistent with
1303 "icon_type INTEGER DEFAULT 1)") &&
1304 db_.Execute("INSERT INTO temp_favicons (id, url, icon_type) "
1305 "SELECT id, url, icon_type FROM favicons") &&
1306 db_.Execute("DROP TABLE favicons") &&
1307 db_.Execute("ALTER TABLE temp_favicons RENAME TO favicons") &&
1308 db_.Execute("CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)");
1313 meta_table_.SetVersionNumber(7);
1314 meta_table_.SetCompatibleVersionNumber(std::min(7, kCompatibleVersionNumber));
1318 bool ThumbnailDatabase::IsFaviconDBStructureIncorrect() {
1319 return !db_.IsSQLValid("SELECT id, url, icon_type FROM favicons");
1322 } // namespace history