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 b519febee7cb315d43c5d277c6333abeb8449631..da9adcfea21fd7949e3369665daf1704f195b08f 100644 |
| --- a/chrome/browser/net/sqlite_persistent_cookie_store.cc |
| +++ b/chrome/browser/net/sqlite_persistent_cookie_store.cc |
| @@ -32,6 +32,7 @@ |
| #include "sql/meta_table.h" |
| #include "sql/statement.h" |
| #include "sql/transaction.h" |
| +#include "third_party/sqlite/sqlite3.h" |
| using base::Time; |
| using content::BrowserThread; |
| @@ -74,6 +75,8 @@ class SQLitePersistentCookieStore::Backend |
| num_cookies_read_(0), |
| num_priority_waiting_(0), |
| total_priority_requests_(0) { |
| + error_delegate_ = |
| + new KillDatabaseErrorDelegate(this, GetErrorHandlerForCookieDb()); |
| } |
| // Creates or loads the SQLite database. |
| @@ -104,6 +107,29 @@ class SQLitePersistentCookieStore::Backend |
| private: |
| friend class base::RefCountedThreadSafe<SQLitePersistentCookieStore::Backend>; |
| + class KillDatabaseErrorDelegate : public sql::ErrorDelegate { |
| + public: |
| + KillDatabaseErrorDelegate(Backend* backend, |
| + sql::ErrorDelegate* wrapped_delegate); |
| + // ErrorDelegate implementation. |
| + virtual int OnError(int error, |
| + sql::Connection* connection, |
| + sql::Statement* stmt) OVERRIDE; |
| + |
| + void reset_backend() { |
| + backend_ = NULL; |
| + } |
| + |
| + private: |
| + // Do not increment the count on Backend, as that would create a circular |
| + // reference (Backend -> Connection -> ErrorDelegate -> Backend). Instead, |
| + // Backend will call reset_backend() when it is going away. |
|
Scott Hess - ex-Googler
2012/09/14 17:15:16
Rather than doing this dance, would it make sense
erikwright (departed)
2012/09/14 18:11:02
Then you would have the circular reference of Back
Scott Hess - ex-Googler
2012/09/14 18:34:03
Doh!
erikwright (departed)
2012/09/14 18:43:26
We hold a pointer to the KillDatabaseErrorDelegate
|
| + Backend* backend_; |
| + scoped_refptr<sql::ErrorDelegate> wrapped_delegate_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(KillDatabaseErrorDelegate); |
| + }; |
| + |
| // You should call Close() before destructing this object. |
| ~Backend() { |
| DCHECK(!db_.get()) << "Close should have already been called."; |
| @@ -189,8 +215,11 @@ class SQLitePersistentCookieStore::Backend |
| void DeleteSessionCookiesOnShutdown(); |
| + void KillDatabase(); |
| + |
| FilePath path_; |
| scoped_ptr<sql::Connection> db_; |
| + scoped_refptr<KillDatabaseErrorDelegate> error_delegate_; |
| sql::MetaTable meta_table_; |
| typedef std::list<PendingOperation*> PendingOperationsList; |
| @@ -247,6 +276,99 @@ class SQLitePersistentCookieStore::Backend |
| DISALLOW_COPY_AND_ASSIGN(Backend); |
| }; |
| +SQLitePersistentCookieStore::Backend::KillDatabaseErrorDelegate:: |
| +KillDatabaseErrorDelegate(Backend* backend, |
| + sql::ErrorDelegate* wrapped_delegate) |
| + : backend_(backend), |
| + wrapped_delegate_(wrapped_delegate) { |
| +} |
| + |
| +int SQLitePersistentCookieStore::Backend::KillDatabaseErrorDelegate::OnError( |
| + int error, sql::Connection* connection, sql::Statement* stmt) { |
| + if (wrapped_delegate_.get()) |
| + error = wrapped_delegate_->OnError(error, connection, stmt); |
| + |
| + bool delete_db = false; |
| + |
| + switch (error) { |
| + case SQLITE_DONE: |
| + case SQLITE_OK: |
| + // Theoretically, the wrapped delegate might have resolved the error, and |
| + // we would end up here. |
| + break; |
| + |
| + case SQLITE_CORRUPT: |
| + case SQLITE_NOTADB: |
| + // Highly unlikely we would ever recover from these. |
| + delete_db = true; |
| + break; |
| + |
| + |
| + case SQLITE_ERROR: |
|
Scott Hess - ex-Googler
2012/09/14 17:01:34
This an SQLITE_CONSTRAINT are _probably_ cases whe
|
| + // TODO(erikwright): Figure out whether this is likely to be recoverable. |
| + break; |
| + |
| + case SQLITE_CANTOPEN: |
| + // TODO(erikwright): Figure out what this means. |
| + break; |
|
Scott Hess - ex-Googler
2012/09/14 17:01:34
Dammit, I wish I remember where I was seeing this,
|
| + |
| + case SQLITE_IOERR: |
| + case SQLITE_BUSY: |
| + // Presumably transient. |
| + break; |
|
Scott Hess - ex-Googler
2012/09/14 17:01:34
It is possible to write the code such that SQLITE_
erikwright (departed)
2012/09/14 18:11:02
SQLITE_IOERR is a relatively commonly occurring er
|
| + |
| + case SQLITE_TOOBIG: |
| + case SQLITE_FULL: |
| + case SQLITE_NOMEM: |
| + // Not a problem with the database. |
| + break; |
| + |
| + case SQLITE_READONLY: |
| + // Presumably either transient or we don't have the privileges to |
| + // move/delete the file anyway. |
| + break; |
|
Scott Hess - ex-Googler
2012/09/14 17:01:34
We might be able to delete a read-only file. Depe
erikwright (departed)
2012/09/14 18:11:02
It is a commonly occuring error. Again, I'll dig i
|
| + |
| + case SQLITE_CONSTRAINT: |
| + // Presumably there is a schema upgrade failure. |
| + // TODO(erikwright): Shall we kill the DB as an easy way out? |
| + break; |
| + |
| + case SQLITE_LOCKED: |
| + case SQLITE_INTERNAL: |
| + case SQLITE_PERM: |
| + case SQLITE_ABORT: |
| + case SQLITE_INTERRUPT: |
| + case SQLITE_NOTFOUND: |
| + case SQLITE_PROTOCOL: |
| + case SQLITE_EMPTY: |
| + case SQLITE_SCHEMA: |
| + case SQLITE_MISMATCH: |
| + case SQLITE_MISUSE: |
| + case SQLITE_NOLFS: |
| + case SQLITE_AUTH: |
| + case SQLITE_FORMAT: |
| + case SQLITE_RANGE: |
| + case SQLITE_ROW: |
| + // None of these appear in error reports, so for now let's not try to |
| + // guess at how to handle them. |
| + break; |
| + } |
| + |
| + if (delete_db && backend_) { |
| + // Don't just do the close/delete here, as we are being called by |db| and |
| + // that seems dangerous. |
| + MessageLoop::current()->PostTask( |
| + FROM_HERE, base::Bind(&Backend::KillDatabase, backend_)); |
| + |
| + // Avoid being called more than once. There should still be a reference to |
| + // this ErrorDelegate in the backend, but just in case don't refer to any |
| + // members from here forward. |
| + connection->set_error_delegate(wrapped_delegate_.get()); |
| + } |
| + |
| + return error; |
| +} |
| + |
| // Version number of the database. |
| // |
| // Version 5 adds the columns has_expires and is_persistent, so that the |
| @@ -494,14 +616,14 @@ bool SQLitePersistentCookieStore::Backend::InitializeDatabase() { |
| } |
| db_.reset(new sql::Connection); |
| + db_->set_error_delegate(error_delegate_.get()); |
| + |
| if (!db_->Open(path_)) { |
| NOTREACHED() << "Unable to open cookie DB."; |
| db_.reset(); |
| return false; |
| } |
| - db_->set_error_delegate(GetErrorHandlerForCookieDb()); |
| - |
| if (!EnsureDatabaseVersion() || !InitTable(db_.get())) { |
| NOTREACHED() << "Unable to open cookie DB."; |
| db_.reset(); |
| @@ -925,6 +1047,10 @@ void SQLitePersistentCookieStore::Backend::InternalBackgroundClose() { |
| } |
| db_.reset(); |
| + if (error_delegate_.get()) { |
| + error_delegate_->reset_backend(); |
| + error_delegate_ = NULL; |
| + } |
| } |
| void SQLitePersistentCookieStore::Backend::DeleteSessionCookiesOnShutdown() { |
| @@ -968,6 +1094,20 @@ void SQLitePersistentCookieStore::Backend::DeleteSessionCookiesOnShutdown() { |
| LOG(WARNING) << "Unable to delete cookies on shutdown."; |
| } |
| +void SQLitePersistentCookieStore::Backend::KillDatabase() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::DB)); |
| + |
| + if (db_.get()) { |
| + // This Backend will now be in-memory only. In a future run we will recreate |
| + // the database. Hopefully things go better then! |
| + db_->Close(); |
| + db_.reset(); |
| + } |
| + |
| + bool success = file_util::Delete(path_, false); |
|
Scott Hess - ex-Googler
2012/09/14 17:01:34
Suggest doing db_.Raze() before closing it. That
erikwright (departed)
2012/09/14 18:11:02
We can do the Raze for now and see what happens. W
|
| + UMA_HISTOGRAM_BOOLEAN("Cookie.KillDatabaseResult", success); |
| +} |
| + |
| void SQLitePersistentCookieStore::Backend::SetForceKeepSessionState() { |
| base::AutoLock locked(lock_); |
| force_keep_session_state_ = true; |