Chromium Code Reviews| Index: sql/recovery.cc |
| diff --git a/sql/recovery.cc b/sql/recovery.cc |
| index 5d5700262c2b71354908c9c4a0861c3e85be4e11..5d6e5fae5251f7ac07da7e85a0deeb7a7bfbf6d0 100644 |
| --- a/sql/recovery.cc |
| +++ b/sql/recovery.cc |
| @@ -108,6 +108,21 @@ enum RecoveryEventType { |
| // Failed to recover triggers or views or virtual tables. |
| RECOVERY_FAILED_AUTORECOVERDB_AUX, |
| + // After SQLITE_NOTADB failure setting up for recovery, Delete() failed. |
| + RECOVERY_FAILED_AUTORECOVERDB_NOTADB_DELETE, |
| + |
| + // After SQLITE_NOTADB failure setting up for recovery, Delete() succeeded |
| + // then Open() failed. |
| + RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN, |
| + |
| + // After SQLITE_NOTADB failure setting up for recovery, Delete() and Open() |
| + // succeeded, then querying the database failed. |
| + RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY, |
| + |
| + // After SQLITE_NOTADB failure setting up for recovery, the database was |
| + // successfully deleted. |
| + RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE, |
| + |
| // Always keep this at the end. |
| RECOVERY_EVENT_MAX, |
| }; |
| @@ -588,9 +603,48 @@ void Recovery::RecoverDatabase(Connection* db, |
| const base::FilePath& db_path) { |
| std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); |
| if (!recovery) { |
| - // TODO(shess): If recovery can't even get started, Raze() or Delete(). |
| - RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_BEGIN); |
| + // Close the underlying sqlite* handle. Windows does not allow deleting |
| + // open files, and all platforms block opening a second sqlite3* handle |
| + // against a database when exclusive locking is set. |
| db->Poison(); |
| + |
| + // Histograms from Recovery::Begin() show all current failures are in |
|
pwnall
2017/02/27 23:54:51
This is a really helpful comment! Thank you!
|
| + // attaching the corrupt database, with 2/3 being SQLITE_NOTADB. Don't |
| + // delete the database except for that specific failure case. |
| + { |
| + Connection probe_db; |
| + if (!probe_db.OpenInMemory() || |
| + probe_db.AttachDatabase(db_path, "corrupt") || |
| + probe_db.GetErrorCode() != SQLITE_NOTADB) { |
| + RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_BEGIN); |
| + return; |
| + } |
| + } |
| + |
| + // The database has invalid data in the SQLite header, so it is almost |
| + // certainly not recoverable without manual intervention (and likely not |
| + // recoverable _with_ manual intervention). Clear away the broken database. |
| + if (!sql::Connection::Delete(db_path)) { |
| + RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_DELETE); |
| + return; |
| + } |
| + |
| + // Query to verify that the database no longer throws SQLITE_NOTADB. |
| + { |
| + Connection probe_db; |
| + if (!probe_db.Open(db_path)) { |
| + RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN); |
| + return; |
| + } |
| + if (!probe_db.Execute("PRAGMA auto_vacuum")) { |
| + RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY); |
| + return; |
| + } |
| + } |
| + |
| + // The rest of the recovery code could be run on the re-opened database, but |
| + // the database is empty, so there would be no point. |
| + RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE); |
| return; |
| } |