diff options
Diffstat (limited to 'webkit/appcache/appcache_database.cc')
-rw-r--r-- | webkit/appcache/appcache_database.cc | 879 |
1 files changed, 879 insertions, 0 deletions
diff --git a/webkit/appcache/appcache_database.cc b/webkit/appcache/appcache_database.cc new file mode 100644 index 0000000..256def1 --- /dev/null +++ b/webkit/appcache/appcache_database.cc @@ -0,0 +1,879 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/appcache/appcache_database.h" + +#include "app/sql/connection.h" +#include "app/sql/meta_table.h" +#include "app/sql/statement.h" +#include "app/sql/transaction.h" +#include "base/auto_reset.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "webkit/appcache/appcache_entry.h" + +// Schema ------------------------------------------------------------------- +namespace { + +const int kCurrentVersion = 0; +const int kCompatibleVersion = 0; + +const char* kGroupsTable = "Groups"; +const char* kCachesTable = "Caches"; +const char* kEntriesTable = "Entries"; +const char* kFallbackNameSpacesTable = "FallbackNameSpaces"; +const char* kOnlineWhiteListsTable = "OnlineWhiteLists"; + +const struct { + const char* table_name; + const char* columns; +} kTables[] = { + { kGroupsTable, + "(group_id INTEGER PRIMARY KEY," + " origin TEXT," + " manifest_url TEXT)" }, + + { kCachesTable, + "(cache_id INTEGER PRIMARY KEY," + " group_id INTEGER," + " online_wildcard INTEGER CHECK(online_wildcard IN (0, 1))," + " update_time INTEGER)" }, + + { kEntriesTable, + "(cache_id INTEGER," + " url TEXT," + " flags INTEGER," + " response_id INTEGER)" }, + + { kFallbackNameSpacesTable, + "(cache_id INTEGER," + " origin TEXT," // intentionally not normalized + " namespace_url TEXT," + " fallback_entry_url TEXT)" }, + + { kOnlineWhiteListsTable, + "(cache_id INTEGER," + " namespace_url TEXT)" }, +}; + +const struct { + const char* index_name; + const char* table_name; + const char* columns; + bool unique; +} kIndexes[] = { + { "GroupsOriginIndex", + kGroupsTable, + "(origin)", + false }, + + { "GroupsManifestIndex", + kGroupsTable, + "(manifest_url)", + true }, + + { "CachesGroupIndex", + kCachesTable, + "(group_id)", + false }, + + { "EntriesCacheIndex", + kEntriesTable, + "(cache_id)", + false }, + + { "EntriesCacheAndUrlIndex", + kEntriesTable, + "(cache_id, url)", + true }, + + { "FallbackNameSpacesCacheIndex", + kFallbackNameSpacesTable, + "(cache_id)", + false }, + + { "FallbackNameSpacesOriginIndex", + kFallbackNameSpacesTable, + "(origin)", + false }, + + { "FallbackNameSpacesCacheAndUrlIndex", + kFallbackNameSpacesTable, + "(cache_id, namespace_url)", + true }, + + { "OnlineWhiteListCacheIndex", + kOnlineWhiteListsTable, + "(cache_id)", + false }, +}; + +const int kTableCount = ARRAYSIZE_UNSAFE(kTables); +const int kIndexCount = ARRAYSIZE_UNSAFE(kIndexes); + +} // anon namespace + + +// AppCacheDatabase ---------------------------------------------------------- +namespace appcache { + +AppCacheDatabase::AppCacheDatabase(const FilePath& path) + : db_file_path_(path), has_open_error_(false), is_recreating_(false) { +} + +AppCacheDatabase::~AppCacheDatabase() { +} + +void AppCacheDatabase::CloseConnection() { + // We can't close the connection for an in-memory database w/o + // losing all of the data, so we don't do that. + if (!db_file_path_.empty()) { + meta_table_.reset(); + db_.reset(); + } +} + +bool AppCacheDatabase::FindOriginsWithGroups(std::set<GURL>* origins) { + DCHECK(origins && origins->empty()); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT DISTINCT(origin) FROM Groups"; + + sql::Statement statement; + if (!PrepareUniqueStatement(kSql, &statement)) + return false; + + while (statement.Step()) + origins->insert(GURL(statement.ColumnString(0))); + + return statement.Succeeded(); +} + +bool AppCacheDatabase::FindLastStorageIds( + int64* last_group_id, int64* last_cache_id, int64* last_response_id) { + DCHECK(last_group_id && last_cache_id && last_response_id); + + *last_group_id = 0; + *last_cache_id = 0; + *last_response_id = 0; + + if (!LazyOpen(false)) + return false; + + sql::Statement statement; + + const char* kSql1 = "SELECT MAX(group_id) FROM Groups"; + if (!PrepareUniqueStatement(kSql1, &statement) || + !statement.Step()) { + return false; + } + int64 group_id = statement.ColumnInt64(0); + + const char* kSql2 = "SELECT MAX(cache_id) FROM Caches"; + if (!PrepareUniqueStatement(kSql2, &statement) || + !statement.Step()) { + return false; + } + int64 cache_id = statement.ColumnInt64(0); + + // TODO(michaeln): SELECT MAX(responseId) FROM Where/How ? + int64 response_id = 0; + + *last_group_id = group_id; + *last_cache_id = cache_id; + *last_response_id = response_id; + return true; +} + +bool AppCacheDatabase::FindGroup(int64 group_id, GroupRecord* record) { + DCHECK(record); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT group_id, origin, manifest_url FROM Groups WHERE group_id = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, group_id); + if (!statement.Step() || !statement.Succeeded()) + return false; + + ReadGroupRecord(statement, record); + DCHECK(record->group_id == group_id); + return true; +} + +bool AppCacheDatabase::FindGroupForManifestUrl( + const GURL& manifest_url, GroupRecord* record) { + DCHECK(record); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT group_id, origin, manifest_url FROM Groups" + " WHERE manifest_url = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindString(0, manifest_url.spec()); + if (!statement.Step() || !statement.Succeeded()) + return false; + + ReadGroupRecord(statement, record); + DCHECK(record->manifest_url == manifest_url); + return true; +} + +bool AppCacheDatabase::FindGroupsForOrigin( + const GURL& origin, std::vector<GroupRecord>* records) { + DCHECK(records && records->empty()); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT group_id, origin, manifest_url FROM Groups WHERE origin = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindString(0, origin.spec()); + while (statement.Step()) { + records->push_back(GroupRecord()); + ReadGroupRecord(statement, &records->back()); + DCHECK(records->back().origin == origin); + } + + return statement.Succeeded(); +} + +bool AppCacheDatabase::FindGroupForCache(int64 cache_id, GroupRecord* record) { + DCHECK(record); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT g.group_id, g.origin, g.manifest_url FROM Groups g, Caches c" + " WHERE c.cache_id = ? AND c.group_id = g.group_id"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, cache_id); + if (!statement.Step() || !statement.Succeeded()) + return false; + + ReadGroupRecord(statement, record); + return true; +} + +bool AppCacheDatabase::InsertGroup(const GroupRecord* record) { + if (!LazyOpen(true)) + return false; + + const char* kSql = + "INSERT INTO Groups (group_id, origin, manifest_url)" + " VALUES(?, ?, ?)"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, record->group_id); + statement.BindString(1, record->origin.spec()); + statement.BindString(2, record->manifest_url.spec()); + return statement.Run(); +} + +bool AppCacheDatabase::DeleteGroup(int64 group_id) { + if (!LazyOpen(false)) + return false; + + const char* kSql = + "DELETE FROM Groups WHERE group_id = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, group_id); + return statement.Run(); +} + +bool AppCacheDatabase::FindCache(int64 cache_id, CacheRecord* record) { + DCHECK(record); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT cache_id, group_id, online_wildcard, update_time" + " FROM Caches WHERE cache_id = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, cache_id); + if (!statement.Step() || !statement.Succeeded()) + return false; + + ReadCacheRecord(statement, record); + return true; +} + +bool AppCacheDatabase::FindCacheForGroup(int64 group_id, CacheRecord* record) { + DCHECK(record); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT cache_id, group_id, online_wildcard, update_time" + " FROM Caches WHERE group_id = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, group_id); + if (!statement.Step() || !statement.Succeeded()) + return false; + + ReadCacheRecord(statement, record); + return true; +} + +bool AppCacheDatabase::InsertCache(const CacheRecord* record) { + if (!LazyOpen(true)) + return false; + + const char* kSql = + "INSERT INTO Caches (cache_id, group_id, online_wildcard," + " update_time)" + " VALUES(?, ?, ?, ?)"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, record->cache_id); + statement.BindInt64(1, record->group_id); + statement.BindBool(2, record->online_wildcard); + + // There are no convinient methods to convert TimeTicks to or + // from microseconds directly, so we compute TimeDelta's + // as an intermediary step. + statement.BindInt64(3, + (record->update_time - base::TimeTicks()).InMicroseconds()); + + return statement.Run(); +} + +bool AppCacheDatabase::DeleteCache(int64 cache_id) { + if (!LazyOpen(false)) + return false; + + const char* kSql = + "DELETE FROM Caches WHERE cache_id = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, cache_id); + return statement.Run(); +} + +bool AppCacheDatabase::FindEntriesForCache( + int64 cache_id, std::vector<EntryRecord>* records) { + DCHECK(records && records->empty()); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT cache_id, url, flags, response_id FROM Entries" + " WHERE cache_id = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, cache_id); + while (statement.Step()) { + records->push_back(EntryRecord()); + ReadEntryRecord(statement, &records->back()); + DCHECK(records->back().cache_id == cache_id); + } + + return statement.Succeeded(); +} + +bool AppCacheDatabase::FindEntriesForUrl( + const GURL& url, std::vector<EntryRecord>* records) { + DCHECK(records && records->empty()); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT cache_id, url, flags, response_id FROM Entries" + " WHERE url = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindString(0, url.spec()); + while (statement.Step()) { + records->push_back(EntryRecord()); + ReadEntryRecord(statement, &records->back()); + DCHECK(records->back().url == url); + } + + return statement.Succeeded(); +} + + +bool AppCacheDatabase::FindEntry( + int64 cache_id, const GURL& url, EntryRecord* record) { + DCHECK(record); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT cache_id, url, flags, response_id FROM Entries" + " WHERE cache_id = ? AND url = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, cache_id); + statement.BindString(1, url.spec()); + if (!statement.Step() || !statement.Succeeded()) + return false; + + ReadEntryRecord(statement, record); + DCHECK(record->cache_id == cache_id); + DCHECK(record->url == url); + return true; +} + +bool AppCacheDatabase::InsertEntry(const EntryRecord* record) { + if (!LazyOpen(true)) + return false; + + const char* kSql = + "INSERT INTO Entries (cache_id, url, flags, response_id)" + " VALUES(?, ?, ?, ?)"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, record->cache_id); + statement.BindString(1, record->url.spec()); + statement.BindInt(2, record->flags); + statement.BindInt64(3, record->response_id); + return statement.Run(); +} + +bool AppCacheDatabase::InsertEntryRecords( + const std::vector<EntryRecord>& records) { + std::vector<EntryRecord>::const_iterator iter = records.begin(); + while (iter != records.end()) { + if (!InsertEntry(&(*iter))) + return false; + ++iter; + } + return true; +} + +bool AppCacheDatabase::DeleteEntriesForCache(int64 cache_id) { + if (!LazyOpen(false)) + return false; + + const char* kSql = + "DELETE FROM Entries WHERE cache_id = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, cache_id); + return statement.Run(); +} + +bool AppCacheDatabase::AddEntryFlags( + const GURL& entry_url, int64 cache_id, int additional_flags) { + if (!LazyOpen(false)) + return false; + + const char* kSql = + "UPDATE Entries SET flags = flags | ? WHERE cache_id = ? AND url = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt(0, additional_flags); + statement.BindInt64(1, cache_id); + statement.BindString(2, entry_url.spec()); + return statement.Run() && db_->GetLastChangeCount(); +} + +bool AppCacheDatabase::FindFallbackNameSpacesForOrigin( + const GURL& origin, std::vector<FallbackNameSpaceRecord>* records) { + DCHECK(records && records->empty()); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT cache_id, origin, namespace_url, fallback_entry_url" + " FROM FallbackNameSpaces WHERE origin = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindString(0, origin.spec()); + while (statement.Step()) { + records->push_back(FallbackNameSpaceRecord()); + ReadFallbackNameSpaceRecord(statement, &records->back()); + DCHECK(records->back().origin == origin); + } + return statement.Succeeded(); +} + +bool AppCacheDatabase::FindFallbackNameSpacesForCache( + int64 cache_id, std::vector<FallbackNameSpaceRecord>* records) { + DCHECK(records && records->empty()); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT cache_id, origin, namespace_url, fallback_entry_url" + " FROM FallbackNameSpaces WHERE cache_id = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, cache_id); + while (statement.Step()) { + records->push_back(FallbackNameSpaceRecord()); + ReadFallbackNameSpaceRecord(statement, &records->back()); + DCHECK(records->back().cache_id == cache_id); + } + return statement.Succeeded(); +} + +bool AppCacheDatabase::InsertFallbackNameSpace( + const FallbackNameSpaceRecord* record) { + if (!LazyOpen(true)) + return false; + + const char* kSql = + "INSERT INTO FallbackNameSpaces" + " (cache_id, origin, namespace_url, fallback_entry_url)" + " VALUES (?, ?, ?, ?)"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, record->cache_id); + statement.BindString(1, record->origin.spec()); + statement.BindString(2, record->namespace_url.spec()); + statement.BindString(3, record->fallback_entry_url.spec()); + return statement.Run(); +} + +bool AppCacheDatabase::InsertFallbackNameSpaceRecords( + const std::vector<FallbackNameSpaceRecord>& records) { + std::vector<FallbackNameSpaceRecord>::const_iterator iter = records.begin(); + while (iter != records.end()) { + if (!InsertFallbackNameSpace(&(*iter))) + return false; + ++iter; + } + return true; +} + +bool AppCacheDatabase::DeleteFallbackNameSpacesForCache(int64 cache_id) { + if (!LazyOpen(false)) + return false; + + const char* kSql = + "DELETE FROM FallbackNameSpaces WHERE cache_id = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, cache_id); + return statement.Run(); +} + +bool AppCacheDatabase::FindOnlineWhiteListForCache( + int64 cache_id, std::vector<OnlineWhiteListRecord>* records) { + DCHECK(records && records->empty()); + if (!LazyOpen(false)) + return false; + + const char* kSql = + "SELECT cache_id, namespace_url FROM OnlineWhiteLists" + " WHERE cache_id = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, cache_id); + while (statement.Step()) { + records->push_back(OnlineWhiteListRecord()); + this->ReadOnlineWhiteListRecord(statement, &records->back()); + DCHECK(records->back().cache_id == cache_id); + } + return statement.Succeeded(); +} + +bool AppCacheDatabase::InsertOnlineWhiteList( + const OnlineWhiteListRecord* record) { + if (!LazyOpen(true)) + return false; + + const char* kSql = + "INSERT INTO OnlineWhiteLists (cache_id, namespace_url) VALUES (?, ?)"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, record->cache_id); + statement.BindString(1, record->namespace_url.spec()); + return statement.Run(); +} + +bool AppCacheDatabase::InsertOnlineWhiteListRecords( + const std::vector<OnlineWhiteListRecord>& records) { + std::vector<OnlineWhiteListRecord>::const_iterator iter = records.begin(); + while (iter != records.end()) { + if (!InsertOnlineWhiteList(&(*iter))) + return false; + ++iter; + } + return true; +} + +bool AppCacheDatabase::DeleteOnlineWhiteListForCache(int64 cache_id) { + if (!LazyOpen(false)) + return false; + + const char* kSql = + "DELETE FROM OnlineWhiteLists WHERE cache_id = ?"; + + sql::Statement statement; + if (!PrepareCachedStatement(SQL_FROM_HERE, kSql, &statement)) + return false; + + statement.BindInt64(0, cache_id); + return statement.Run(); +} + +bool AppCacheDatabase::PrepareUniqueStatement( + const char* sql, sql::Statement* statement) { + DCHECK(sql && statement); + statement->Assign(db_->GetUniqueStatement(sql)); + if (!statement->is_valid()) { + NOTREACHED() << db_->GetErrorMessage(); + return false; + } + return true; +} + +bool AppCacheDatabase::PrepareCachedStatement( + const sql::StatementID& id, const char* sql, sql::Statement* statement) { + DCHECK(sql && statement); + statement->Assign(db_->GetCachedStatement(id, sql)); + if (!statement->is_valid()) { + NOTREACHED() << db_->GetErrorMessage(); + return false; + } + return true; +} + +void AppCacheDatabase::ReadGroupRecord( + const sql::Statement& statement, GroupRecord* record) { + record->group_id = statement.ColumnInt64(0); + record->origin = GURL(statement.ColumnString(1)); + record->manifest_url = GURL(statement.ColumnString(2)); +} + +void AppCacheDatabase::ReadCacheRecord( + const sql::Statement& statement, CacheRecord* record) { + record->cache_id = statement.ColumnInt64(0); + record->group_id = statement.ColumnInt64(1); + record->online_wildcard = statement.ColumnBool(2); + + // There are no convinient methods to convert TimeTicks to or + // from microseconds directly, so we compute TimeDelta's + // as an intermediary step. + record->update_time = base::TimeTicks() + + base::TimeDelta::FromMicroseconds(statement.ColumnInt64(3)); +} + +void AppCacheDatabase::ReadEntryRecord( + const sql::Statement& statement, EntryRecord* record) { + record->cache_id = statement.ColumnInt64(0); + record->url = GURL(statement.ColumnString(1)); + record->flags = statement.ColumnInt(2); + record->response_id = statement.ColumnInt64(3); +} + +void AppCacheDatabase::ReadFallbackNameSpaceRecord( + const sql::Statement& statement, FallbackNameSpaceRecord* record) { + record->cache_id = statement.ColumnInt64(0); + record->origin = GURL(statement.ColumnString(1)); + record->namespace_url = GURL(statement.ColumnString(2)); + record->fallback_entry_url = GURL(statement.ColumnString(3)); +} + +void AppCacheDatabase::ReadOnlineWhiteListRecord( + const sql::Statement& statement, OnlineWhiteListRecord* record) { + record->cache_id = statement.ColumnInt64(0); + record->namespace_url = GURL(statement.ColumnString(1)); +} + +bool AppCacheDatabase::LazyOpen(bool create_if_needed) { + if (db_.get()) + return true; + + // If we tried and failed once, don't try again in the same session + // to avoid creating an incoherent mess on disk. + if (has_open_error_) + return false; + + // Avoid creating a database at all if we can. + bool use_in_memory_db = db_file_path_.empty(); + if (!create_if_needed && + (use_in_memory_db || !file_util::PathExists(db_file_path_))) { + return false; + } + + db_.reset(new sql::Connection); + bool opened = use_in_memory_db ? db_->OpenInMemory() + : db_->Open(db_file_path_); + if (opened) + db_->Preload(); + if (!opened || !EnsureDatabaseVersion()) { + has_open_error_ = true; + meta_table_.reset(); + db_.reset(); + return false; + } + + return true; +} + +bool AppCacheDatabase::EnsureDatabaseVersion() { + bool did_create_meta_table = !sql::MetaTable::DoesTableExist(db_.get()); + + meta_table_.reset(new sql::MetaTable); + if (!meta_table_->Init(db_.get(), kCurrentVersion, kCompatibleVersion)) + return false; + + if (did_create_meta_table) + return CreateSchema(); + + if (meta_table_->GetCompatibleVersionNumber() > kCurrentVersion) { + DCHECK(!did_create_meta_table); + LOG(WARNING) << "AppCache database is too new."; + return false; + } + + if (meta_table_->GetVersionNumber() < kCurrentVersion) + return UpgradeSchema(); + +#ifndef NDEBUG + for (int i = 0; i < kTableCount; ++i) { + DCHECK(db_->DoesTableExist(kTables[i].table_name)); + } +#endif + + return true; +} + +bool AppCacheDatabase::CreateSchema() { + sql::Transaction transaction(db_.get()); + if (!transaction.Begin()) + return false; + + for (int i = 0; i < kTableCount; ++i) { + std::string sql("CREATE TABLE "); + sql += kTables[i].table_name; + sql += kTables[i].columns; + if (!db_->Execute(sql.c_str())) + return false; + } + + for (int i = 0; i < kIndexCount; ++i) { + std::string sql; + if (kIndexes[i].unique) + sql += "CREATE UNIQUE INDEX "; + else + sql += "CREATE INDEX "; + sql += kIndexes[i].index_name; + sql += " ON "; + sql += kIndexes[i].table_name; + sql += kIndexes[i].columns; + if (!db_->Execute(sql.c_str())) + return false; + } + + return transaction.Commit(); +} + +bool AppCacheDatabase::UpgradeSchema() { + DCHECK(false); // We don't have any upgrades yet since we're at version 0 + + // Upgrade logic goes here + + // If there is no upgrade path for the version on disk to the current + // version, nuke everything and start over. + return DeleteExistingAndCreateNewDatabase(); +} + +bool AppCacheDatabase::DeleteExistingAndCreateNewDatabase() { + DCHECK(!db_file_path_.empty()); + DCHECK(file_util::PathExists(db_file_path_)); + + meta_table_.reset(); + db_.reset(); + + FilePath directory = db_file_path_.DirName(); + if (!file_util::Delete(directory, true) || + !file_util::CreateDirectory(directory)) { + return false; + } + + // Make sure the steps above actually deleted things. + if (file_util::PathExists(db_file_path_)) + return false; + + // So we can't go recursive. + if (is_recreating_) + return false; + + AutoReset auto_reset(&is_recreating_, true); + return LazyOpen(true); +} + +} // namespace appcache |