Home | History | Annotate | Download | only in appcache
      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 "content/browser/appcache/appcache_database.h"
      6 
      7 #include "base/auto_reset.h"
      8 #include "base/bind.h"
      9 #include "base/command_line.h"
     10 #include "base/files/file_util.h"
     11 #include "base/logging.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "content/browser/appcache/appcache_entry.h"
     14 #include "content/browser/appcache/appcache_histograms.h"
     15 #include "sql/connection.h"
     16 #include "sql/error_delegate_util.h"
     17 #include "sql/meta_table.h"
     18 #include "sql/statement.h"
     19 #include "sql/transaction.h"
     20 
     21 namespace content {
     22 
     23 // Schema -------------------------------------------------------------------
     24 namespace {
     25 
     26 #if defined(APPCACHE_USE_SIMPLE_CACHE)
     27 const int kCurrentVersion = 6;
     28 const int kCompatibleVersion = 6;
     29 #else
     30 const int kCurrentVersion = 5;
     31 const int kCompatibleVersion = 5;
     32 #endif
     33 
     34 // A mechanism to run experiments that may affect in data being persisted
     35 // in different ways such that when the experiment is toggled on/off via
     36 // cmd line flags, the database gets reset. The active flags are stored at
     37 // the time of database creation and compared when reopening. If different
     38 // the database is reset.
     39 const char kExperimentFlagsKey[] = "ExperimentFlags";
     40 
     41 const char kGroupsTable[] = "Groups";
     42 const char kCachesTable[] = "Caches";
     43 const char kEntriesTable[] = "Entries";
     44 const char kNamespacesTable[] = "Namespaces";
     45 const char kOnlineWhiteListsTable[] = "OnlineWhiteLists";
     46 const char kDeletableResponseIdsTable[] = "DeletableResponseIds";
     47 
     48 struct TableInfo {
     49   const char* table_name;
     50   const char* columns;
     51 };
     52 
     53 struct IndexInfo {
     54   const char* index_name;
     55   const char* table_name;
     56   const char* columns;
     57   bool unique;
     58 };
     59 
     60 const TableInfo kTables[] = {
     61   { kGroupsTable,
     62     "(group_id INTEGER PRIMARY KEY,"
     63     " origin TEXT,"
     64     " manifest_url TEXT,"
     65     " creation_time INTEGER,"
     66     " last_access_time INTEGER)" },
     67 
     68   { kCachesTable,
     69     "(cache_id INTEGER PRIMARY KEY,"
     70     " group_id INTEGER,"
     71     " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1)),"
     72     " update_time INTEGER,"
     73     " cache_size INTEGER)" },  // intentionally not normalized
     74 
     75   { kEntriesTable,
     76     "(cache_id INTEGER,"
     77     " url TEXT,"
     78     " flags INTEGER,"
     79     " response_id INTEGER,"
     80     " response_size INTEGER)" },
     81 
     82   { kNamespacesTable,
     83     "(cache_id INTEGER,"
     84     " origin TEXT,"  // intentionally not normalized
     85     " type INTEGER,"
     86     " namespace_url TEXT,"
     87     " target_url TEXT,"
     88     " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))" },
     89 
     90   { kOnlineWhiteListsTable,
     91     "(cache_id INTEGER,"
     92     " namespace_url TEXT,"
     93     " is_pattern INTEGER CHECK(is_pattern IN (0, 1)))" },
     94 
     95   { kDeletableResponseIdsTable,
     96     "(response_id INTEGER NOT NULL)" },
     97 };
     98 
     99 const IndexInfo kIndexes[] = {
    100   { "GroupsOriginIndex",
    101     kGroupsTable,
    102     "(origin)",
    103     false },
    104 
    105   { "GroupsManifestIndex",
    106     kGroupsTable,
    107     "(manifest_url)",
    108     true },
    109 
    110   { "CachesGroupIndex",
    111     kCachesTable,
    112     "(group_id)",
    113     false },
    114 
    115   { "EntriesCacheIndex",
    116     kEntriesTable,
    117     "(cache_id)",
    118     false },
    119 
    120   { "EntriesCacheAndUrlIndex",
    121     kEntriesTable,
    122     "(cache_id, url)",
    123     true },
    124 
    125   { "EntriesResponseIdIndex",
    126     kEntriesTable,
    127     "(response_id)",
    128     true },
    129 
    130   { "NamespacesCacheIndex",
    131     kNamespacesTable,
    132     "(cache_id)",
    133     false },
    134 
    135   { "NamespacesOriginIndex",
    136     kNamespacesTable,
    137     "(origin)",
    138     false },
    139 
    140   { "NamespacesCacheAndUrlIndex",
    141     kNamespacesTable,
    142     "(cache_id, namespace_url)",
    143     true },
    144 
    145   { "OnlineWhiteListCacheIndex",
    146     kOnlineWhiteListsTable,
    147     "(cache_id)",
    148     false },
    149 
    150   { "DeletableResponsesIdIndex",
    151     kDeletableResponseIdsTable,
    152     "(response_id)",
    153     true },
    154 };
    155 
    156 const int kTableCount = ARRAYSIZE_UNSAFE(kTables);
    157 const int kIndexCount = ARRAYSIZE_UNSAFE(kIndexes);
    158 
    159 bool CreateTable(sql::Connection* db, const TableInfo& info) {
    160   std::string sql("CREATE TABLE ");
    161   sql += info.table_name;
    162   sql += info.columns;
    163   return db->Execute(sql.c_str());
    164 }
    165 
    166 bool CreateIndex(sql::Connection* db, const IndexInfo& info) {
    167   std::string sql;
    168   if (info.unique)
    169     sql += "CREATE UNIQUE INDEX ";
    170   else
    171     sql += "CREATE INDEX ";
    172   sql += info.index_name;
    173   sql += " ON ";
    174   sql += info.table_name;
    175   sql += info.columns;
    176   return db->Execute(sql.c_str());
    177 }
    178 
    179 std::string GetActiveExperimentFlags() {
    180   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
    181           kEnableExecutableHandlers))
    182     return std::string("executableHandlersEnabled");
    183   return std::string();
    184 }
    185 
    186 }  // anon namespace
    187 
    188 // AppCacheDatabase ----------------------------------------------------------
    189 
    190 AppCacheDatabase::GroupRecord::GroupRecord()
    191     : group_id(0) {
    192 }
    193 
    194 AppCacheDatabase::GroupRecord::~GroupRecord() {
    195 }
    196 
    197 AppCacheDatabase::NamespaceRecord::NamespaceRecord()
    198     : cache_id(0) {
    199 }
    200 
    201 AppCacheDatabase::NamespaceRecord::~NamespaceRecord() {
    202 }
    203 
    204 
    205 AppCacheDatabase::AppCacheDatabase(const base::FilePath& path)
    206     : db_file_path_(path),
    207       is_disabled_(false),
    208       is_recreating_(false),
    209       was_corruption_detected_(false) {
    210 }
    211 
    212 AppCacheDatabase::~AppCacheDatabase() {
    213 }
    214 
    215 void AppCacheDatabase::Disable() {
    216   VLOG(1) << "Disabling appcache database.";
    217   is_disabled_ = true;
    218   ResetConnectionAndTables();
    219 }
    220 
    221 int64 AppCacheDatabase::GetOriginUsage(const GURL& origin) {
    222   std::vector<CacheRecord> records;
    223   if (!FindCachesForOrigin(origin, &records))
    224     return 0;
    225 
    226   int64 origin_usage = 0;
    227   std::vector<CacheRecord>::const_iterator iter = records.begin();
    228   while (iter != records.end()) {
    229     origin_usage += iter->cache_size;
    230     ++iter;
    231   }
    232   return origin_usage;
    233 }
    234 
    235 bool AppCacheDatabase::GetAllOriginUsage(std::map<GURL, int64>* usage_map) {
    236   std::set<GURL> origins;
    237   if (!FindOriginsWithGroups(&origins))
    238     return false;
    239   for (std::set<GURL>::const_iterator origin = origins.begin();
    240        origin != origins.end(); ++origin) {
    241     (*usage_map)[*origin] = GetOriginUsage(*origin);
    242   }
    243   return true;
    244 }
    245 
    246 bool AppCacheDatabase::FindOriginsWithGroups(std::set<GURL>* origins) {
    247   DCHECK(origins && origins->empty());
    248   if (!LazyOpen(false))
    249     return false;
    250 
    251   const char* kSql =
    252       "SELECT DISTINCT(origin) FROM Groups";
    253 
    254   sql::Statement statement(db_->GetUniqueStatement(kSql));
    255 
    256   while (statement.Step())
    257     origins->insert(GURL(statement.ColumnString(0)));
    258 
    259   return statement.Succeeded();
    260 }
    261 
    262 bool AppCacheDatabase::FindLastStorageIds(
    263     int64* last_group_id, int64* last_cache_id, int64* last_response_id,
    264     int64* last_deletable_response_rowid) {
    265   DCHECK(last_group_id && last_cache_id && last_response_id &&
    266          last_deletable_response_rowid);
    267 
    268   *last_group_id = 0;
    269   *last_cache_id = 0;
    270   *last_response_id = 0;
    271   *last_deletable_response_rowid = 0;
    272 
    273   if (!LazyOpen(false))
    274     return false;
    275 
    276   const char* kMaxGroupIdSql = "SELECT MAX(group_id) FROM Groups";
    277   const char* kMaxCacheIdSql = "SELECT MAX(cache_id) FROM Caches";
    278   const char* kMaxResponseIdFromEntriesSql =
    279       "SELECT MAX(response_id) FROM Entries";
    280   const char* kMaxResponseIdFromDeletablesSql =
    281       "SELECT MAX(response_id) FROM DeletableResponseIds";
    282   const char* kMaxDeletableResponseRowIdSql =
    283       "SELECT MAX(rowid) FROM DeletableResponseIds";
    284   int64 max_group_id;
    285   int64 max_cache_id;
    286   int64 max_response_id_from_entries;
    287   int64 max_response_id_from_deletables;
    288   int64 max_deletable_response_rowid;
    289   if (!RunUniqueStatementWithInt64Result(kMaxGroupIdSql, &max_group_id) ||
    290       !RunUniqueStatementWithInt64Result(kMaxCacheIdSql, &max_cache_id) ||
    291       !RunUniqueStatementWithInt64Result(kMaxResponseIdFromEntriesSql,
    292                                          &max_response_id_from_entries) ||
    293       !RunUniqueStatementWithInt64Result(kMaxResponseIdFromDeletablesSql,
    294                                          &max_response_id_from_deletables) ||
    295       !RunUniqueStatementWithInt64Result(kMaxDeletableResponseRowIdSql,
    296                                          &max_deletable_response_rowid)) {
    297     return false;
    298   }
    299 
    300   *last_group_id = max_group_id;
    301   *last_cache_id = max_cache_id;
    302   *last_response_id = std::max(max_response_id_from_entries,
    303                                max_response_id_from_deletables);
    304   *last_deletable_response_rowid = max_deletable_response_rowid;
    305   return true;
    306 }
    307 
    308 bool AppCacheDatabase::FindGroup(int64 group_id, GroupRecord* record) {
    309   DCHECK(record);
    310   if (!LazyOpen(false))
    311     return false;
    312 
    313   const char* kSql =
    314       "SELECT group_id, origin, manifest_url,"
    315       "       creation_time, last_access_time"
    316       "  FROM Groups WHERE group_id = ?";
    317 
    318   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    319 
    320   statement.BindInt64(0, group_id);
    321   if (!statement.Step())
    322     return false;
    323 
    324   ReadGroupRecord(statement, record);
    325   DCHECK(record->group_id == group_id);
    326   return true;
    327 }
    328 
    329 bool AppCacheDatabase::FindGroupForManifestUrl(
    330     const GURL& manifest_url, GroupRecord* record) {
    331   DCHECK(record);
    332   if (!LazyOpen(false))
    333     return false;
    334 
    335   const char* kSql =
    336       "SELECT group_id, origin, manifest_url,"
    337       "       creation_time, last_access_time"
    338       "  FROM Groups WHERE manifest_url = ?";
    339 
    340   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    341   statement.BindString(0, manifest_url.spec());
    342 
    343   if (!statement.Step())
    344     return false;
    345 
    346   ReadGroupRecord(statement, record);
    347   DCHECK(record->manifest_url == manifest_url);
    348   return true;
    349 }
    350 
    351 bool AppCacheDatabase::FindGroupsForOrigin(
    352     const GURL& origin, std::vector<GroupRecord>* records) {
    353   DCHECK(records && records->empty());
    354   if (!LazyOpen(false))
    355     return false;
    356 
    357   const char* kSql =
    358       "SELECT group_id, origin, manifest_url,"
    359       "       creation_time, last_access_time"
    360       "   FROM Groups WHERE origin = ?";
    361 
    362   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    363   statement.BindString(0, origin.spec());
    364 
    365   while (statement.Step()) {
    366     records->push_back(GroupRecord());
    367     ReadGroupRecord(statement, &records->back());
    368     DCHECK(records->back().origin == origin);
    369   }
    370 
    371   return statement.Succeeded();
    372 }
    373 
    374 bool AppCacheDatabase::FindGroupForCache(int64 cache_id, GroupRecord* record) {
    375   DCHECK(record);
    376   if (!LazyOpen(false))
    377     return false;
    378 
    379   const char* kSql =
    380       "SELECT g.group_id, g.origin, g.manifest_url,"
    381       "       g.creation_time, g.last_access_time"
    382       "  FROM Groups g, Caches c"
    383       "  WHERE c.cache_id = ? AND c.group_id = g.group_id";
    384 
    385   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    386   statement.BindInt64(0, cache_id);
    387 
    388   if (!statement.Step())
    389     return false;
    390 
    391   ReadGroupRecord(statement, record);
    392   return true;
    393 }
    394 
    395 bool AppCacheDatabase::UpdateGroupLastAccessTime(
    396     int64 group_id, base::Time time) {
    397   if (!LazyOpen(true))
    398     return false;
    399 
    400   const char* kSql =
    401       "UPDATE Groups SET last_access_time = ? WHERE group_id = ?";
    402 
    403   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    404   statement.BindInt64(0, time.ToInternalValue());
    405   statement.BindInt64(1, group_id);
    406 
    407   return statement.Run() && db_->GetLastChangeCount();
    408 }
    409 
    410 bool AppCacheDatabase::InsertGroup(const GroupRecord* record) {
    411   if (!LazyOpen(true))
    412     return false;
    413 
    414   const char* kSql =
    415       "INSERT INTO Groups"
    416       "  (group_id, origin, manifest_url, creation_time, last_access_time)"
    417       "  VALUES(?, ?, ?, ?, ?)";
    418 
    419   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    420   statement.BindInt64(0, record->group_id);
    421   statement.BindString(1, record->origin.spec());
    422   statement.BindString(2, record->manifest_url.spec());
    423   statement.BindInt64(3, record->creation_time.ToInternalValue());
    424   statement.BindInt64(4, record->last_access_time.ToInternalValue());
    425 
    426   return statement.Run();
    427 }
    428 
    429 bool AppCacheDatabase::DeleteGroup(int64 group_id) {
    430   if (!LazyOpen(false))
    431     return false;
    432 
    433   const char* kSql =
    434       "DELETE FROM Groups WHERE group_id = ?";
    435 
    436   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    437   statement.BindInt64(0, group_id);
    438 
    439   return statement.Run();
    440 }
    441 
    442 bool AppCacheDatabase::FindCache(int64 cache_id, CacheRecord* record) {
    443   DCHECK(record);
    444   if (!LazyOpen(false))
    445     return false;
    446 
    447   const char* kSql =
    448       "SELECT cache_id, group_id, online_wildcard, update_time, cache_size"
    449       " FROM Caches WHERE cache_id = ?";
    450 
    451   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    452   statement.BindInt64(0, cache_id);
    453 
    454   if (!statement.Step())
    455     return false;
    456 
    457   ReadCacheRecord(statement, record);
    458   return true;
    459 }
    460 
    461 bool AppCacheDatabase::FindCacheForGroup(int64 group_id, CacheRecord* record) {
    462   DCHECK(record);
    463   if (!LazyOpen(false))
    464     return false;
    465 
    466   const char* kSql =
    467       "SELECT cache_id, group_id, online_wildcard, update_time, cache_size"
    468       "  FROM Caches WHERE group_id = ?";
    469 
    470   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    471   statement.BindInt64(0, group_id);
    472 
    473   if (!statement.Step())
    474     return false;
    475 
    476   ReadCacheRecord(statement, record);
    477   return true;
    478 }
    479 
    480 bool AppCacheDatabase::FindCachesForOrigin(
    481     const GURL& origin, std::vector<CacheRecord>* records) {
    482   DCHECK(records);
    483   std::vector<GroupRecord> group_records;
    484   if (!FindGroupsForOrigin(origin, &group_records))
    485     return false;
    486 
    487   CacheRecord cache_record;
    488   std::vector<GroupRecord>::const_iterator iter = group_records.begin();
    489   while (iter != group_records.end()) {
    490     if (FindCacheForGroup(iter->group_id, &cache_record))
    491       records->push_back(cache_record);
    492     ++iter;
    493   }
    494   return true;
    495 }
    496 
    497 bool AppCacheDatabase::InsertCache(const CacheRecord* record) {
    498   if (!LazyOpen(true))
    499     return false;
    500 
    501   const char* kSql =
    502       "INSERT INTO Caches (cache_id, group_id, online_wildcard,"
    503       "                    update_time, cache_size)"
    504       "  VALUES(?, ?, ?, ?, ?)";
    505 
    506   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    507   statement.BindInt64(0, record->cache_id);
    508   statement.BindInt64(1, record->group_id);
    509   statement.BindBool(2, record->online_wildcard);
    510   statement.BindInt64(3, record->update_time.ToInternalValue());
    511   statement.BindInt64(4, record->cache_size);
    512 
    513   return statement.Run();
    514 }
    515 
    516 bool AppCacheDatabase::DeleteCache(int64 cache_id) {
    517   if (!LazyOpen(false))
    518     return false;
    519 
    520   const char* kSql =
    521       "DELETE FROM Caches WHERE cache_id = ?";
    522 
    523   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    524   statement.BindInt64(0, cache_id);
    525 
    526   return statement.Run();
    527 }
    528 
    529 bool AppCacheDatabase::FindEntriesForCache(
    530     int64 cache_id, std::vector<EntryRecord>* records) {
    531   DCHECK(records && records->empty());
    532   if (!LazyOpen(false))
    533     return false;
    534 
    535   const char* kSql =
    536       "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
    537       "  WHERE cache_id = ?";
    538 
    539   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    540   statement.BindInt64(0, cache_id);
    541 
    542   while (statement.Step()) {
    543     records->push_back(EntryRecord());
    544     ReadEntryRecord(statement, &records->back());
    545     DCHECK(records->back().cache_id == cache_id);
    546   }
    547 
    548   return statement.Succeeded();
    549 }
    550 
    551 bool AppCacheDatabase::FindEntriesForUrl(
    552     const GURL& url, std::vector<EntryRecord>* records) {
    553   DCHECK(records && records->empty());
    554   if (!LazyOpen(false))
    555     return false;
    556 
    557   const char* kSql =
    558       "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
    559       "  WHERE url = ?";
    560 
    561   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    562   statement.BindString(0, url.spec());
    563 
    564   while (statement.Step()) {
    565     records->push_back(EntryRecord());
    566     ReadEntryRecord(statement, &records->back());
    567     DCHECK(records->back().url == url);
    568   }
    569 
    570   return statement.Succeeded();
    571 }
    572 
    573 bool AppCacheDatabase::FindEntry(
    574     int64 cache_id, const GURL& url, EntryRecord* record) {
    575   DCHECK(record);
    576   if (!LazyOpen(false))
    577     return false;
    578 
    579   const char* kSql =
    580       "SELECT cache_id, url, flags, response_id, response_size FROM Entries"
    581       "  WHERE cache_id = ? AND url = ?";
    582 
    583   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    584   statement.BindInt64(0, cache_id);
    585   statement.BindString(1, url.spec());
    586 
    587   if (!statement.Step())
    588     return false;
    589 
    590   ReadEntryRecord(statement, record);
    591   DCHECK(record->cache_id == cache_id);
    592   DCHECK(record->url == url);
    593   return true;
    594 }
    595 
    596 bool AppCacheDatabase::InsertEntry(const EntryRecord* record) {
    597   if (!LazyOpen(true))
    598     return false;
    599 
    600   const char* kSql =
    601       "INSERT INTO Entries (cache_id, url, flags, response_id, response_size)"
    602       "  VALUES(?, ?, ?, ?, ?)";
    603 
    604   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    605   statement.BindInt64(0, record->cache_id);
    606   statement.BindString(1, record->url.spec());
    607   statement.BindInt(2, record->flags);
    608   statement.BindInt64(3, record->response_id);
    609   statement.BindInt64(4, record->response_size);
    610 
    611   return statement.Run();
    612 }
    613 
    614 bool AppCacheDatabase::InsertEntryRecords(
    615     const std::vector<EntryRecord>& records) {
    616   if (records.empty())
    617     return true;
    618   sql::Transaction transaction(db_.get());
    619   if (!transaction.Begin())
    620     return false;
    621   std::vector<EntryRecord>::const_iterator iter = records.begin();
    622   while (iter != records.end()) {
    623     if (!InsertEntry(&(*iter)))
    624       return false;
    625     ++iter;
    626   }
    627   return transaction.Commit();
    628 }
    629 
    630 bool AppCacheDatabase::DeleteEntriesForCache(int64 cache_id) {
    631   if (!LazyOpen(false))
    632     return false;
    633 
    634   const char* kSql =
    635       "DELETE FROM Entries WHERE cache_id = ?";
    636 
    637   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    638   statement.BindInt64(0, cache_id);
    639 
    640   return statement.Run();
    641 }
    642 
    643 bool AppCacheDatabase::AddEntryFlags(
    644     const GURL& entry_url, int64 cache_id, int additional_flags) {
    645   if (!LazyOpen(false))
    646     return false;
    647 
    648   const char* kSql =
    649       "UPDATE Entries SET flags = flags | ? WHERE cache_id = ? AND url = ?";
    650 
    651   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    652   statement.BindInt(0, additional_flags);
    653   statement.BindInt64(1, cache_id);
    654   statement.BindString(2, entry_url.spec());
    655 
    656   return statement.Run() && db_->GetLastChangeCount();
    657 }
    658 
    659 bool AppCacheDatabase::FindNamespacesForOrigin(
    660     const GURL& origin,
    661     std::vector<NamespaceRecord>* intercepts,
    662     std::vector<NamespaceRecord>* fallbacks) {
    663   DCHECK(intercepts && intercepts->empty());
    664   DCHECK(fallbacks && fallbacks->empty());
    665   if (!LazyOpen(false))
    666     return false;
    667 
    668   const char* kSql =
    669       "SELECT cache_id, origin, type, namespace_url, target_url, is_pattern"
    670       "  FROM Namespaces WHERE origin = ?";
    671 
    672   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    673   statement.BindString(0, origin.spec());
    674 
    675   ReadNamespaceRecords(&statement, intercepts, fallbacks);
    676 
    677   return statement.Succeeded();
    678 }
    679 
    680 bool AppCacheDatabase::FindNamespacesForCache(
    681     int64 cache_id,
    682     std::vector<NamespaceRecord>* intercepts,
    683     std::vector<NamespaceRecord>* fallbacks) {
    684   DCHECK(intercepts && intercepts->empty());
    685   DCHECK(fallbacks && fallbacks->empty());
    686   if (!LazyOpen(false))
    687     return false;
    688 
    689   const char* kSql =
    690       "SELECT cache_id, origin, type, namespace_url, target_url, is_pattern"
    691       "  FROM Namespaces WHERE cache_id = ?";
    692 
    693   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    694   statement.BindInt64(0, cache_id);
    695 
    696   ReadNamespaceRecords(&statement, intercepts, fallbacks);
    697 
    698   return statement.Succeeded();
    699 }
    700 
    701 bool AppCacheDatabase::InsertNamespace(
    702     const NamespaceRecord* record) {
    703   if (!LazyOpen(true))
    704     return false;
    705 
    706   const char* kSql =
    707       "INSERT INTO Namespaces"
    708       "  (cache_id, origin, type, namespace_url, target_url, is_pattern)"
    709       "  VALUES (?, ?, ?, ?, ?, ?)";
    710 
    711   // Note: quick and dirty storage for the 'executable' bit w/o changing
    712   // schemas, we use the high bit of 'type' field.
    713   int type_with_executable_bit = record->namespace_.type;
    714   if (record->namespace_.is_executable) {
    715     type_with_executable_bit |= 0x8000000;
    716     DCHECK(base::CommandLine::ForCurrentProcess()->HasSwitch(
    717         kEnableExecutableHandlers));
    718   }
    719 
    720   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    721   statement.BindInt64(0, record->cache_id);
    722   statement.BindString(1, record->origin.spec());
    723   statement.BindInt(2, type_with_executable_bit);
    724   statement.BindString(3, record->namespace_.namespace_url.spec());
    725   statement.BindString(4, record->namespace_.target_url.spec());
    726   statement.BindBool(5, record->namespace_.is_pattern);
    727   return statement.Run();
    728 }
    729 
    730 bool AppCacheDatabase::InsertNamespaceRecords(
    731     const std::vector<NamespaceRecord>& records) {
    732   if (records.empty())
    733     return true;
    734   sql::Transaction transaction(db_.get());
    735   if (!transaction.Begin())
    736     return false;
    737   std::vector<NamespaceRecord>::const_iterator iter = records.begin();
    738   while (iter != records.end()) {
    739     if (!InsertNamespace(&(*iter)))
    740       return false;
    741     ++iter;
    742   }
    743   return transaction.Commit();
    744 }
    745 
    746 bool AppCacheDatabase::DeleteNamespacesForCache(int64 cache_id) {
    747   if (!LazyOpen(false))
    748     return false;
    749 
    750   const char* kSql =
    751       "DELETE FROM Namespaces WHERE cache_id = ?";
    752 
    753   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    754   statement.BindInt64(0, cache_id);
    755 
    756   return statement.Run();
    757 }
    758 
    759 bool AppCacheDatabase::FindOnlineWhiteListForCache(
    760     int64 cache_id, std::vector<OnlineWhiteListRecord>* records) {
    761   DCHECK(records && records->empty());
    762   if (!LazyOpen(false))
    763     return false;
    764 
    765   const char* kSql =
    766       "SELECT cache_id, namespace_url, is_pattern FROM OnlineWhiteLists"
    767       "  WHERE cache_id = ?";
    768 
    769   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    770   statement.BindInt64(0, cache_id);
    771 
    772   while (statement.Step()) {
    773     records->push_back(OnlineWhiteListRecord());
    774     this->ReadOnlineWhiteListRecord(statement, &records->back());
    775     DCHECK(records->back().cache_id == cache_id);
    776   }
    777   return statement.Succeeded();
    778 }
    779 
    780 bool AppCacheDatabase::InsertOnlineWhiteList(
    781     const OnlineWhiteListRecord* record) {
    782   if (!LazyOpen(true))
    783     return false;
    784 
    785   const char* kSql =
    786       "INSERT INTO OnlineWhiteLists (cache_id, namespace_url, is_pattern)"
    787       "  VALUES (?, ?, ?)";
    788 
    789   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    790   statement.BindInt64(0, record->cache_id);
    791   statement.BindString(1, record->namespace_url.spec());
    792   statement.BindBool(2, record->is_pattern);
    793 
    794   return statement.Run();
    795 }
    796 
    797 bool AppCacheDatabase::InsertOnlineWhiteListRecords(
    798     const std::vector<OnlineWhiteListRecord>& records) {
    799   if (records.empty())
    800     return true;
    801   sql::Transaction transaction(db_.get());
    802   if (!transaction.Begin())
    803     return false;
    804   std::vector<OnlineWhiteListRecord>::const_iterator iter = records.begin();
    805   while (iter != records.end()) {
    806     if (!InsertOnlineWhiteList(&(*iter)))
    807       return false;
    808     ++iter;
    809   }
    810   return transaction.Commit();
    811 }
    812 
    813 bool AppCacheDatabase::DeleteOnlineWhiteListForCache(int64 cache_id) {
    814   if (!LazyOpen(false))
    815     return false;
    816 
    817   const char* kSql =
    818       "DELETE FROM OnlineWhiteLists WHERE cache_id = ?";
    819 
    820   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    821   statement.BindInt64(0, cache_id);
    822 
    823   return statement.Run();
    824 }
    825 
    826 bool AppCacheDatabase::GetDeletableResponseIds(
    827     std::vector<int64>* response_ids, int64 max_rowid, int limit) {
    828   if (!LazyOpen(false))
    829     return false;
    830 
    831   const char* kSql =
    832       "SELECT response_id FROM DeletableResponseIds "
    833       "  WHERE rowid <= ?"
    834       "  LIMIT ?";
    835 
    836   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    837   statement.BindInt64(0, max_rowid);
    838   statement.BindInt64(1, limit);
    839 
    840   while (statement.Step())
    841     response_ids->push_back(statement.ColumnInt64(0));
    842   return statement.Succeeded();
    843 }
    844 
    845 bool AppCacheDatabase::InsertDeletableResponseIds(
    846     const std::vector<int64>& response_ids) {
    847   const char* kSql =
    848       "INSERT INTO DeletableResponseIds (response_id) VALUES (?)";
    849   return RunCachedStatementWithIds(SQL_FROM_HERE, kSql, response_ids);
    850 }
    851 
    852 bool AppCacheDatabase::DeleteDeletableResponseIds(
    853     const std::vector<int64>& response_ids) {
    854   const char* kSql =
    855       "DELETE FROM DeletableResponseIds WHERE response_id = ?";
    856   return RunCachedStatementWithIds(SQL_FROM_HERE, kSql, response_ids);
    857 }
    858 
    859 bool AppCacheDatabase::RunCachedStatementWithIds(
    860     const sql::StatementID& statement_id, const char* sql,
    861     const std::vector<int64>& ids) {
    862   DCHECK(sql);
    863   if (!LazyOpen(true))
    864     return false;
    865 
    866   sql::Transaction transaction(db_.get());
    867   if (!transaction.Begin())
    868     return false;
    869 
    870   sql::Statement statement(db_->GetCachedStatement(statement_id, sql));
    871 
    872   std::vector<int64>::const_iterator iter = ids.begin();
    873   while (iter != ids.end()) {
    874     statement.BindInt64(0, *iter);
    875     if (!statement.Run())
    876       return false;
    877     statement.Reset(true);
    878     ++iter;
    879   }
    880 
    881   return transaction.Commit();
    882 }
    883 
    884 bool AppCacheDatabase::RunUniqueStatementWithInt64Result(
    885     const char* sql, int64* result) {
    886   DCHECK(sql);
    887   sql::Statement statement(db_->GetUniqueStatement(sql));
    888   if (!statement.Step()) {
    889     return false;
    890   }
    891   *result = statement.ColumnInt64(0);
    892   return true;
    893 }
    894 
    895 bool AppCacheDatabase::FindResponseIdsForCacheHelper(
    896     int64 cache_id, std::vector<int64>* ids_vector,
    897     std::set<int64>* ids_set) {
    898   DCHECK(ids_vector || ids_set);
    899   DCHECK(!(ids_vector && ids_set));
    900   if (!LazyOpen(false))
    901     return false;
    902 
    903   const char* kSql =
    904       "SELECT response_id FROM Entries WHERE cache_id = ?";
    905 
    906   sql::Statement statement(db_->GetCachedStatement(SQL_FROM_HERE, kSql));
    907 
    908   statement.BindInt64(0, cache_id);
    909   while (statement.Step()) {
    910     int64 id = statement.ColumnInt64(0);
    911     if (ids_set)
    912       ids_set->insert(id);
    913     else
    914       ids_vector->push_back(id);
    915   }
    916 
    917   return statement.Succeeded();
    918 }
    919 
    920 void AppCacheDatabase::ReadGroupRecord(
    921     const sql::Statement& statement, GroupRecord* record) {
    922   record->group_id = statement.ColumnInt64(0);
    923   record->origin = GURL(statement.ColumnString(1));
    924   record->manifest_url = GURL(statement.ColumnString(2));
    925   record->creation_time =
    926       base::Time::FromInternalValue(statement.ColumnInt64(3));
    927   record->last_access_time =
    928       base::Time::FromInternalValue(statement.ColumnInt64(4));
    929 }
    930 
    931 void AppCacheDatabase::ReadCacheRecord(
    932     const sql::Statement& statement, CacheRecord* record) {
    933   record->cache_id = statement.ColumnInt64(0);
    934   record->group_id = statement.ColumnInt64(1);
    935   record->online_wildcard = statement.ColumnBool(2);
    936   record->update_time =
    937       base::Time::FromInternalValue(statement.ColumnInt64(3));
    938   record->cache_size = statement.ColumnInt64(4);
    939 }
    940 
    941 void AppCacheDatabase::ReadEntryRecord(
    942     const sql::Statement& statement, EntryRecord* record) {
    943   record->cache_id = statement.ColumnInt64(0);
    944   record->url = GURL(statement.ColumnString(1));
    945   record->flags = statement.ColumnInt(2);
    946   record->response_id = statement.ColumnInt64(3);
    947   record->response_size = statement.ColumnInt64(4);
    948 }
    949 
    950 void AppCacheDatabase::ReadNamespaceRecords(
    951       sql::Statement* statement,
    952       NamespaceRecordVector* intercepts,
    953       NamespaceRecordVector* fallbacks) {
    954   while (statement->Step()) {
    955     AppCacheNamespaceType type = static_cast<AppCacheNamespaceType>(
    956         statement->ColumnInt(2));
    957     NamespaceRecordVector* records =
    958         (type == APPCACHE_FALLBACK_NAMESPACE) ? fallbacks : intercepts;
    959     records->push_back(NamespaceRecord());
    960     ReadNamespaceRecord(statement, &records->back());
    961   }
    962 }
    963 
    964 void AppCacheDatabase::ReadNamespaceRecord(
    965     const sql::Statement* statement, NamespaceRecord* record) {
    966   record->cache_id = statement->ColumnInt64(0);
    967   record->origin = GURL(statement->ColumnString(1));
    968   int type_with_executable_bit = statement->ColumnInt(2);
    969   record->namespace_.namespace_url = GURL(statement->ColumnString(3));
    970   record->namespace_.target_url = GURL(statement->ColumnString(4));
    971   record->namespace_.is_pattern = statement->ColumnBool(5);
    972 
    973   // Note: quick and dirty storage for the 'executable' bit w/o changing
    974   // schemas, we use the high bit of 'type' field.
    975   record->namespace_.type = static_cast<AppCacheNamespaceType>
    976       (type_with_executable_bit & 0x7ffffff);
    977   record->namespace_.is_executable =
    978       (type_with_executable_bit & 0x80000000) != 0;
    979   DCHECK(!record->namespace_.is_executable ||
    980       base::CommandLine::ForCurrentProcess()->HasSwitch(
    981           kEnableExecutableHandlers));
    982 }
    983 
    984 void AppCacheDatabase::ReadOnlineWhiteListRecord(
    985     const sql::Statement& statement, OnlineWhiteListRecord* record) {
    986   record->cache_id = statement.ColumnInt64(0);
    987   record->namespace_url = GURL(statement.ColumnString(1));
    988   record->is_pattern = statement.ColumnBool(2);
    989 }
    990 
    991 bool AppCacheDatabase::LazyOpen(bool create_if_needed) {
    992   if (db_)
    993     return true;
    994 
    995   // If we tried and failed once, don't try again in the same session
    996   // to avoid creating an incoherent mess on disk.
    997   if (is_disabled_)
    998     return false;
    999 
   1000   // Avoid creating a database at all if we can.
   1001   bool use_in_memory_db = db_file_path_.empty();
   1002   if (!create_if_needed &&
   1003       (use_in_memory_db || !base::PathExists(db_file_path_))) {
   1004     return false;
   1005   }
   1006 
   1007   db_.reset(new sql::Connection);
   1008   meta_table_.reset(new sql::MetaTable);
   1009 
   1010   db_->set_histogram_tag("AppCache");
   1011 
   1012   bool opened = false;
   1013   if (use_in_memory_db) {
   1014     opened = db_->OpenInMemory();
   1015   } else if (!base::CreateDirectory(db_file_path_.DirName())) {
   1016     LOG(ERROR) << "Failed to create appcache directory.";
   1017   } else {
   1018     opened = db_->Open(db_file_path_);
   1019     if (opened)
   1020       db_->Preload();
   1021   }
   1022 
   1023   if (!opened || !db_->QuickIntegrityCheck() || !EnsureDatabaseVersion()) {
   1024     LOG(ERROR) << "Failed to open the appcache database.";
   1025     AppCacheHistograms::CountInitResult(
   1026         AppCacheHistograms::SQL_DATABASE_ERROR);
   1027 
   1028     // We're unable to open the database. This is a fatal error
   1029     // which we can't recover from. We try to handle it by deleting
   1030     // the existing appcache data and starting with a clean slate in
   1031     // this browser session.
   1032     if (!use_in_memory_db && DeleteExistingAndCreateNewDatabase())
   1033       return true;
   1034 
   1035     Disable();
   1036     return false;
   1037   }
   1038 
   1039   AppCacheHistograms::CountInitResult(AppCacheHistograms::INIT_OK);
   1040   was_corruption_detected_ = false;
   1041   db_->set_error_callback(
   1042       base::Bind(&AppCacheDatabase::OnDatabaseError, base::Unretained(this)));
   1043   return true;
   1044 }
   1045 
   1046 bool AppCacheDatabase::EnsureDatabaseVersion() {
   1047   if (!sql::MetaTable::DoesTableExist(db_.get()))
   1048     return CreateSchema();
   1049 
   1050   if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
   1051     return false;
   1052 
   1053   if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) {
   1054     LOG(WARNING) << "AppCache database is too new.";
   1055     return false;
   1056   }
   1057 
   1058   std::string stored_flags;
   1059   meta_table_->GetValue(kExperimentFlagsKey, &stored_flags);
   1060   if (stored_flags != GetActiveExperimentFlags())
   1061     return false;
   1062 
   1063   if (meta_table_->GetVersionNumber() < kCurrentVersion)
   1064     return UpgradeSchema();
   1065 
   1066 #ifndef NDEBUG
   1067   DCHECK(sql::MetaTable::DoesTableExist(db_.get()));
   1068   for (int i = 0; i < kTableCount; ++i) {
   1069     DCHECK(db_->DoesTableExist(kTables[i].table_name));
   1070   }
   1071   for (int i = 0; i < kIndexCount; ++i) {
   1072     DCHECK(db_->DoesIndexExist(kIndexes[i].index_name));
   1073   }
   1074 #endif
   1075 
   1076   return true;
   1077 }
   1078 
   1079 bool AppCacheDatabase::CreateSchema() {
   1080   sql::Transaction transaction(db_.get());
   1081   if (!transaction.Begin())
   1082     return false;
   1083 
   1084   if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion))
   1085     return false;
   1086 
   1087   if (!meta_table_->SetValue(kExperimentFlagsKey,
   1088                              GetActiveExperimentFlags())) {
   1089     return false;
   1090   }
   1091 
   1092   for (int i = 0; i < kTableCount; ++i) {
   1093     if (!CreateTable(db_.get(), kTables[i]))
   1094       return false;
   1095   }
   1096 
   1097   for (int i = 0; i < kIndexCount; ++i) {
   1098     if (!CreateIndex(db_.get(), kIndexes[i]))
   1099       return false;
   1100   }
   1101 
   1102   return transaction.Commit();
   1103 }
   1104 
   1105 bool AppCacheDatabase::UpgradeSchema() {
   1106 #if defined(APPCACHE_USE_SIMPLE_CACHE)
   1107   return DeleteExistingAndCreateNewDatabase();
   1108 #else
   1109   if (meta_table_->GetVersionNumber() == 3) {
   1110     // version 3 was pre 12/17/2011
   1111     DCHECK_EQ(strcmp(kNamespacesTable, kTables[3].table_name), 0);
   1112     DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[6].table_name), 0);
   1113     DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[7].table_name), 0);
   1114     DCHECK_EQ(strcmp(kNamespacesTable, kIndexes[8].table_name), 0);
   1115 
   1116     const TableInfo kNamespaceTable_v4 = {
   1117         kNamespacesTable,
   1118         "(cache_id INTEGER,"
   1119         " origin TEXT,"  // intentionally not normalized
   1120         " type INTEGER,"
   1121         " namespace_url TEXT,"
   1122         " target_url TEXT)"
   1123     };
   1124 
   1125     // Migrate from the old FallbackNameSpaces to the newer Namespaces table,
   1126     // but without the is_pattern column added in v5.
   1127     sql::Transaction transaction(db_.get());
   1128     if (!transaction.Begin() ||
   1129         !CreateTable(db_.get(), kNamespaceTable_v4)) {
   1130       return false;
   1131     }
   1132 
   1133     // Move data from the old table to the new table, setting the
   1134     // 'type' for all current records to the value for
   1135     // APPCACHE_FALLBACK_NAMESPACE.
   1136     DCHECK_EQ(0, static_cast<int>(APPCACHE_FALLBACK_NAMESPACE));
   1137     if (!db_->Execute(
   1138             "INSERT INTO Namespaces"
   1139             "  SELECT cache_id, origin, 0, namespace_url, fallback_entry_url"
   1140             "  FROM FallbackNameSpaces")) {
   1141       return false;
   1142     }
   1143 
   1144     // Drop the old table, indexes on that table are also removed by this.
   1145     if (!db_->Execute("DROP TABLE FallbackNameSpaces"))
   1146       return false;
   1147 
   1148     // Create new indexes.
   1149     if (!CreateIndex(db_.get(), kIndexes[6]) ||
   1150         !CreateIndex(db_.get(), kIndexes[7]) ||
   1151         !CreateIndex(db_.get(), kIndexes[8])) {
   1152       return false;
   1153     }
   1154 
   1155     meta_table_->SetVersionNumber(4);
   1156     meta_table_->SetCompatibleVersionNumber(4);
   1157     if (!transaction.Commit())
   1158       return false;
   1159   }
   1160 
   1161   if (meta_table_->GetVersionNumber() == 4) {
   1162     // version 4 pre 3/30/2013
   1163     // Add the is_pattern column to the Namespaces and OnlineWhitelists tables.
   1164     DCHECK_EQ(strcmp(kNamespacesTable, "Namespaces"), 0);
   1165     sql::Transaction transaction(db_.get());
   1166     if (!transaction.Begin())
   1167       return false;
   1168     if (!db_->Execute(
   1169             "ALTER TABLE Namespaces ADD COLUMN"
   1170             "  is_pattern INTEGER CHECK(is_pattern IN (0, 1))")) {
   1171       return false;
   1172     }
   1173     if (!db_->Execute(
   1174             "ALTER TABLE OnlineWhitelists ADD COLUMN"
   1175             "  is_pattern INTEGER CHECK(is_pattern IN (0, 1))")) {
   1176       return false;
   1177     }
   1178     meta_table_->SetVersionNumber(5);
   1179     meta_table_->SetCompatibleVersionNumber(5);
   1180     return transaction.Commit();
   1181   }
   1182 
   1183   // If there is no upgrade path for the version on disk to the current
   1184   // version, nuke everything and start over.
   1185   return DeleteExistingAndCreateNewDatabase();
   1186 #endif
   1187 }
   1188 
   1189 void AppCacheDatabase::ResetConnectionAndTables() {
   1190   meta_table_.reset();
   1191   db_.reset();
   1192 }
   1193 
   1194 bool AppCacheDatabase::DeleteExistingAndCreateNewDatabase() {
   1195   DCHECK(!db_file_path_.empty());
   1196   DCHECK(base::PathExists(db_file_path_));
   1197   VLOG(1) << "Deleting existing appcache data and starting over.";
   1198 
   1199   ResetConnectionAndTables();
   1200 
   1201   // This also deletes the disk cache data.
   1202   base::FilePath directory = db_file_path_.DirName();
   1203   if (!base::DeleteFile(directory, true))
   1204     return false;
   1205 
   1206   // Make sure the steps above actually deleted things.
   1207   if (base::PathExists(directory))
   1208     return false;
   1209 
   1210   if (!base::CreateDirectory(directory))
   1211     return false;
   1212 
   1213   // So we can't go recursive.
   1214   if (is_recreating_)
   1215     return false;
   1216 
   1217   base::AutoReset<bool> auto_reset(&is_recreating_, true);
   1218   return LazyOpen(true);
   1219 }
   1220 
   1221 void AppCacheDatabase::OnDatabaseError(int err, sql::Statement* stmt) {
   1222   was_corruption_detected_ |= sql::IsErrorCatastrophic(err);
   1223   if (!db_->ShouldIgnoreSqliteError(err))
   1224     DLOG(ERROR) << db_->GetErrorMessage();
   1225   // TODO: Maybe use non-catostrophic errors to trigger a full integrity check?
   1226 }
   1227 
   1228 }  // namespace content
   1229