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