| Index: sql/recovery.cc
|
| diff --git a/sql/recovery.cc b/sql/recovery.cc
|
| index 5d5700262c2b71354908c9c4a0861c3e85be4e11..a805c520a88ba15ff8e47b2e6f2ea0b4654f0b21 100644
|
| --- a/sql/recovery.cc
|
| +++ b/sql/recovery.cc
|
| @@ -22,6 +22,9 @@ namespace sql {
|
|
|
| namespace {
|
|
|
| +// This enum must match the numbering for Sqlite.RecoveryEvents in
|
| +// histograms.xml. Do not reorder or remove items, only add new items before
|
| +// RECOVERY_EVENT_MAX.
|
| enum RecoveryEventType {
|
| // Init() completed successfully.
|
| RECOVERY_SUCCESS_INIT = 0,
|
| @@ -108,7 +111,22 @@ enum RecoveryEventType {
|
| // Failed to recover triggers or views or virtual tables.
|
| RECOVERY_FAILED_AUTORECOVERDB_AUX,
|
|
|
| - // Always keep this at the end.
|
| + // 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,
|
| +
|
| + // Add new items before this one, always keep this one at the end.
|
| RECOVERY_EVENT_MAX,
|
| };
|
|
|
| @@ -588,9 +606,50 @@ 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
|
| + // 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;
|
| + }
|
| +
|
| + // Windows deletion is complicated by file scanners and malware - sometimes
|
| + // Delete() appears to succeed, even though the file remains. The following
|
| + // attempts to track if this happens often enough to cause concern.
|
| + {
|
| + 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;
|
| }
|
|
|
|
|