Home | History | Annotate | Download | only in history
      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/file_util.h"
     13 #include "base/format_macros.h"
     14 #include "base/memory/ref_counted_memory.h"
     15 #include "base/metrics/histogram.h"
     16 #include "base/rand_util.h"
     17 #include "base/strings/string_util.h"
     18 #include "base/strings/stringprintf.h"
     19 #include "base/time/time.h"
     20 #include "chrome/browser/history/url_database.h"
     21 #include "chrome/common/chrome_version_info.h"
     22 #include "chrome/common/dump_without_crashing.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 (at) chromium.org on 2013-07-01
     79 // Version 6: 610f923b/r152367 by pkotwicz (at) chromium.org on 2012-08-20
     80 // Version 5: e2ee8ae9/r105004 by groby (at) chromium.org on 2011-10-12
     81 // Version 4: 5f104d76/r77288 by sky (at) 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<chrome::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   logging::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 // Create v5 schema for recovery code.
    279 bool InitSchemaV5(sql::Connection* db) {
    280   // This schema was derived from the strings used when v5 was in
    281   // force.  The [favicons] index and the [icon_mapping] items were
    282   // copied from the current strings, after verifying that the
    283   // resulting schema exactly matches the schema created by the
    284   // original versions of those strings.  This allows the linker to
    285   // share the strings if they match, while preferring correctness of
    286   // the current versions change.
    287 
    288   const char kFaviconsV5[] =
    289       "CREATE TABLE IF NOT EXISTS favicons("
    290       "id INTEGER PRIMARY KEY,"
    291       "url LONGVARCHAR NOT NULL,"
    292       "last_updated INTEGER DEFAULT 0,"
    293       "image_data BLOB,"
    294       "icon_type INTEGER DEFAULT 1,"
    295       "sizes LONGVARCHAR"
    296       ")";
    297   const char kFaviconsIndexV5[] =
    298       "CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)";
    299   if (!db->Execute(kFaviconsV5) || !db->Execute(kFaviconsIndexV5))
    300     return false;
    301 
    302   const char kIconMappingV5[] =
    303       "CREATE TABLE IF NOT EXISTS icon_mapping"
    304       "("
    305       "id INTEGER PRIMARY KEY,"
    306       "page_url LONGVARCHAR NOT NULL,"
    307       "icon_id INTEGER"
    308       ")";
    309   const char kIconMappingUrlIndexV5[] =
    310       "CREATE INDEX IF NOT EXISTS icon_mapping_page_url_idx"
    311       " ON icon_mapping(page_url)";
    312   const char kIconMappingIdIndexV5[] =
    313       "CREATE INDEX IF NOT EXISTS icon_mapping_icon_id_idx"
    314       " ON icon_mapping(icon_id)";
    315   if (!db->Execute(kIconMappingV5) ||
    316       !db->Execute(kIconMappingUrlIndexV5) ||
    317       !db->Execute(kIconMappingIdIndexV5)) {
    318     return false;
    319   }
    320 
    321   return true;
    322 }
    323 
    324 // TODO(shess): Consider InitSchemaV7().  InitSchemaV5() is worthwhile
    325 // because there appear to be 10s of thousands of marooned v5
    326 // databases in the wild.  Once recovery reaches stable, the number of
    327 // corrupt-but-recoverable databases should drop, possibly to the
    328 // point where it is not worthwhile to maintain previous-version
    329 // recovery code.
    330 // TODO(shess): Alternately, think on a way to more cleanly represent
    331 // versioned schema going forward.
    332 bool InitTables(sql::Connection* db) {
    333   const char kIconMappingSql[] =
    334       "CREATE TABLE IF NOT EXISTS icon_mapping"
    335       "("
    336       "id INTEGER PRIMARY KEY,"
    337       "page_url LONGVARCHAR NOT NULL,"
    338       "icon_id INTEGER"
    339       ")";
    340   if (!db->Execute(kIconMappingSql))
    341     return false;
    342 
    343   const char kFaviconsSql[] =
    344       "CREATE TABLE IF NOT EXISTS favicons"
    345       "("
    346       "id INTEGER PRIMARY KEY,"
    347       "url LONGVARCHAR NOT NULL,"
    348       // default icon_type FAVICON to be consistent with past migration.
    349       "icon_type INTEGER DEFAULT 1"
    350       ")";
    351   if (!db->Execute(kFaviconsSql))
    352     return false;
    353 
    354   const char kFaviconBitmapsSql[] =
    355       "CREATE TABLE IF NOT EXISTS favicon_bitmaps"
    356       "("
    357       "id INTEGER PRIMARY KEY,"
    358       "icon_id INTEGER NOT NULL,"
    359       "last_updated INTEGER DEFAULT 0,"
    360       "image_data BLOB,"
    361       "width INTEGER DEFAULT 0,"
    362       "height INTEGER DEFAULT 0"
    363       ")";
    364   if (!db->Execute(kFaviconBitmapsSql))
    365     return false;
    366 
    367   return true;
    368 }
    369 
    370 bool InitIndices(sql::Connection* db) {
    371   const char kIconMappingUrlIndexSql[] =
    372       "CREATE INDEX IF NOT EXISTS icon_mapping_page_url_idx"
    373       " ON icon_mapping(page_url)";
    374   const char kIconMappingIdIndexSql[] =
    375       "CREATE INDEX IF NOT EXISTS icon_mapping_icon_id_idx"
    376       " ON icon_mapping(icon_id)";
    377   if (!db->Execute(kIconMappingUrlIndexSql) ||
    378       !db->Execute(kIconMappingIdIndexSql)) {
    379     return false;
    380   }
    381 
    382   const char kFaviconsIndexSql[] =
    383       "CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)";
    384   if (!db->Execute(kFaviconsIndexSql))
    385     return false;
    386 
    387   const char kFaviconBitmapsIndexSql[] =
    388       "CREATE INDEX IF NOT EXISTS favicon_bitmaps_icon_id ON "
    389       "favicon_bitmaps(icon_id)";
    390   if (!db->Execute(kFaviconBitmapsIndexSql))
    391     return false;
    392 
    393   return true;
    394 }
    395 
    396 enum RecoveryEventType {
    397   RECOVERY_EVENT_RECOVERED = 0,
    398   RECOVERY_EVENT_FAILED_SCOPER,
    399   RECOVERY_EVENT_FAILED_META_VERSION_ERROR,  // obsolete
    400   RECOVERY_EVENT_FAILED_META_VERSION_NONE,  // obsolete
    401   RECOVERY_EVENT_FAILED_META_WRONG_VERSION6,  // obsolete
    402   RECOVERY_EVENT_FAILED_META_WRONG_VERSION5,  // obsolete
    403   RECOVERY_EVENT_FAILED_META_WRONG_VERSION,
    404   RECOVERY_EVENT_FAILED_RECOVER_META,  // obsolete
    405   RECOVERY_EVENT_FAILED_META_INSERT,  // obsolete
    406   RECOVERY_EVENT_FAILED_INIT,
    407   RECOVERY_EVENT_FAILED_RECOVER_FAVICONS,  // obsolete
    408   RECOVERY_EVENT_FAILED_FAVICONS_INSERT,  // obsolete
    409   RECOVERY_EVENT_FAILED_RECOVER_FAVICON_BITMAPS,  // obsolete
    410   RECOVERY_EVENT_FAILED_FAVICON_BITMAPS_INSERT,  // obsolete
    411   RECOVERY_EVENT_FAILED_RECOVER_ICON_MAPPING,  // obsolete
    412   RECOVERY_EVENT_FAILED_ICON_MAPPING_INSERT,  // obsolete
    413   RECOVERY_EVENT_RECOVERED_VERSION6,
    414   RECOVERY_EVENT_FAILED_META_INIT,
    415   RECOVERY_EVENT_FAILED_META_VERSION,
    416   RECOVERY_EVENT_DEPRECATED,
    417   RECOVERY_EVENT_FAILED_V5_INITSCHEMA,
    418   RECOVERY_EVENT_FAILED_V5_AUTORECOVER_FAVICONS,
    419   RECOVERY_EVENT_FAILED_V5_AUTORECOVER_ICON_MAPPING,
    420   RECOVERY_EVENT_RECOVERED_VERSION5,
    421   RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICONS,
    422   RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICON_BITMAPS,
    423   RECOVERY_EVENT_FAILED_AUTORECOVER_ICON_MAPPING,
    424   RECOVERY_EVENT_FAILED_COMMIT,
    425 
    426   // Always keep this at the end.
    427   RECOVERY_EVENT_MAX,
    428 };
    429 
    430 void RecordRecoveryEvent(RecoveryEventType recovery_event) {
    431   UMA_HISTOGRAM_ENUMERATION("History.FaviconsRecovery",
    432                             recovery_event, RECOVERY_EVENT_MAX);
    433 }
    434 
    435 // Recover the database to the extent possible, razing it if recovery
    436 // is not possible.
    437 // TODO(shess): This is mostly just a safe proof of concept.  In the
    438 // real world, this database is probably not worthwhile recovering, as
    439 // opposed to just razing it and starting over whenever corruption is
    440 // detected.  So this database is a good test subject.
    441 void RecoverDatabaseOrRaze(sql::Connection* db, const base::FilePath& db_path) {
    442   // NOTE(shess): This code is currently specific to the version
    443   // number.  I am working on simplifying things to loosen the
    444   // dependency, meanwhile contact me if you need to bump the version.
    445   DCHECK_EQ(7, kCurrentVersionNumber);
    446 
    447   // TODO(shess): Reset back after?
    448   db->reset_error_callback();
    449 
    450   // For histogram purposes.
    451   size_t favicons_rows_recovered = 0;
    452   size_t favicon_bitmaps_rows_recovered = 0;
    453   size_t icon_mapping_rows_recovered = 0;
    454   int64 original_size = 0;
    455   base::GetFileSize(db_path, &original_size);
    456 
    457   scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path);
    458   if (!recovery) {
    459     // TODO(shess): Unable to create recovery connection.  This
    460     // implies something substantial is wrong.  At this point |db| has
    461     // been poisoned so there is nothing really to do.
    462     //
    463     // Possible responses are unclear.  If the failure relates to a
    464     // problem somehow specific to the temporary file used to back the
    465     // database, then an in-memory database could possibly be used.
    466     // This could potentially allow recovering the main database, and
    467     // might be simple to implement w/in Begin().
    468     RecordRecoveryEvent(RECOVERY_EVENT_FAILED_SCOPER);
    469     return;
    470   }
    471 
    472   // Setup the meta recovery table and fetch the version number from
    473   // the corrupt database.
    474   int version = 0;
    475   if (!recovery->SetupMeta() || !recovery->GetMetaVersionNumber(&version)) {
    476     // TODO(shess): Prior histograms indicate all failures are in
    477     // creating the recover virtual table for corrupt.meta.  The table
    478     // may not exist, or the database may be too far gone.  Either
    479     // way, unclear how to resolve.
    480     sql::Recovery::Rollback(recovery.Pass());
    481     RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_VERSION);
    482     return;
    483   }
    484 
    485   // Recover v5 database to v5 schema.  Next pass through Init() will
    486   // migrate to v7.
    487   if (version == 5) {
    488     sql::MetaTable recover_meta_table;
    489     if (!recover_meta_table.Init(recovery->db(), version, version)) {
    490       sql::Recovery::Rollback(recovery.Pass());
    491       RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_INIT);
    492       return;
    493     }
    494 
    495     // TODO(shess): These tests are separate for histogram purposes,
    496     // but once things look stable it can be tightened up.
    497     if (!InitSchemaV5(recovery->db())) {
    498       sql::Recovery::Rollback(recovery.Pass());
    499       RecordRecoveryEvent(RECOVERY_EVENT_FAILED_V5_INITSCHEMA);
    500       return;
    501     }
    502 
    503     if (!recovery->AutoRecoverTable("favicons", 0, &favicons_rows_recovered)) {
    504       sql::Recovery::Rollback(recovery.Pass());
    505       RecordRecoveryEvent(RECOVERY_EVENT_FAILED_V5_AUTORECOVER_FAVICONS);
    506       return;
    507     }
    508 
    509     if (!recovery->AutoRecoverTable("icon_mapping", 0,
    510                                     &icon_mapping_rows_recovered)) {
    511       sql::Recovery::Rollback(recovery.Pass());
    512       RecordRecoveryEvent(RECOVERY_EVENT_FAILED_V5_AUTORECOVER_ICON_MAPPING);
    513       return;
    514     }
    515 
    516     ignore_result(sql::Recovery::Recovered(recovery.Pass()));
    517 
    518     // TODO(shess): Could this code be shared with the v6/7 code
    519     // without requiring too much state to be carried?
    520 
    521     // Track the size of the recovered database relative to the size of
    522     // the input database.  The size should almost always be smaller,
    523     // unless the input database was empty to start with.  If the
    524     // percentage results are very low, something is awry.
    525     int64 final_size = 0;
    526     if (original_size > 0 &&
    527         base::GetFileSize(db_path, &final_size) &&
    528         final_size > 0) {
    529       UMA_HISTOGRAM_PERCENTAGE("History.FaviconsRecoveredPercentage",
    530                                final_size * 100 / original_size);
    531     }
    532 
    533     // Using 10,000 because these cases mostly care about "none
    534     // recovered" and "lots recovered".  More than 10,000 rows recovered
    535     // probably means there's something wrong with the profile.
    536     UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsFavicons",
    537                                favicons_rows_recovered);
    538     UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsIconMapping",
    539                                icon_mapping_rows_recovered);
    540 
    541     RecordRecoveryEvent(RECOVERY_EVENT_RECOVERED_VERSION5);
    542     return;
    543   }
    544 
    545   // This code may be able to fetch versions that the regular
    546   // deprecation path cannot.
    547   if (version <= kDeprecatedVersionNumber) {
    548     sql::Recovery::Unrecoverable(recovery.Pass());
    549     RecordRecoveryEvent(RECOVERY_EVENT_DEPRECATED);
    550     return;
    551   }
    552 
    553   // TODO(shess): Earlier versions have been handled or deprecated,
    554   // later versions should be impossible.  Unrecoverable() seems
    555   // reasonable.
    556   if (version != 6 && version != 7) {
    557     RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_WRONG_VERSION);
    558     sql::Recovery::Rollback(recovery.Pass());
    559     return;
    560   }
    561 
    562   // Both v6 and v7 recover to current schema version.
    563   sql::MetaTable recover_meta_table;
    564   if (!recover_meta_table.Init(recovery->db(), kCurrentVersionNumber,
    565                                kCompatibleVersionNumber)) {
    566     sql::Recovery::Rollback(recovery.Pass());
    567     RecordRecoveryEvent(RECOVERY_EVENT_FAILED_META_INIT);
    568     return;
    569   }
    570 
    571   // Create a fresh version of the database.  The recovery code uses
    572   // conflict-resolution to handle duplicates, so the indices are
    573   // necessary.
    574   if (!InitTables(recovery->db()) || !InitIndices(recovery->db())) {
    575     // TODO(shess): Unable to create the new schema in the new
    576     // database.  The new database should be a temporary file, so
    577     // being unable to work with it is pretty unclear.
    578     //
    579     // What are the potential responses, even?  The recovery database
    580     // could be opened as in-memory.  If the temp database had a
    581     // filesystem problem and the temp filesystem differs from the
    582     // main database, then that could fix it.
    583     sql::Recovery::Rollback(recovery.Pass());
    584     RecordRecoveryEvent(RECOVERY_EVENT_FAILED_INIT);
    585     return;
    586   }
    587 
    588   // [favicons] differs because v6 had an unused [sizes] column which
    589   // was removed in v7.
    590   if (!recovery->AutoRecoverTable("favicons", 1, &favicons_rows_recovered)) {
    591     sql::Recovery::Rollback(recovery.Pass());
    592     RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICONS);
    593     return;
    594   }
    595   if (!recovery->AutoRecoverTable("favicon_bitmaps", 0,
    596                                   &favicon_bitmaps_rows_recovered)) {
    597     sql::Recovery::Rollback(recovery.Pass());
    598     RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_FAVICON_BITMAPS);
    599     return;
    600   }
    601   if (!recovery->AutoRecoverTable("icon_mapping", 0,
    602                                   &icon_mapping_rows_recovered)) {
    603     sql::Recovery::Rollback(recovery.Pass());
    604     RecordRecoveryEvent(RECOVERY_EVENT_FAILED_AUTORECOVER_ICON_MAPPING);
    605     return;
    606   }
    607 
    608   // TODO(shess): Is it possible/likely to have broken foreign-key
    609   // issues with the tables?
    610   // - icon_mapping.icon_id maps to no favicons.id
    611   // - favicon_bitmaps.icon_id maps to no favicons.id
    612   // - favicons.id is referenced by no icon_mapping.icon_id
    613   // - favicons.id is referenced by no favicon_bitmaps.icon_id
    614   // This step is possibly not worth the effort necessary to develop
    615   // and sequence the statements, as it is basically a form of garbage
    616   // collection.
    617 
    618   if (!sql::Recovery::Recovered(recovery.Pass())) {
    619     RecordRecoveryEvent(RECOVERY_EVENT_FAILED_COMMIT);
    620     return;
    621   }
    622 
    623   // Track the size of the recovered database relative to the size of
    624   // the input database.  The size should almost always be smaller,
    625   // unless the input database was empty to start with.  If the
    626   // percentage results are very low, something is awry.
    627   int64 final_size = 0;
    628   if (original_size > 0 &&
    629       base::GetFileSize(db_path, &final_size) &&
    630       final_size > 0) {
    631     int percentage = static_cast<int>(original_size * 100 / final_size);
    632     UMA_HISTOGRAM_PERCENTAGE("History.FaviconsRecoveredPercentage",
    633                              std::max(100, percentage));
    634   }
    635 
    636   // Using 10,000 because these cases mostly care about "none
    637   // recovered" and "lots recovered".  More than 10,000 rows recovered
    638   // probably means there's something wrong with the profile.
    639   UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsFavicons",
    640                              favicons_rows_recovered);
    641   UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsFaviconBitmaps",
    642                              favicon_bitmaps_rows_recovered);
    643   UMA_HISTOGRAM_COUNTS_10000("History.FaviconsRecoveredRowsIconMapping",
    644                              icon_mapping_rows_recovered);
    645 
    646   if (version == 6) {
    647     RecordRecoveryEvent(RECOVERY_EVENT_RECOVERED_VERSION6);
    648   } else {
    649     RecordRecoveryEvent(RECOVERY_EVENT_RECOVERED);
    650   }
    651 }
    652 
    653 void DatabaseErrorCallback(sql::Connection* db,
    654                            const base::FilePath& db_path,
    655                            size_t startup_kb,
    656                            int extended_error,
    657                            sql::Statement* stmt) {
    658   // TODO(shess): Assert that this is running on a safe thread.
    659   // AFAICT, should be the history thread, but at this level I can't
    660   // see how to reach that.
    661 
    662   // TODO(shess): For now, don't report on beta or stable so as not to
    663   // overwhelm the crash server.  Once the big fish are fried,
    664   // consider reporting at a reduced rate on the bigger channels.
    665   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
    666   if (channel != chrome::VersionInfo::CHANNEL_STABLE &&
    667       channel != chrome::VersionInfo::CHANNEL_BETA) {
    668     GenerateDiagnostics(db, startup_kb, extended_error);
    669   }
    670 
    671   // Attempt to recover corrupt databases.
    672   int error = (extended_error & 0xFF);
    673   if (error == SQLITE_CORRUPT ||
    674       error == SQLITE_CANTOPEN ||
    675       error == SQLITE_NOTADB) {
    676     RecoverDatabaseOrRaze(db, db_path);
    677   }
    678 
    679   // The default handling is to assert on debug and to ignore on release.
    680   if (!sql::Connection::ShouldIgnoreSqliteError(extended_error))
    681     DLOG(FATAL) << db->GetErrorMessage();
    682 }
    683 
    684 }  // namespace
    685 
    686 namespace history {
    687 
    688 ThumbnailDatabase::IconMappingEnumerator::IconMappingEnumerator() {
    689 }
    690 
    691 ThumbnailDatabase::IconMappingEnumerator::~IconMappingEnumerator() {
    692 }
    693 
    694 bool ThumbnailDatabase::IconMappingEnumerator::GetNextIconMapping(
    695     IconMapping* icon_mapping) {
    696   if (!statement_.Step())
    697     return false;
    698   FillIconMapping(statement_, GURL(statement_.ColumnString(4)), icon_mapping);
    699   return true;
    700 }
    701 
    702 ThumbnailDatabase::ThumbnailDatabase() {
    703 }
    704 
    705 ThumbnailDatabase::~ThumbnailDatabase() {
    706   // The DBCloseScoper will delete the DB and the cache.
    707 }
    708 
    709 sql::InitStatus ThumbnailDatabase::Init(const base::FilePath& db_name) {
    710   // TODO(shess): Consider separating database open from schema setup.
    711   // With that change, this code could Raze() from outside the
    712   // transaction, rather than needing RazeAndClose() in InitImpl().
    713 
    714   // Retry failed setup in case the recovery system fixed things.
    715   const size_t kAttempts = 2;
    716 
    717   sql::InitStatus status = sql::INIT_FAILURE;
    718   for (size_t i = 0; i < kAttempts; ++i) {
    719     status = InitImpl(db_name);
    720     if (status == sql::INIT_OK)
    721       return status;
    722 
    723     meta_table_.Reset();
    724     db_.Close();
    725   }
    726   return status;
    727 }
    728 
    729 void ThumbnailDatabase::ComputeDatabaseMetrics() {
    730   sql::Statement favicon_count(
    731       db_.GetCachedStatement(SQL_FROM_HERE, "SELECT COUNT(*) FROM favicons"));
    732   UMA_HISTOGRAM_COUNTS_10000(
    733       "History.NumFaviconsInDB",
    734       favicon_count.Step() ? favicon_count.ColumnInt(0) : 0);
    735 }
    736 
    737 void ThumbnailDatabase::BeginTransaction() {
    738   db_.BeginTransaction();
    739 }
    740 
    741 void ThumbnailDatabase::CommitTransaction() {
    742   db_.CommitTransaction();
    743 }
    744 
    745 void ThumbnailDatabase::RollbackTransaction() {
    746   db_.RollbackTransaction();
    747 }
    748 
    749 void ThumbnailDatabase::Vacuum() {
    750   DCHECK(db_.transaction_nesting() == 0) <<
    751       "Can not have a transaction when vacuuming.";
    752   ignore_result(db_.Execute("VACUUM"));
    753 }
    754 
    755 void ThumbnailDatabase::TrimMemory(bool aggressively) {
    756   db_.TrimMemory(aggressively);
    757 }
    758 
    759 bool ThumbnailDatabase::GetFaviconBitmapIDSizes(
    760     chrome::FaviconID icon_id,
    761     std::vector<FaviconBitmapIDSize>* bitmap_id_sizes) {
    762   DCHECK(icon_id);
    763   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    764       "SELECT id, width, height FROM favicon_bitmaps WHERE icon_id=?"));
    765   statement.BindInt64(0, icon_id);
    766 
    767   bool result = false;
    768   while (statement.Step()) {
    769     result = true;
    770     if (!bitmap_id_sizes)
    771       return result;
    772 
    773     FaviconBitmapIDSize bitmap_id_size;
    774     bitmap_id_size.bitmap_id = statement.ColumnInt64(0);
    775     bitmap_id_size.pixel_size = gfx::Size(statement.ColumnInt(1),
    776                                           statement.ColumnInt(2));
    777     bitmap_id_sizes->push_back(bitmap_id_size);
    778   }
    779   return result;
    780 }
    781 
    782 bool ThumbnailDatabase::GetFaviconBitmaps(
    783     chrome::FaviconID icon_id,
    784     std::vector<FaviconBitmap>* favicon_bitmaps) {
    785   DCHECK(icon_id);
    786   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    787       "SELECT id, last_updated, image_data, width, height FROM favicon_bitmaps "
    788       "WHERE icon_id=?"));
    789   statement.BindInt64(0, icon_id);
    790 
    791   bool result = false;
    792   while (statement.Step()) {
    793     result = true;
    794     if (!favicon_bitmaps)
    795       return result;
    796 
    797     FaviconBitmap favicon_bitmap;
    798     favicon_bitmap.bitmap_id = statement.ColumnInt64(0);
    799     favicon_bitmap.icon_id = icon_id;
    800     favicon_bitmap.last_updated =
    801         base::Time::FromInternalValue(statement.ColumnInt64(1));
    802     if (statement.ColumnByteLength(2) > 0) {
    803       scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes());
    804       statement.ColumnBlobAsVector(2, &data->data());
    805       favicon_bitmap.bitmap_data = data;
    806     }
    807     favicon_bitmap.pixel_size = gfx::Size(statement.ColumnInt(3),
    808                                           statement.ColumnInt(4));
    809     favicon_bitmaps->push_back(favicon_bitmap);
    810   }
    811   return result;
    812 }
    813 
    814 bool ThumbnailDatabase::GetFaviconBitmap(
    815     FaviconBitmapID bitmap_id,
    816     base::Time* last_updated,
    817     scoped_refptr<base::RefCountedMemory>* png_icon_data,
    818     gfx::Size* pixel_size) {
    819   DCHECK(bitmap_id);
    820   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    821       "SELECT last_updated, image_data, width, height FROM favicon_bitmaps "
    822       "WHERE id=?"));
    823   statement.BindInt64(0, bitmap_id);
    824 
    825   if (!statement.Step())
    826     return false;
    827 
    828   if (last_updated)
    829     *last_updated = base::Time::FromInternalValue(statement.ColumnInt64(0));
    830 
    831   if (png_icon_data && statement.ColumnByteLength(1) > 0) {
    832     scoped_refptr<base::RefCountedBytes> data(new base::RefCountedBytes());
    833     statement.ColumnBlobAsVector(1, &data->data());
    834     *png_icon_data = data;
    835   }
    836 
    837   if (pixel_size) {
    838     *pixel_size = gfx::Size(statement.ColumnInt(2),
    839                             statement.ColumnInt(3));
    840   }
    841   return true;
    842 }
    843 
    844 FaviconBitmapID ThumbnailDatabase::AddFaviconBitmap(
    845     chrome::FaviconID icon_id,
    846     const scoped_refptr<base::RefCountedMemory>& icon_data,
    847     base::Time time,
    848     const gfx::Size& pixel_size) {
    849   DCHECK(icon_id);
    850   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    851       "INSERT INTO favicon_bitmaps (icon_id, image_data, last_updated, width, "
    852       "height) VALUES (?, ?, ?, ?, ?)"));
    853   statement.BindInt64(0, icon_id);
    854   if (icon_data.get() && icon_data->size()) {
    855     statement.BindBlob(1, icon_data->front(),
    856                        static_cast<int>(icon_data->size()));
    857   } else {
    858     statement.BindNull(1);
    859   }
    860   statement.BindInt64(2, time.ToInternalValue());
    861   statement.BindInt(3, pixel_size.width());
    862   statement.BindInt(4, pixel_size.height());
    863 
    864   if (!statement.Run())
    865     return 0;
    866   return db_.GetLastInsertRowId();
    867 }
    868 
    869 bool ThumbnailDatabase::SetFaviconBitmap(
    870     FaviconBitmapID bitmap_id,
    871     scoped_refptr<base::RefCountedMemory> bitmap_data,
    872     base::Time time) {
    873   DCHECK(bitmap_id);
    874   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    875       "UPDATE favicon_bitmaps SET image_data=?, last_updated=? WHERE id=?"));
    876   if (bitmap_data.get() && bitmap_data->size()) {
    877     statement.BindBlob(0, bitmap_data->front(),
    878                        static_cast<int>(bitmap_data->size()));
    879   } else {
    880     statement.BindNull(0);
    881   }
    882   statement.BindInt64(1, time.ToInternalValue());
    883   statement.BindInt64(2, bitmap_id);
    884 
    885   return statement.Run();
    886 }
    887 
    888 bool ThumbnailDatabase::SetFaviconBitmapLastUpdateTime(
    889     FaviconBitmapID bitmap_id,
    890     base::Time time) {
    891   DCHECK(bitmap_id);
    892   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    893       "UPDATE favicon_bitmaps SET last_updated=? WHERE id=?"));
    894   statement.BindInt64(0, time.ToInternalValue());
    895   statement.BindInt64(1, bitmap_id);
    896   return statement.Run();
    897 }
    898 
    899 bool ThumbnailDatabase::DeleteFaviconBitmap(FaviconBitmapID bitmap_id) {
    900   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    901       "DELETE FROM favicon_bitmaps WHERE id=?"));
    902   statement.BindInt64(0, bitmap_id);
    903   return statement.Run();
    904 }
    905 
    906 bool ThumbnailDatabase::SetFaviconOutOfDate(chrome::FaviconID icon_id) {
    907   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    908       "UPDATE favicon_bitmaps SET last_updated=? WHERE icon_id=?"));
    909   statement.BindInt64(0, 0);
    910   statement.BindInt64(1, icon_id);
    911 
    912   return statement.Run();
    913 }
    914 
    915 chrome::FaviconID ThumbnailDatabase::GetFaviconIDForFaviconURL(
    916     const GURL& icon_url,
    917     int required_icon_type,
    918     chrome::IconType* icon_type) {
    919   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    920       "SELECT id, icon_type FROM favicons WHERE url=? AND (icon_type & ? > 0) "
    921       "ORDER BY icon_type DESC"));
    922   statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url));
    923   statement.BindInt(1, required_icon_type);
    924 
    925   if (!statement.Step())
    926     return 0;  // not cached
    927 
    928   if (icon_type)
    929     *icon_type = static_cast<chrome::IconType>(statement.ColumnInt(1));
    930   return statement.ColumnInt64(0);
    931 }
    932 
    933 bool ThumbnailDatabase::GetFaviconHeader(chrome::FaviconID icon_id,
    934                                          GURL* icon_url,
    935                                          chrome::IconType* icon_type) {
    936   DCHECK(icon_id);
    937 
    938   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    939       "SELECT url, icon_type FROM favicons WHERE id=?"));
    940   statement.BindInt64(0, icon_id);
    941 
    942   if (!statement.Step())
    943     return false;  // No entry for the id.
    944 
    945   if (icon_url)
    946     *icon_url = GURL(statement.ColumnString(0));
    947   if (icon_type)
    948     *icon_type = static_cast<chrome::IconType>(statement.ColumnInt(1));
    949 
    950   return true;
    951 }
    952 
    953 chrome::FaviconID ThumbnailDatabase::AddFavicon(
    954     const GURL& icon_url,
    955     chrome::IconType icon_type) {
    956 
    957   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
    958       "INSERT INTO favicons (url, icon_type) VALUES (?, ?)"));
    959   statement.BindString(0, URLDatabase::GURLToDatabaseURL(icon_url));
    960   statement.BindInt(1, icon_type);
    961 
    962   if (!statement.Run())
    963     return 0;
    964   return db_.GetLastInsertRowId();
    965 }
    966 
    967 chrome::FaviconID ThumbnailDatabase::AddFavicon(
    968     const GURL& icon_url,
    969     chrome::IconType icon_type,
    970     const scoped_refptr<base::RefCountedMemory>& icon_data,
    971     base::Time time,
    972     const gfx::Size& pixel_size) {
    973   chrome::FaviconID icon_id = AddFavicon(icon_url, icon_type);
    974   if (!icon_id || !AddFaviconBitmap(icon_id, icon_data, time, pixel_size))
    975     return 0;
    976 
    977   return icon_id;
    978 }
    979 
    980 bool ThumbnailDatabase::DeleteFavicon(chrome::FaviconID id) {
    981   sql::Statement statement;
    982   statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
    983       "DELETE FROM favicons WHERE id = ?"));
    984   statement.BindInt64(0, id);
    985   if (!statement.Run())
    986     return false;
    987 
    988   statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
    989       "DELETE FROM favicon_bitmaps WHERE icon_id = ?"));
    990   statement.BindInt64(0, id);
    991   return statement.Run();
    992 }
    993 
    994 bool ThumbnailDatabase::GetIconMappingsForPageURL(
    995     const GURL& page_url,
    996     int required_icon_types,
    997     std::vector<IconMapping>* filtered_mapping_data) {
    998   std::vector<IconMapping> mapping_data;
    999   if (!GetIconMappingsForPageURL(page_url, &mapping_data))
   1000     return false;
   1001 
   1002   bool result = false;
   1003   for (std::vector<IconMapping>::iterator m = mapping_data.begin();
   1004        m != mapping_data.end(); ++m) {
   1005     if (m->icon_type & required_icon_types) {
   1006       result = true;
   1007       if (!filtered_mapping_data)
   1008         return result;
   1009 
   1010       // Restrict icon type of subsequent matches to |m->icon_type|.
   1011       // |m->icon_type| is the largest IconType in |mapping_data| because
   1012       // |mapping_data| is sorted in descending order of IconType.
   1013       required_icon_types = m->icon_type;
   1014 
   1015       filtered_mapping_data->push_back(*m);
   1016     }
   1017   }
   1018   return result;
   1019 }
   1020 
   1021 bool ThumbnailDatabase::GetIconMappingsForPageURL(
   1022     const GURL& page_url,
   1023     std::vector<IconMapping>* mapping_data) {
   1024   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
   1025       "SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type, "
   1026       "favicons.url "
   1027       "FROM icon_mapping "
   1028       "INNER JOIN favicons "
   1029       "ON icon_mapping.icon_id = favicons.id "
   1030       "WHERE icon_mapping.page_url=? "
   1031       "ORDER BY favicons.icon_type DESC"));
   1032   statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
   1033 
   1034   bool result = false;
   1035   while (statement.Step()) {
   1036     result = true;
   1037     if (!mapping_data)
   1038       return result;
   1039 
   1040     IconMapping icon_mapping;
   1041     FillIconMapping(statement, page_url, &icon_mapping);
   1042     mapping_data->push_back(icon_mapping);
   1043   }
   1044   return result;
   1045 }
   1046 
   1047 IconMappingID ThumbnailDatabase::AddIconMapping(const GURL& page_url,
   1048                                                 chrome::FaviconID icon_id) {
   1049   const char kSql[] =
   1050       "INSERT INTO icon_mapping (page_url, icon_id) VALUES (?, ?)";
   1051   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE, kSql));
   1052   statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
   1053   statement.BindInt64(1, icon_id);
   1054 
   1055   if (!statement.Run())
   1056     return 0;
   1057 
   1058   return db_.GetLastInsertRowId();
   1059 }
   1060 
   1061 bool ThumbnailDatabase::UpdateIconMapping(IconMappingID mapping_id,
   1062                                           chrome::FaviconID icon_id) {
   1063   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
   1064       "UPDATE icon_mapping SET icon_id=? WHERE id=?"));
   1065   statement.BindInt64(0, icon_id);
   1066   statement.BindInt64(1, mapping_id);
   1067 
   1068   return statement.Run();
   1069 }
   1070 
   1071 bool ThumbnailDatabase::DeleteIconMappings(const GURL& page_url) {
   1072   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
   1073       "DELETE FROM icon_mapping WHERE page_url = ?"));
   1074   statement.BindString(0, URLDatabase::GURLToDatabaseURL(page_url));
   1075 
   1076   return statement.Run();
   1077 }
   1078 
   1079 bool ThumbnailDatabase::DeleteIconMapping(IconMappingID mapping_id) {
   1080   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
   1081       "DELETE FROM icon_mapping WHERE id=?"));
   1082   statement.BindInt64(0, mapping_id);
   1083 
   1084   return statement.Run();
   1085 }
   1086 
   1087 bool ThumbnailDatabase::HasMappingFor(chrome::FaviconID id) {
   1088   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
   1089       "SELECT id FROM icon_mapping "
   1090       "WHERE icon_id=?"));
   1091   statement.BindInt64(0, id);
   1092 
   1093   return statement.Step();
   1094 }
   1095 
   1096 bool ThumbnailDatabase::CloneIconMappings(const GURL& old_page_url,
   1097                                           const GURL& new_page_url) {
   1098   sql::Statement statement(db_.GetCachedStatement(SQL_FROM_HERE,
   1099       "SELECT icon_id FROM icon_mapping "
   1100       "WHERE page_url=?"));
   1101   if (!statement.is_valid())
   1102     return false;
   1103 
   1104   // Do nothing if there are existing bindings
   1105   statement.BindString(0, URLDatabase::GURLToDatabaseURL(new_page_url));
   1106   if (statement.Step())
   1107     return true;
   1108 
   1109   statement.Assign(db_.GetCachedStatement(SQL_FROM_HERE,
   1110       "INSERT INTO icon_mapping (page_url, icon_id) "
   1111         "SELECT ?, icon_id FROM icon_mapping "
   1112         "WHERE page_url = ?"));
   1113 
   1114   statement.BindString(0, URLDatabase::GURLToDatabaseURL(new_page_url));
   1115   statement.BindString(1, URLDatabase::GURLToDatabaseURL(old_page_url));
   1116   return statement.Run();
   1117 }
   1118 
   1119 bool ThumbnailDatabase::InitIconMappingEnumerator(
   1120     chrome::IconType type,
   1121     IconMappingEnumerator* enumerator) {
   1122   DCHECK(!enumerator->statement_.is_valid());
   1123   enumerator->statement_.Assign(db_.GetCachedStatement(
   1124       SQL_FROM_HERE,
   1125       "SELECT icon_mapping.id, icon_mapping.icon_id, favicons.icon_type, "
   1126              "favicons.url, icon_mapping.page_url "
   1127          "FROM icon_mapping JOIN favicons ON ("
   1128               "icon_mapping.icon_id = favicons.id) "
   1129          "WHERE favicons.icon_type = ?"));
   1130   enumerator->statement_.BindInt(0, type);
   1131   return enumerator->statement_.is_valid();
   1132 }
   1133 
   1134 bool ThumbnailDatabase::RetainDataForPageUrls(
   1135     const std::vector<GURL>& urls_to_keep) {
   1136   sql::Transaction transaction(&db_);
   1137   if (!transaction.Begin())
   1138     return false;
   1139 
   1140   // temp.icon_id_mapping generates new icon ids as consecutive
   1141   // integers starting from 1, and maps them to the old icon ids.
   1142   {
   1143     const char kIconMappingCreate[] =
   1144         "CREATE TEMP TABLE icon_id_mapping "
   1145         "("
   1146         "new_icon_id INTEGER PRIMARY KEY,"
   1147         "old_icon_id INTEGER NOT NULL UNIQUE"
   1148         ")";
   1149     if (!db_.Execute(kIconMappingCreate))
   1150       return false;
   1151 
   1152     // Insert the icon ids for retained urls, skipping duplicates.
   1153     const char kIconMappingSql[] =
   1154         "INSERT OR IGNORE INTO temp.icon_id_mapping (old_icon_id) "
   1155         "SELECT icon_id FROM icon_mapping WHERE page_url = ?";
   1156     sql::Statement statement(db_.GetUniqueStatement(kIconMappingSql));
   1157     for (std::vector<GURL>::const_iterator
   1158              i = urls_to_keep.begin(); i != urls_to_keep.end(); ++i) {
   1159       statement.BindString(0, URLDatabase::GURLToDatabaseURL(*i));
   1160       if (!statement.Run())
   1161         return false;
   1162       statement.Reset(true);
   1163     }
   1164   }
   1165 
   1166   const char kRenameIconMappingTable[] =
   1167       "ALTER TABLE icon_mapping RENAME TO old_icon_mapping";
   1168   const char kCopyIconMapping[] =
   1169       "INSERT INTO icon_mapping (page_url, icon_id) "
   1170       "SELECT old.page_url, mapping.new_icon_id "
   1171       "FROM old_icon_mapping AS old "
   1172       "JOIN temp.icon_id_mapping AS mapping "
   1173       "ON (old.icon_id = mapping.old_icon_id)";
   1174   const char kDropOldIconMappingTable[] = "DROP TABLE old_icon_mapping";
   1175 
   1176   const char kRenameFaviconsTable[] =
   1177       "ALTER TABLE favicons RENAME TO old_favicons";
   1178   const char kCopyFavicons[] =
   1179       "INSERT INTO favicons (id, url, icon_type) "
   1180       "SELECT mapping.new_icon_id, old.url, old.icon_type "
   1181       "FROM old_favicons AS old "
   1182       "JOIN temp.icon_id_mapping AS mapping "
   1183       "ON (old.id = mapping.old_icon_id)";
   1184   const char kDropOldFaviconsTable[] = "DROP TABLE old_favicons";
   1185 
   1186   const char kRenameFaviconBitmapsTable[] =
   1187       "ALTER TABLE favicon_bitmaps RENAME TO old_favicon_bitmaps";
   1188   const char kCopyFaviconBitmaps[] =
   1189       "INSERT INTO favicon_bitmaps "
   1190       "  (icon_id, last_updated, image_data, width, height) "
   1191       "SELECT mapping.new_icon_id, old.last_updated, "
   1192       "    old.image_data, old.width, old.height "
   1193       "FROM old_favicon_bitmaps AS old "
   1194       "JOIN temp.icon_id_mapping AS mapping "
   1195       "ON (old.icon_id = mapping.old_icon_id)";
   1196   const char kDropOldFaviconBitmapsTable[] =
   1197       "DROP TABLE old_favicon_bitmaps";
   1198 
   1199   // Rename existing tables to new location.
   1200   if (!db_.Execute(kRenameIconMappingTable) ||
   1201       !db_.Execute(kRenameFaviconsTable) ||
   1202       !db_.Execute(kRenameFaviconBitmapsTable)) {
   1203     return false;
   1204   }
   1205 
   1206   // Initialize the replacement tables.  At this point the old indices
   1207   // still exist (pointing to the old_* tables), so do not initialize
   1208   // the indices.
   1209   if (!InitTables(&db_))
   1210     return false;
   1211 
   1212   // Copy all of the data over.
   1213   if (!db_.Execute(kCopyIconMapping) ||
   1214       !db_.Execute(kCopyFavicons) ||
   1215       !db_.Execute(kCopyFaviconBitmaps)) {
   1216     return false;
   1217   }
   1218 
   1219   // Drop the old_* tables, which also drops the indices.
   1220   if (!db_.Execute(kDropOldIconMappingTable) ||
   1221       !db_.Execute(kDropOldFaviconsTable) ||
   1222       !db_.Execute(kDropOldFaviconBitmapsTable)) {
   1223     return false;
   1224   }
   1225 
   1226   // Recreate the indices.
   1227   // TODO(shess): UNIQUE indices could fail due to duplication.  This
   1228   // could happen in case of corruption.
   1229   if (!InitIndices(&db_))
   1230     return false;
   1231 
   1232   const char kIconMappingDrop[] = "DROP TABLE temp.icon_id_mapping";
   1233   if (!db_.Execute(kIconMappingDrop))
   1234     return false;
   1235 
   1236   return transaction.Commit();
   1237 }
   1238 
   1239 sql::InitStatus ThumbnailDatabase::OpenDatabase(sql::Connection* db,
   1240                                                 const base::FilePath& db_name) {
   1241   size_t startup_kb = 0;
   1242   int64 size_64;
   1243   if (base::GetFileSize(db_name, &size_64))
   1244     startup_kb = static_cast<size_t>(size_64 / 1024);
   1245 
   1246   db->set_histogram_tag("Thumbnail");
   1247   db->set_error_callback(base::Bind(&DatabaseErrorCallback,
   1248                                     db, db_name, startup_kb));
   1249 
   1250   // Thumbnails db now only stores favicons, so we don't need that big a page
   1251   // size or cache.
   1252   db->set_page_size(2048);
   1253   db->set_cache_size(32);
   1254 
   1255   // Run the database in exclusive mode. Nobody else should be accessing the
   1256   // database while we're running, and this will give somewhat improved perf.
   1257   db->set_exclusive_locking();
   1258 
   1259   if (!db->Open(db_name))
   1260     return sql::INIT_FAILURE;
   1261 
   1262   return sql::INIT_OK;
   1263 }
   1264 
   1265 sql::InitStatus ThumbnailDatabase::InitImpl(const base::FilePath& db_name) {
   1266   sql::InitStatus status = OpenDatabase(&db_, db_name);
   1267   if (status != sql::INIT_OK)
   1268     return status;
   1269 
   1270   // Clear databases which are too old to process.
   1271   DCHECK_LT(kDeprecatedVersionNumber, kCurrentVersionNumber);
   1272   sql::MetaTable::RazeIfDeprecated(&db_, kDeprecatedVersionNumber);
   1273 
   1274   // TODO(shess): Sqlite.Version.Thumbnail shows versions 22, 23, and
   1275   // 25.  Future versions are not destroyed because that could lead to
   1276   // data loss if the profile is opened by a later channel, but
   1277   // perhaps a heuristic like >kCurrentVersionNumber+3 could be used.
   1278 
   1279   // Scope initialization in a transaction so we can't be partially initialized.
   1280   sql::Transaction transaction(&db_);
   1281   if (!transaction.Begin())
   1282     return sql::INIT_FAILURE;
   1283 
   1284   // TODO(shess): Failing Begin() implies that something serious is
   1285   // wrong with the database.  Raze() may be in order.
   1286 
   1287 #if defined(OS_MACOSX)
   1288   // Exclude the thumbnails file from backups.
   1289   base::mac::SetFileBackupExclusion(db_name);
   1290 #endif
   1291 
   1292   // thumbnails table has been obsolete for a long time, remove any
   1293   // detrious.
   1294   ignore_result(db_.Execute("DROP TABLE IF EXISTS thumbnails"));
   1295 
   1296   // At some point, operations involving temporary tables weren't done
   1297   // atomically and users have been stranded.  Drop those tables and
   1298   // move on.
   1299   // TODO(shess): Prove it?  Audit all cases and see if it's possible
   1300   // that this implies non-atomic update, and should thus be handled
   1301   // via the corruption handler.
   1302   ignore_result(db_.Execute("DROP TABLE IF EXISTS temp_favicons"));
   1303   ignore_result(db_.Execute("DROP TABLE IF EXISTS temp_favicon_bitmaps"));
   1304   ignore_result(db_.Execute("DROP TABLE IF EXISTS temp_icon_mapping"));
   1305 
   1306   // Create the tables.
   1307   if (!meta_table_.Init(&db_, kCurrentVersionNumber,
   1308                         kCompatibleVersionNumber) ||
   1309       !InitTables(&db_) ||
   1310       !InitIndices(&db_)) {
   1311     return sql::INIT_FAILURE;
   1312   }
   1313 
   1314   // Version check. We should not encounter a database too old for us to handle
   1315   // in the wild, so we try to continue in that case.
   1316   if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) {
   1317     LOG(WARNING) << "Thumbnail database is too new.";
   1318     return sql::INIT_TOO_NEW;
   1319   }
   1320 
   1321   int cur_version = meta_table_.GetVersionNumber();
   1322 
   1323   if (!db_.DoesColumnExist("favicons", "icon_type")) {
   1324     LOG(ERROR) << "Raze because of missing favicon.icon_type";
   1325     RecordInvalidStructure(STRUCTURE_EVENT_VERSION4);
   1326 
   1327     db_.RazeAndClose();
   1328     return sql::INIT_FAILURE;
   1329   }
   1330 
   1331   if (cur_version < 7 && !db_.DoesColumnExist("favicons", "sizes")) {
   1332     LOG(ERROR) << "Raze because of missing favicon.sizes";
   1333     RecordInvalidStructure(STRUCTURE_EVENT_VERSION5);
   1334 
   1335     db_.RazeAndClose();
   1336     return sql::INIT_FAILURE;
   1337   }
   1338 
   1339   if (cur_version == 5) {
   1340     ++cur_version;
   1341     if (!UpgradeToVersion6())
   1342       return CantUpgradeToVersion(cur_version);
   1343   }
   1344 
   1345   if (cur_version == 6) {
   1346     ++cur_version;
   1347     if (!UpgradeToVersion7())
   1348       return CantUpgradeToVersion(cur_version);
   1349   }
   1350 
   1351   LOG_IF(WARNING, cur_version < kCurrentVersionNumber) <<
   1352       "Thumbnail database version " << cur_version << " is too old to handle.";
   1353 
   1354   // Initialization is complete.
   1355   if (!transaction.Commit())
   1356     return sql::INIT_FAILURE;
   1357 
   1358   // Raze the database if the structure of the favicons database is not what
   1359   // it should be. This error cannot be detected via the SQL error code because
   1360   // the error code for running SQL statements against a database with missing
   1361   // columns is SQLITE_ERROR which is not unique enough to act upon.
   1362   // TODO(pkotwicz): Revisit this in M27 and see if the razing can be removed.
   1363   // (crbug.com/166453)
   1364   if (IsFaviconDBStructureIncorrect()) {
   1365     LOG(ERROR) << "Raze because of invalid favicon db structure.";
   1366     RecordInvalidStructure(STRUCTURE_EVENT_FAVICON);
   1367 
   1368     db_.RazeAndClose();
   1369     return sql::INIT_FAILURE;
   1370   }
   1371 
   1372   return sql::INIT_OK;
   1373 }
   1374 
   1375 sql::InitStatus ThumbnailDatabase::CantUpgradeToVersion(int cur_version) {
   1376   LOG(WARNING) << "Unable to update to thumbnail database to version " <<
   1377                cur_version << ".";
   1378   db_.Close();
   1379   return sql::INIT_FAILURE;
   1380 }
   1381 
   1382 bool ThumbnailDatabase::UpgradeToVersion6() {
   1383   // Move bitmap data from favicons to favicon_bitmaps.
   1384   bool success =
   1385       db_.Execute("INSERT INTO favicon_bitmaps (icon_id, last_updated, "
   1386                   "image_data, width, height)"
   1387                   "SELECT id, last_updated, image_data, 0, 0 FROM favicons") &&
   1388       db_.Execute("CREATE TABLE temp_favicons ("
   1389                   "id INTEGER PRIMARY KEY,"
   1390                   "url LONGVARCHAR NOT NULL,"
   1391                   "icon_type INTEGER DEFAULT 1,"
   1392                   // default icon_type FAVICON to be consistent with
   1393                   // past migration.
   1394                   "sizes LONGVARCHAR)") &&
   1395       db_.Execute("INSERT INTO temp_favicons (id, url, icon_type) "
   1396                   "SELECT id, url, icon_type FROM favicons") &&
   1397       db_.Execute("DROP TABLE favicons") &&
   1398       db_.Execute("ALTER TABLE temp_favicons RENAME TO favicons");
   1399   // NOTE(shess): v7 will re-create the index.
   1400   if (!success)
   1401     return false;
   1402 
   1403   meta_table_.SetVersionNumber(6);
   1404   meta_table_.SetCompatibleVersionNumber(std::min(6, kCompatibleVersionNumber));
   1405   return true;
   1406 }
   1407 
   1408 bool ThumbnailDatabase::UpgradeToVersion7() {
   1409   // Sizes column was never used, remove it.
   1410   bool success =
   1411       db_.Execute("CREATE TABLE temp_favicons ("
   1412                   "id INTEGER PRIMARY KEY,"
   1413                   "url LONGVARCHAR NOT NULL,"
   1414                   // default icon_type FAVICON to be consistent with
   1415                   // past migration.
   1416                   "icon_type INTEGER DEFAULT 1)") &&
   1417       db_.Execute("INSERT INTO temp_favicons (id, url, icon_type) "
   1418                   "SELECT id, url, icon_type FROM favicons") &&
   1419       db_.Execute("DROP TABLE favicons") &&
   1420       db_.Execute("ALTER TABLE temp_favicons RENAME TO favicons") &&
   1421       db_.Execute("CREATE INDEX IF NOT EXISTS favicons_url ON favicons(url)");
   1422 
   1423   if (!success)
   1424     return false;
   1425 
   1426   meta_table_.SetVersionNumber(7);
   1427   meta_table_.SetCompatibleVersionNumber(std::min(7, kCompatibleVersionNumber));
   1428   return true;
   1429 }
   1430 
   1431 bool ThumbnailDatabase::IsFaviconDBStructureIncorrect() {
   1432   return !db_.IsSQLValid("SELECT id, url, icon_type FROM favicons");
   1433 }
   1434 
   1435 }  // namespace history
   1436