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