Chromium Code Reviews| Index: chrome/browser/net/sqlite_persistent_cookie_store.cc |
| diff --git a/chrome/browser/net/sqlite_persistent_cookie_store.cc b/chrome/browser/net/sqlite_persistent_cookie_store.cc |
| index e3007c6122b03ffeff17e67a729fe88620e2568e..f791b91f9dcea0804b28dbfc8c8e983ee4b2ddb0 100644 |
| --- a/chrome/browser/net/sqlite_persistent_cookie_store.cc |
| +++ b/chrome/browser/net/sqlite_persistent_cookie_store.cc |
| @@ -108,9 +108,6 @@ class SQLitePersistentCookieStore::Backend |
| DCHECK(num_pending_ == 0 && pending_.empty()); |
| } |
| - // Database upgrade statements. |
| - bool EnsureDatabaseVersion(); |
| - |
| class PendingOperation { |
| public: |
| typedef enum { |
| @@ -165,8 +162,18 @@ class SQLitePersistentCookieStore::Backend |
| // tasks). |
| void ReportMetricsOnDBThread(); |
| - // Initialize the data base. |
| - bool InitializeDatabase(); |
| + // Open the database and read enough information to allow us to respond to |
| + // clients. |
| + bool PrepareDatabaseForAccess(); |
| + |
| + // Opens the database file and creates or migrates the schema if necessary. |
| + bool OpenDatabase(); |
| + |
| + // Initializes the cookies table, returning true on success. |
| + bool InitializeSchema(); |
| + |
| + // Check the database version and perform migration steps if necessary. |
| + bool EnsureDatabaseVersion(); |
| // Loads cookies for the next domain key from the DB, then either reschedules |
| // itself or schedules the provided callback to run on the IO thread (if all |
| @@ -179,8 +186,10 @@ class SQLitePersistentCookieStore::Backend |
| // Batch a cookie operation (add or delete) |
| void BatchOperation(PendingOperation::OperationType op, |
| const net::CookieMonster::CanonicalCookie& cc); |
| + |
| // Commit our pending operations to the database. |
| void Commit(); |
| + |
| // Close() executed on the background thread. |
| void InternalBackgroundClose(); |
| @@ -278,36 +287,6 @@ class IncrementTimeDelta { |
| DISALLOW_COPY_AND_ASSIGN(IncrementTimeDelta); |
| }; |
| -// Initializes the cookies table, returning true on success. |
| -bool InitTable(sql::Connection* db) { |
| - if (!db->DoesTableExist("cookies")) { |
| - if (!db->Execute("CREATE TABLE cookies (" |
| - "creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY," |
| - "host_key TEXT NOT NULL," |
| - "name TEXT NOT NULL," |
| - "value TEXT NOT NULL," |
| - "path TEXT NOT NULL," |
| - "expires_utc INTEGER NOT NULL," |
| - "secure INTEGER NOT NULL," |
| - "httponly INTEGER NOT NULL," |
| - "last_access_utc INTEGER NOT NULL, " |
| - "has_expires INTEGER NOT NULL DEFAULT 1, " |
| - "persistent INTEGER NOT NULL DEFAULT 1)")) |
| - return false; |
| - } |
| - |
| - // Try to create the index every time. Older versions did not have this index, |
| - // so we want those people to get it. |
| - if (!db->Execute("CREATE INDEX IF NOT EXISTS cookie_times ON cookies" |
| - " (creation_utc)")) |
| - return false; |
| - |
| - if (!db->Execute("CREATE INDEX IF NOT EXISTS domain ON cookies(host_key)")) |
| - return false; |
| - |
| - return true; |
| -} |
| - |
| // TODO(shess): Send up a crash report containing information to help |
| // debug http://crbug.com/111376 . |
| void DumpSchemaInfoWithoutCrashing(sql::Connection* db, |
| @@ -397,7 +376,7 @@ void SQLitePersistentCookieStore::Backend::LoadAndNotifyOnDBThread( |
| base::TimeDelta::FromMilliseconds(1), base::TimeDelta::FromMinutes(1), |
| 50); |
| - if (!InitializeDatabase()) { |
| + if (!PrepareDatabaseForAccess()) { |
| BrowserThread::PostTask( |
| BrowserThread::IO, FROM_HERE, |
| base::Bind(&SQLitePersistentCookieStore::Backend::CompleteLoadOnIOThread, |
| @@ -421,7 +400,7 @@ void SQLitePersistentCookieStore::Backend::LoadKeyAndNotifyOnDBThread( |
| 50); |
| bool success = false; |
| - if (InitializeDatabase()) { |
| + if (PrepareDatabaseForAccess()) { |
| std::map<std::string, std::set<std::string> >::iterator |
| it = keys_to_load_.find(key); |
| if (it != keys_to_load_.end()) { |
| @@ -507,7 +486,7 @@ void SQLitePersistentCookieStore::Backend::Notify( |
| loaded_callback.Run(cookies); |
| } |
| -bool SQLitePersistentCookieStore::Backend::InitializeDatabase() { |
| +bool SQLitePersistentCookieStore::Backend::PrepareDatabaseForAccess() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| if (initialized_) { |
| @@ -529,20 +508,8 @@ bool SQLitePersistentCookieStore::Backend::InitializeDatabase() { |
| UMA_HISTOGRAM_COUNTS("Cookie.DBSizeInKB", db_size / 1024 ); |
| } |
| - db_.reset(new sql::Connection); |
| - if (!db_->Open(path_)) { |
| - NOTREACHED() << "Unable to open cookie DB."; |
| - db_.reset(); |
| + if (!OpenDatabase()) |
| return false; |
| - } |
| - |
| - db_->set_error_delegate(GetErrorHandlerForCookieDb()); |
| - |
| - if (!EnsureDatabaseVersion() || !InitTable(db_.get())) { |
| - NOTREACHED() << "Unable to open cookie DB."; |
| - db_.reset(); |
| - return false; |
| - } |
| UMA_HISTOGRAM_CUSTOM_TIMES( |
| "Cookie.TimeInitializeDB", |
| @@ -585,6 +552,73 @@ bool SQLitePersistentCookieStore::Backend::InitializeDatabase() { |
| return true; |
| } |
| +bool SQLitePersistentCookieStore::Backend::OpenDatabase() { |
| + DCHECK(!db_.get()); |
| + |
| + db_.reset(new sql::Connection); |
| + if (!db_->Open(path_)) { |
| + NOTREACHED() << "Unable to open cookie DB."; |
| + db_.reset(); |
| + return false; |
| + } |
| + |
| + db_->set_error_delegate(GetErrorHandlerForCookieDb()); |
| + |
| + if (!sql::MetaTable::DoesTableExist(db_.get())) |
| + return InitializeSchema(); |
| + |
| + // We don't pass the current version or current compatible version because |
| + // we are expecting the schema to already be populated - hence these values |
| + // will be ignored. If we subsequently query for the version and get back 0 |
| + // we know something is wrong. |
|
Scott Hess - ex-Googler
2012/03/08 00:48:44
I don't like making these assumptions on how MetaT
|
| + if (!meta_table_.Init(db_.get(), 0, 0) || |
| + meta_table_.GetVersionNumber() == 0 || |
| + meta_table_.GetCompatibleVersionNumber() == 0) { |
| + UMA_HISTOGRAM_COUNTS_100("Cookie.CorruptMetaTable", 1); |
| + |
| + meta_table_.Reset(); |
| + db_.reset(new sql::Connection); |
| + if (!file_util::Delete(path_, false) || !db_->Open(path_)) { |
| + UMA_HISTOGRAM_COUNTS_100("Cookie.CorruptMetaTableRecoveryFailed", 1); |
| + NOTREACHED() << "Unable to reset the cookie DB."; |
| + meta_table_.Reset(); |
| + db_.reset(); |
| + return false; |
| + } |
| + db_->set_error_delegate(GetErrorHandlerForCookieDb()); |
| + |
| + return InitializeSchema(); |
| + } |
| + |
| + return EnsureDatabaseVersion(); |
| +} |
| + |
| +bool SQLitePersistentCookieStore::Backend::InitializeSchema() { |
| + DCHECK(!db_.get()->DoesTableExist("cookies")); |
| + sql::Transaction transaction(db_.get()); |
| + |
| + return transaction.Begin() && |
| + meta_table_.Init(db_.get(), |
| + kCurrentVersionNumber, |
| + kCompatibleVersionNumber) && |
| + db_->Execute("CREATE TABLE cookies (" |
| + "creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY," |
| + "host_key TEXT NOT NULL," |
| + "name TEXT NOT NULL," |
| + "value TEXT NOT NULL," |
| + "path TEXT NOT NULL," |
| + "expires_utc INTEGER NOT NULL," |
| + "secure INTEGER NOT NULL," |
| + "httponly INTEGER NOT NULL," |
| + "last_access_utc INTEGER NOT NULL, " |
| + "has_expires INTEGER NOT NULL DEFAULT 1, " |
| + "persistent INTEGER NOT NULL DEFAULT 1)") && |
| + db_->Execute("CREATE INDEX IF NOT EXISTS cookie_times ON cookies" |
| + " (creation_utc)") && |
| + db_->Execute("CREATE INDEX IF NOT EXISTS domain ON cookies(host_key)") && |
| + transaction.Commit(); |
|
Scott Hess - ex-Googler
2012/03/08 00:48:44
I don't really care for this style, versus a bunch
|
| +} |
| + |
| void SQLitePersistentCookieStore::Backend::ChainLoadCookies( |
| const LoadedCallback& loaded_callback) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| @@ -682,12 +716,6 @@ bool SQLitePersistentCookieStore::Backend::LoadCookiesForDomains( |
| } |
| bool SQLitePersistentCookieStore::Backend::EnsureDatabaseVersion() { |
| - // Version check. |
| - if (!meta_table_.Init( |
| - db_.get(), kCurrentVersionNumber, kCompatibleVersionNumber)) { |
| - return false; |
| - } |
| - |
| if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { |
| LOG(WARNING) << "Cookie database is too new."; |
| return false; |
| @@ -708,7 +736,8 @@ bool SQLitePersistentCookieStore::Backend::EnsureDatabaseVersion() { |
| meta_table_.SetVersionNumber(cur_version); |
| meta_table_.SetCompatibleVersionNumber( |
| std::min(cur_version, kCompatibleVersionNumber)); |
| - transaction.Commit(); |
| + if (!transaction.Commit()) |
| + return false; |
| } |
| if (cur_version == 3) { |
| @@ -742,7 +771,8 @@ bool SQLitePersistentCookieStore::Backend::EnsureDatabaseVersion() { |
| #endif |
| ++cur_version; |
| meta_table_.SetVersionNumber(cur_version); |
| - transaction.Commit(); |
| + if (!transaction.Commit()) |
| + return false; |
| } |
| if (cur_version == 4) { |
| @@ -750,29 +780,34 @@ bool SQLitePersistentCookieStore::Backend::EnsureDatabaseVersion() { |
| sql::Transaction transaction(db_.get()); |
| if (!transaction.Begin()) |
| return false; |
| + |
| + // The IF NOT EXISTS are because some databases were created without |
| + // these indexes. As of version 5 all tables are create with them, so let's |
| + // make sure that those old DBs get them as they go through this migration. |
| if (!db_->Execute("ALTER TABLE cookies " |
| "ADD COLUMN has_expires INTEGER DEFAULT 1") || |
| !db_->Execute("ALTER TABLE cookies " |
| - "ADD COLUMN persistent INTEGER DEFAULT 1")) { |
| + "ADD COLUMN persistent INTEGER DEFAULT 1") || |
| + !db_->Execute("CREATE INDEX IF NOT EXISTS cookie_times ON cookies" |
| + " (creation_utc)") || |
|
Scott Hess - ex-Googler
2012/03/08 00:48:44
If/when you get the conflict with my change to rem
|
| + !db_->Execute("CREATE INDEX IF NOT EXISTS domain ON cookies" |
| + "(host_key)")) { |
| LOG(WARNING) << "Unable to update cookie database to version 5."; |
| return false; |
| } |
| + |
| ++cur_version; |
| meta_table_.SetVersionNumber(cur_version); |
| meta_table_.SetCompatibleVersionNumber( |
| std::min(cur_version, kCompatibleVersionNumber)); |
| - transaction.Commit(); |
| + if (!transaction.Commit()) |
| + return false; |
| UMA_HISTOGRAM_TIMES("Cookie.TimeDatabaseMigrationToV5", |
| base::TimeTicks::Now() - start_time); |
| } |
| // Put future migration cases here. |
| - // When the version is too old, we just try to continue anyway, there should |
| - // not be a released product that makes a database too old for us to handle. |
| - LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << |
| - "Cookie database version " << cur_version << " is too old to handle."; |
| - |
|
Scott Hess - ex-Googler
2012/03/08 00:48:44
If the code gets to this point without cur_version
|
| return true; |
| } |