Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / history / thumbnail_database.cc
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/history/thumbnail_database.h"
6
7 #include <algorithm>
8 #include <string>
9
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"
27
28 #if defined(OS_MACOSX)
29 #include "base/mac/mac_util.h"
30 #endif
31
32 // Description of database tables:
33 //
34 // icon_mapping
35 //   id               Unique ID.
36 //   page_url         Page URL which has one or more associated favicons.
37 //   icon_id          The ID of favicon that this mapping maps to.
38 //
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.
45 //
46 //   id               Unique ID.
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.
51 //
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|
57 //                    table.
58 //
59 //  id                Unique ID.
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|.
67
68 namespace {
69
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
76 // anyhow).
77
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)
83
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.
90
91 void FillIconMapping(const sql::Statement& statement,
92                      const GURL& page_url,
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;
100 }
101
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,
108
109   // Always keep this at the end.
110   STRUCTURE_EVENT_MAX,
111 };
112
113 void RecordInvalidStructure(InvalidStructureType invalid_type) {
114   UMA_HISTOGRAM_ENUMERATION("History.InvalidFaviconsDBStructure",
115                             invalid_type, STRUCTURE_EVENT_MAX);
116 }
117
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);
123
124   base::debug::DumpWithoutCrashing();
125 }
126
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;
132
133   base::StringAppendF(&debug_info, "SQLITE_CORRUPT, integrity_check:\n");
134
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);
139   } else {
140     std::vector<std::string> messages;
141
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(),
146                         messages.size());
147
148     // SQLite returns up to 100 messages by default, trim deeper to
149     // keep close to the 2000-character size limit for dumping.
150     //
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
154     // restriction.
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());
158     }
159   }
160
161   DumpWithoutCrashing2000(debug_info);
162 }
163
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;
169
170   // The error message from the failed operation.
171   base::StringAppendF(&debug_info, "db error: %d/%s\n",
172                       db->GetErrorCode(), db->GetErrorMessage());
173
174   // System errno information.
175   base::StringAppendF(&debug_info, "errno: %d\n", db->GetLastErrno());
176
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);
186         debug_info += '\n';
187       } else if (statement.Succeeded()) {
188         debug_info += "version: none\n";
189       } else {
190         debug_info += "version: error\n";
191       }
192     } else {
193       debug_info += "version: invalid\n";
194     }
195
196     debug_info += "schema:\n";
197
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);
212       debug_info += '\n';
213     }
214     if (!statement.Succeeded())
215       debug_info += "error\n";
216   }
217
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).
223
224   DumpWithoutCrashing2000(debug_info);
225 }
226
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,
230                          size_t startup_kb,
231                          int extended_error) {
232   int error = (extended_error & 0xFF);
233
234   // Infrequently report information about the error up to the crash
235   // server.
236   static const uint64 kReportsPerMillion = 50000;
237
238   // Since some/most errors will not resolve themselves, only report
239   // once per Chrome run.
240   static bool reported = false;
241   if (reported)
242     return;
243
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.
249     reported = true;
250
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.
258     reported = true;
259
260     if (rand < kReportsPerMillion)
261       ReportError(db, extended_error);
262   } else {
263     // Only set the flag when making a report.  This should allow
264     // later (potentially different) errors in a stream of errors to
265     // be reported.
266     //
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) {
272       reported = true;
273       ReportError(db, extended_error);
274     }
275   }
276 }
277
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"
284       "("
285       "id INTEGER PRIMARY KEY,"
286       "page_url LONGVARCHAR NOT NULL,"
287       "icon_id INTEGER"
288       ")";
289   if (!db->Execute(kIconMappingSql))
290     return false;
291
292   const char kFaviconsSql[] =
293       "CREATE TABLE IF NOT EXISTS favicons"
294       "("
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"
299       ")";
300   if (!db->Execute(kFaviconsSql))
301     return false;
302
303   const char kFaviconBitmapsSql[] =
304       "CREATE TABLE IF NOT EXISTS favicon_bitmaps"
305       "("
306       "id INTEGER PRIMARY KEY,"
307       "icon_id INTEGER NOT NULL,"
308       "last_updated INTEGER DEFAULT 0,"
309       "image_data BLOB,"
310       "width INTEGER DEFAULT 0,"
311       "height INTEGER DEFAULT 0"
312       ")";
313   if (!db->Execute(kFaviconBitmapsSql))
314     return false;
315
316   return true;
317 }
318
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)) {
331     return false;
332   }
333
334   const char kFaviconsIndexSql[] =
335       "CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)";
336   if (!db->Execute(kFaviconsIndexSql))
337     return false;
338
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))
343     return false;
344
345   return true;
346 }
347
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,
377
378   // Always keep this at the end.
379   RECOVERY_EVENT_MAX,
380 };
381
382 void RecordRecoveryEvent(RecoveryEventType recovery_event) {
383   UMA_HISTOGRAM_ENUMERATION("History.FaviconsRecovery",
384                             recovery_event, RECOVERY_EVENT_MAX);
385 }
386
387 // Recover the database to the extent possible, razing it if recovery
388 // is not possible.
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);
398
399   // TODO(shess): Reset back after?
400   db->reset_error_callback();
401
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);
408
409   scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path);
410   if (!recovery) {
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.
414     //
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);
421     return;
422   }
423
424   // Setup the meta recovery table and fetch the version number from
425   // the corrupt database.
426   int version = 0;
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);
434     return;
435   }
436
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);
443   if (version <= 6) {
444     sql::Recovery::Unrecoverable(recovery.Pass());
445     RecordRecoveryEvent(RECOVERY_EVENT_DEPRECATED);
446     return;
447   }
448
449   // Earlier versions have been handled or deprecated, later versions should be
450   // impossible.
451   if (version != 7) {
452     sql::Recovery::Unrecoverable(recovery.Pass());
453     RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_WRONG_VERSION);
454     return;
455   }
456
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);
463     return;
464   }
465
466   // Create a fresh version of the database.  The recovery code uses
467   // conflict-resolution to handle duplicates, so the indices are
468   // necessary.
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.
473     //
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);
480     return;
481   }
482
483   if (!recovery->AutoRecoverTable("favicons", 0, &favicons_rows_recovered)) {
484     sql::Recovery::Rollback(recovery.Pass());
485     RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICONS);
486     return;
487   }
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);
492     return;
493   }
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);
498     return;
499   }
500
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
509   // collection.
510
511   if (!sql::Recovery::Recovered(recovery.Pass())) {
512     RecordRecoveryEvent(RECOVERY_EVENT_FAILED_COMMIT);
513     return;
514   }
515
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) &&
523       final_size > 0) {
524     int percentage = static_cast<int>(original_size * 100 / final_size);
525     UMA_HISTOGRAM_PERCENTAGE("History.FaviconsRecoveredPercentage",
526                              std::max(100, percentage));
527   }
528
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);
538
539   RecordRecoveryEvent(RECOVERY_EVENT_RECOVERED);
540 }
541
542 void DatabaseErrorCallback(sql::Connection* db,
543                            const base::FilePath& db_path,
544                            size_t startup_kb,
545                            history::HistoryClient* history_client,
546                            int extended_error,
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.
551
552   if (history_client && history_client->ShouldReportDatabaseError()) {
553     GenerateDiagnostics(db, startup_kb, extended_error);
554   }
555
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);
562   }
563
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();
567 }
568
569 }  // namespace
570
571 namespace history {
572
573 ThumbnailDatabase::IconMappingEnumerator::IconMappingEnumerator() {
574 }
575
576 ThumbnailDatabase::IconMappingEnumerator::~IconMappingEnumerator() {
577 }
578
579 bool ThumbnailDatabase::IconMappingEnumerator::GetNextIconMapping(
580     IconMapping* icon_mapping) {
581   if (!statement_.Step())
582     return false;
583   FillIconMapping(statement_, GURL(statement_.ColumnString(4)), icon_mapping);
584   return true;
585 }
586
587 ThumbnailDatabase::ThumbnailDatabase(HistoryClient* history_client)
588     : history_client_(history_client) {
589 }
590
591 ThumbnailDatabase::~ThumbnailDatabase() {
592   // The DBCloseScoper will delete the DB and the cache.
593 }
594
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().
599
600   // Retry failed setup in case the recovery system fixed things.
601   const size_t kAttempts = 2;
602
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)
607       return status;
608
609     meta_table_.Reset();
610     db_.Close();
611   }
612   return status;
613 }
614
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);
621 }
622
623 void ThumbnailDatabase::BeginTransaction() {
624   db_.BeginTransaction();
625 }
626
627 void ThumbnailDatabase::CommitTransaction() {
628   db_.CommitTransaction();
629 }
630
631 void ThumbnailDatabase::RollbackTransaction() {
632   db_.RollbackTransaction();
633 }
634
635 void ThumbnailDatabase::Vacuum() {
636   DCHECK(db_.transaction_nesting() == 0) <<
637       "Can not have a transaction when vacuuming.";
638   ignore_result(db_.Execute("VACUUM"));
639 }
640
641 void ThumbnailDatabase::TrimMemory(bool aggressively) {
642   db_.TrimMemory(aggressively);
643 }
644
645 bool ThumbnailDatabase::GetFaviconBitmapIDSizes(
646     favicon_base::FaviconID icon_id,
647     std::vector<FaviconBitmapIDSize>* bitmap_id_sizes) {
648   DCHECK(icon_id);
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);
652
653   bool result = false;
654   while (statement.Step()) {
655     result = true;
656     if (!bitmap_id_sizes)
657       return result;
658
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);
664   }
665   return result;
666 }
667
668 bool ThumbnailDatabase::GetFaviconBitmaps(
669     favicon_base::FaviconID icon_id,
670     std::vector<FaviconBitmap>* favicon_bitmaps) {
671   DCHECK(icon_id);
672   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
673       "SELECT id, last_updated, image_data, width, height FROM favicon_bitmaps "
674       "WHERE icon_id=?"));
675   statement.BindInt64(0, icon_id);
676
677   bool result = false;
678   while (statement.Step()) {
679     result = true;
680     if (!favicon_bitmaps)
681       return result;
682
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;
692     }
693     favicon_bitmap.pixel_size = gfx::Size(statement.ColumnInt(3),
694                                           statement.ColumnInt(4));
695     favicon_bitmaps->push_back(favicon_bitmap);
696   }
697   return result;
698 }
699
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) {
705   DCHECK(bitmap_id);
706   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
707       "SELECT last_updated, image_data, width, height FROM favicon_bitmaps "
708       "WHERE id=?"));
709   statement.BindInt64(0, bitmap_id);
710
711   if (!statement.Step())
712     return false;
713
714   if (last_updated)
715     *last_updated = base::Time::FromInternalValue(statement.ColumnInt64(0));
716
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;
721   }
722
723   if (pixel_size) {
724     *pixel_size = gfx::Size(statement.ColumnInt(2),
725                             statement.ColumnInt(3));
726   }
727   return true;
728 }
729
730 FaviconBitmapID ThumbnailDatabase::AddFaviconBitmap(
731     favicon_base::FaviconID icon_id,
732     const scoped_refptr<base::RefCountedMemory>& icon_data,
733     base::Time time,
734     const gfx::Size& pixel_size) {
735   DCHECK(icon_id);
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()));
743   } else {
744     statement.BindNull(1);
745   }
746   statement.BindInt64(2, time.ToInternalValue());
747   statement.BindInt(3, pixel_size.width());
748   statement.BindInt(4, pixel_size.height());
749
750   if (!statement.Run())
751     return 0;
752   return db_.GetLastInsertRowId();
753 }
754
755 bool ThumbnailDatabase::SetFaviconBitmap(
756     FaviconBitmapID bitmap_id,
757     scoped_refptr<base::RefCountedMemory> bitmap_data,
758     base::Time time) {
759   DCHECK(bitmap_id);
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()));
765   } else {
766     statement.BindNull(0);
767   }
768   statement.BindInt64(1, time.ToInternalValue());
769   statement.BindInt64(2, bitmap_id);
770
771   return statement.Run();
772 }
773
774 bool ThumbnailDatabase::SetFaviconBitmapLastUpdateTime(
775     FaviconBitmapID bitmap_id,
776     base::Time time) {
777   DCHECK(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();
783 }
784
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();
790 }
791
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);
797
798   return statement.Run();
799 }
800
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);
810
811   if (!statement.Step())
812     return 0;  // not cached
813
814   if (icon_type)
815     *icon_type = static_cast<favicon_base::IconType>(statement.ColumnInt(1));
816   return statement.ColumnInt64(0);
817 }
818
819 bool ThumbnailDatabase::GetFaviconHeader(favicon_base::FaviconID icon_id,
820                                          GURL* icon_url,
821                                          favicon_base::IconType* icon_type) {
822   DCHECK(icon_id);
823
824   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
825       "SELECT url, icon_type FROM favicons WHERE id=?"));
826   statement.BindInt64(0, icon_id);
827
828   if (!statement.Step())
829     return false;  // No entry for the id.
830
831   if (icon_url)
832     *icon_url = GURL(statement.ColumnString(0));
833   if (icon_type)
834     *icon_type = static_cast<favicon_base::IconType>(statement.ColumnInt(1));
835
836   return true;
837 }
838
839 favicon_base::FaviconID ThumbnailDatabase::AddFavicon(
840     const GURL& icon_url,
841     favicon_base::IconType icon_type) {
842
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);
847
848   if (!statement.Run())
849     return 0;
850   return db_.GetLastInsertRowId();
851 }
852
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,
857     base::Time time,
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))
861     return 0;
862
863   return icon_id;
864 }
865
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())
872     return false;
873
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();
878 }
879
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))
886     return false;
887
888   bool result = false;
889   for (std::vector<IconMapping>::iterator m = mapping_data.begin();
890        m != mapping_data.end(); ++m) {
891     if (m->icon_type & required_icon_types) {
892       result = true;
893       if (!filtered_mapping_data)
894         return result;
895
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;
900
901       filtered_mapping_data->push_back(*m);
902     }
903   }
904   return result;
905 }
906
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, "
912       "favicons.url "
913       "FROM icon_mapping "
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));
919
920   bool result = false;
921   while (statement.Step()) {
922     result = true;
923     if (!mapping_data)
924       return result;
925
926     IconMapping icon_mapping;
927     FillIconMapping(statement, page_url, &icon_mapping);
928     mapping_data->push_back(icon_mapping);
929   }
930   return result;
931 }
932
933 IconMappingID ThumbnailDatabase::AddIconMapping(
934     const GURL& page_url,
935     favicon_base::FaviconID icon_id) {
936   const char kSql[] =
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);
941
942   if (!statement.Run())
943     return 0;
944
945   return db_.GetLastInsertRowId();
946 }
947
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);
954
955   return statement.Run();
956 }
957
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));
962
963   return statement.Run();
964 }
965
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);
970
971   return statement.Run();
972 }
973
974 bool ThumbnailDatabase::HasMappingFor(favicon_base::FaviconID id) {
975   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
976       "SELECT id FROM icon_mapping "
977       "WHERE icon_id=?"));
978   statement.BindInt64(0, id);
979
980   return statement.Step();
981 }
982
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())
989     return false;
990
991   // Do nothing if there are existing bindings
992   statement.BindString(0, URLDatabase::GURLToDatabaseURL(new_page_url));
993   if (statement.Step())
994     return true;
995
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 = ?"));
1000
1001   statement.BindString(0, URLDatabase::GURLToDatabaseURL(new_page_url));
1002   statement.BindString(1, URLDatabase::GURLToDatabaseURL(old_page_url));
1003   return statement.Run();
1004 }
1005
1006 bool ThumbnailDatabase::InitIconMappingEnumerator(
1007     favicon_base::IconType type,
1008     IconMappingEnumerator* enumerator) {
1009   DCHECK(!enumerator->statement_.is_valid());
1010   enumerator->statement_.Assign(db_.GetCachedStatement(
1011       SQL_FROM_HERE,
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();
1019 }
1020
1021 bool ThumbnailDatabase::RetainDataForPageUrls(
1022     const std::vector<GURL>& urls_to_keep) {
1023   sql::Transaction transaction(&db_);
1024   if (!transaction.Begin())
1025     return false;
1026
1027   // temp.icon_id_mapping generates new icon ids as consecutive
1028   // integers starting from 1, and maps them to the old icon ids.
1029   {
1030     const char kIconMappingCreate[] =
1031         "CREATE TEMP TABLE icon_id_mapping "
1032         "("
1033         "new_icon_id INTEGER PRIMARY KEY,"
1034         "old_icon_id INTEGER NOT NULL UNIQUE"
1035         ")";
1036     if (!db_.Execute(kIconMappingCreate))
1037       return false;
1038
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())
1048         return false;
1049       statement.Reset(true);
1050     }
1051   }
1052
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";
1062
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";
1072
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";
1085
1086   // Rename existing tables to new location.
1087   if (!db_.Execute(kRenameIconMappingTable) ||
1088       !db_.Execute(kRenameFaviconsTable) ||
1089       !db_.Execute(kRenameFaviconBitmapsTable)) {
1090     return false;
1091   }
1092
1093   // Initialize the replacement tables.  At this point the old indices
1094   // still exist (pointing to the old_* tables), so do not initialize
1095   // the indices.
1096   if (!InitTables(&db_))
1097     return false;
1098
1099   // Copy all of the data over.
1100   if (!db_.Execute(kCopyIconMapping) ||
1101       !db_.Execute(kCopyFavicons) ||
1102       !db_.Execute(kCopyFaviconBitmaps)) {
1103     return false;
1104   }
1105
1106   // Drop the old_* tables, which also drops the indices.
1107   if (!db_.Execute(kDropOldIconMappingTable) ||
1108       !db_.Execute(kDropOldFaviconsTable) ||
1109       !db_.Execute(kDropOldFaviconBitmapsTable)) {
1110     return false;
1111   }
1112
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_))
1117     return false;
1118
1119   const char kIconMappingDrop[] = "DROP TABLE temp.icon_id_mapping";
1120   if (!db_.Execute(kIconMappingDrop))
1121     return false;
1122
1123   return transaction.Commit();
1124 }
1125
1126 sql::InitStatus ThumbnailDatabase::OpenDatabase(sql::Connection* db,
1127                                                 const base::FilePath& db_name) {
1128   size_t startup_kb = 0;
1129   int64 size_64;
1130   if (base::GetFileSize(db_name, &size_64))
1131     startup_kb = static_cast<size_t>(size_64 / 1024);
1132
1133   db->set_histogram_tag("Thumbnail");
1134   db->set_error_callback(base::Bind(&DatabaseErrorCallback,
1135                                     db, db_name, startup_kb, history_client_));
1136
1137   // Thumbnails db now only stores favicons, so we don't need that big a page
1138   // size or cache.
1139   db->set_page_size(2048);
1140   db->set_cache_size(32);
1141
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();
1145
1146   if (!db->Open(db_name))
1147     return sql::INIT_FAILURE;
1148
1149   return sql::INIT_OK;
1150 }
1151
1152 sql::InitStatus ThumbnailDatabase::InitImpl(const base::FilePath& db_name) {
1153   sql::InitStatus status = OpenDatabase(&db_, db_name);
1154   if (status != sql::INIT_OK)
1155     return status;
1156
1157   // Clear databases which are too old to process.
1158   DCHECK_LT(kDeprecatedVersionNumber, kCurrentVersionNumber);
1159   sql::MetaTable::RazeIfDeprecated(&db_, kDeprecatedVersionNumber);
1160
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.
1165
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;
1170
1171   // TODO(shess): Failing Begin() implies that something serious is
1172   // wrong with the database.  Raze() may be in order.
1173
1174 #if defined(OS_MACOSX)
1175   // Exclude the thumbnails file from backups.
1176   base::mac::SetFileBackupExclusion(db_name);
1177 #endif
1178
1179   // thumbnails table has been obsolete for a long time, remove any
1180   // detrious.
1181   ignore_result(db_.Execute("DROP TABLE IF EXISTS thumbnails"));
1182
1183   // At some point, operations involving temporary tables weren't done
1184   // atomically and users have been stranded.  Drop those tables and
1185   // move on.
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"));
1192
1193   // Create the tables.
1194   if (!meta_table_.Init(&db_, kCurrentVersionNumber,
1195                         kCompatibleVersionNumber) ||
1196       !InitTables(&db_) ||
1197       !InitIndices(&db_)) {
1198     return sql::INIT_FAILURE;
1199   }
1200
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;
1206   }
1207
1208   int cur_version = meta_table_.GetVersionNumber();
1209
1210   if (!db_.DoesColumnExist("favicons", "icon_type")) {
1211     LOG(ERROR) << "Raze because of missing favicon.icon_type";
1212     RecordInvalidStructure(STRUCTURE_EVENT_VERSION4);
1213
1214     db_.RazeAndClose();
1215     return sql::INIT_FAILURE;
1216   }
1217
1218   if (cur_version < 7 && !db_.DoesColumnExist("favicons", "sizes")) {
1219     LOG(ERROR) << "Raze because of missing favicon.sizes";
1220     RecordInvalidStructure(STRUCTURE_EVENT_VERSION5);
1221
1222     db_.RazeAndClose();
1223     return sql::INIT_FAILURE;
1224   }
1225
1226   if (cur_version == 5) {
1227     ++cur_version;
1228     if (!UpgradeToVersion6())
1229       return CantUpgradeToVersion(cur_version);
1230   }
1231
1232   if (cur_version == 6) {
1233     ++cur_version;
1234     if (!UpgradeToVersion7())
1235       return CantUpgradeToVersion(cur_version);
1236   }
1237
1238   LOG_IF(WARNING, cur_version < kCurrentVersionNumber) <<
1239       "Thumbnail database version " << cur_version << " is too old to handle.";
1240
1241   // Initialization is complete.
1242   if (!transaction.Commit())
1243     return sql::INIT_FAILURE;
1244
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);
1254
1255     db_.RazeAndClose();
1256     return sql::INIT_FAILURE;
1257   }
1258
1259   return sql::INIT_OK;
1260 }
1261
1262 sql::InitStatus ThumbnailDatabase::CantUpgradeToVersion(int cur_version) {
1263   LOG(WARNING) << "Unable to update to thumbnail database to version " <<
1264                cur_version << ".";
1265   db_.Close();
1266   return sql::INIT_FAILURE;
1267 }
1268
1269 bool ThumbnailDatabase::UpgradeToVersion6() {
1270   // Move bitmap data from favicons to favicon_bitmaps.
1271   bool success =
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
1280                   // past migration.
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.
1287   if (!success)
1288     return false;
1289
1290   meta_table_.SetVersionNumber(6);
1291   meta_table_.SetCompatibleVersionNumber(std::min(6, kCompatibleVersionNumber));
1292   return true;
1293 }
1294
1295 bool ThumbnailDatabase::UpgradeToVersion7() {
1296   // Sizes column was never used, remove it.
1297   bool success =
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
1302                   // past migration.
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)");
1309
1310   if (!success)
1311     return false;
1312
1313   meta_table_.SetVersionNumber(7);
1314   meta_table_.SetCompatibleVersionNumber(std::min(7, kCompatibleVersionNumber));
1315   return true;
1316 }
1317
1318 bool ThumbnailDatabase::IsFaviconDBStructureIncorrect() {
1319   return !db_.IsSQLValid("SELECT id, url, icon_type FROM favicons");
1320 }
1321
1322 }  // namespace history