1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sql/recovery.h"
9 #include "base/files/file_path.h"
10 #include "base/format_macros.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram_functions.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h"
16 #include "sql/database.h"
17 #include "sql/statement.h"
18 #include "third_party/sqlite/sqlite3.h"
19 #include "third_party/sqlite/src/src/recover.h"
25 // This enum must match the numbering for Sqlite.RecoveryEvents in
26 // histograms.xml. Do not reorder or remove items, only add new items before
27 // RECOVERY_EVENT_MAX.
28 enum RecoveryEventType {
29 // Init() completed successfully.
30 RECOVERY_SUCCESS_INIT = 0,
32 // Failed to open temporary database to recover into.
33 RECOVERY_FAILED_OPEN_TEMPORARY,
35 // Failed to initialize recover vtable system.
36 RECOVERY_FAILED_VIRTUAL_TABLE_INIT,
38 // System SQLite doesn't support vtable.
39 // This is deprecated. Chrome doesn't support using the system SQLite anymore.
40 DEPRECATED_RECOVERY_FAILED_VIRTUAL_TABLE_SYSTEM_SQLITE,
42 // Failed attempting to enable writable_schema.
43 RECOVERY_FAILED_WRITABLE_SCHEMA,
45 // Failed to attach the corrupt database to the temporary database.
46 RECOVERY_FAILED_ATTACH,
48 // Backup() successfully completed.
49 RECOVERY_SUCCESS_BACKUP,
51 // Failed sqlite3_backup_init(). Error code in Sqlite.RecoveryHandle.
52 RECOVERY_FAILED_BACKUP_INIT,
54 // Failed sqlite3_backup_step(). Error code in Sqlite.RecoveryStep.
55 RECOVERY_FAILED_BACKUP_STEP,
57 // AutoRecoverTable() successfully completed.
58 RECOVERY_SUCCESS_AUTORECOVER,
60 // The target table contained a type which the code is not equipped
61 // to handle. This should only happen if things are fubar.
62 RECOVERY_FAILED_AUTORECOVER_UNRECOGNIZED_TYPE,
64 // The target table does not exist.
65 RECOVERY_FAILED_AUTORECOVER_MISSING_TABLE,
67 // The recovery virtual table creation failed.
68 RECOVERY_FAILED_AUTORECOVER_CREATE,
70 // Copying data from the recovery table to the target table failed.
71 RECOVERY_FAILED_AUTORECOVER_INSERT,
73 // Dropping the recovery virtual table failed.
74 RECOVERY_FAILED_AUTORECOVER_DROP,
76 // SetupMeta() successfully completed.
77 RECOVERY_SUCCESS_SETUP_META,
79 // Failure creating recovery meta table.
80 RECOVERY_FAILED_META_CREATE,
82 // GetMetaVersionNumber() successfully completed.
83 RECOVERY_SUCCESS_META_VERSION,
85 // Failed in querying recovery meta table.
86 RECOVERY_FAILED_META_QUERY,
88 // No version key in recovery meta table.
89 RECOVERY_FAILED_META_NO_VERSION,
91 // Automatically recovered entire database successfully.
92 RECOVERY_SUCCESS_AUTORECOVERDB,
94 // Database was so broken recovery couldn't be entered.
95 RECOVERY_FAILED_AUTORECOVERDB_BEGIN,
97 // Failed to schema from corrupt database.
98 RECOVERY_FAILED_AUTORECOVERDB_SCHEMASELECT,
100 // Failed to create copy of schema in recovery database.
101 RECOVERY_FAILED_AUTORECOVERDB_SCHEMACREATE,
103 // Failed querying tables to recover. Should be impossible.
104 RECOVERY_FAILED_AUTORECOVERDB_NAMESELECT,
106 // Failed to recover an individual table.
107 RECOVERY_FAILED_AUTORECOVERDB_TABLE,
109 // Failed to recover [sqlite_sequence] table.
110 RECOVERY_FAILED_AUTORECOVERDB_SEQUENCE,
112 // Failed to recover triggers or views or virtual tables.
113 RECOVERY_FAILED_AUTORECOVERDB_AUX,
115 // After SQLITE_NOTADB failure setting up for recovery, Delete() failed.
116 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_DELETE,
118 // After SQLITE_NOTADB failure setting up for recovery, Delete() succeeded
119 // then Open() failed.
120 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN,
122 // After SQLITE_NOTADB failure setting up for recovery, Delete() and Open()
123 // succeeded, then querying the database failed.
124 RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY,
126 // After SQLITE_NOTADB failure setting up for recovery, the database was
127 // successfully deleted.
128 RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE,
130 // Failed to find required [meta.version] information.
131 RECOVERY_FAILED_AUTORECOVERDB_META_VERSION,
133 // Add new items before this one, always keep this one at the end.
137 void RecordRecoveryEvent(RecoveryEventType recovery_event) {
138 UMA_HISTOGRAM_ENUMERATION("Sqlite.RecoveryEvents",
139 recovery_event, RECOVERY_EVENT_MAX);
145 std::unique_ptr<Recovery> Recovery::Begin(Database* database,
146 const base::FilePath& db_path) {
147 // Recovery is likely to be used in error handling. Since recovery changes
148 // the state of the handle, protect against multiple layers attempting the
150 if (!database->is_open()) {
151 // Warn about API mis-use.
152 DCHECK(database->poisoned(InternalApiToken()))
153 << "Illegal to recover with closed Database";
154 return std::unique_ptr<Recovery>();
157 // Using `new` to access a non-public constructor
158 std::unique_ptr<Recovery> recovery(new Recovery(database));
159 if (!recovery->Init(db_path)) {
160 // TODO(shess): Should Init() failure result in Raze()?
161 recovery->Shutdown(POISON);
162 return std::unique_ptr<Recovery>();
169 bool Recovery::Recovered(std::unique_ptr<Recovery> r) {
174 void Recovery::Unrecoverable(std::unique_ptr<Recovery> r) {
176 // ~Recovery() will RAZE_AND_POISON.
180 void Recovery::Rollback(std::unique_ptr<Recovery> r) {
181 // TODO(shess): HISTOGRAM to track? Or just have people crash out?
186 Recovery::Recovery(Database* connection) : db_(connection), recover_db_() {
187 // Result should keep the page size specified earlier.
188 recover_db_.set_page_size(db_->page_size());
190 // Files with I/O errors cannot be safely memory-mapped.
191 recover_db_.set_mmap_disabled();
193 // TODO(shess): This may not handle cases where the default page
194 // size is used, but the default has changed. I do not think this
195 // has ever happened. This could be handled by using "PRAGMA
196 // page_size", at the cost of potential additional failure cases.
199 Recovery::~Recovery() {
200 Shutdown(RAZE_AND_POISON);
203 bool Recovery::Init(const base::FilePath& db_path) {
204 // Prevent the possibility of re-entering this code due to errors
205 // which happen while executing this code.
206 DCHECK(!db_->has_error_callback());
208 // Break any outstanding transactions on the original database to
209 // prevent deadlocks reading through the attached version.
210 // TODO(shess): A client may legitimately wish to recover from
211 // within the transaction context, because it would potentially
212 // preserve any in-flight changes. Unfortunately, any attach-based
213 // system could not handle that. A system which manually queried
214 // one database and stored to the other possibly could, but would be
216 db_->RollbackAllTransactions();
218 // Disable exclusive locking mode so that the attached database can
219 // access things. The locking_mode change is not active until the
220 // next database access, so immediately force an access. Enabling
221 // writable_schema allows processing through certain kinds of
223 // TODO(shess): It would be better to just close the handle, but it
224 // is necessary for the final backup which rewrites things. It
225 // might be reasonable to close then re-open the handle.
226 ignore_result(db_->Execute("PRAGMA writable_schema=1"));
227 ignore_result(db_->Execute("PRAGMA locking_mode=NORMAL"));
228 ignore_result(db_->Execute("SELECT COUNT(*) FROM sqlite_master"));
230 // TODO(shess): If this is a common failure case, it might be
231 // possible to fall back to a memory database. But it probably
232 // implies that the SQLite tmpdir logic is busted, which could cause
233 // a variety of other random issues in our code.
234 if (!recover_db_.OpenTemporary()) {
235 RecordRecoveryEvent(RECOVERY_FAILED_OPEN_TEMPORARY);
239 // Enable the recover virtual table for this connection.
240 int rc = chrome_sqlite3_recoverVtableInit(recover_db_.db(InternalApiToken()));
241 if (rc != SQLITE_OK) {
242 RecordRecoveryEvent(RECOVERY_FAILED_VIRTUAL_TABLE_INIT);
243 LOG(ERROR) << "Failed to initialize recover module: "
244 << recover_db_.GetErrorMessage();
248 // Turn on |SQLITE_RecoveryMode| for the handle, which allows
249 // reading certain broken databases.
250 if (!recover_db_.Execute("PRAGMA writable_schema=1")) {
251 RecordRecoveryEvent(RECOVERY_FAILED_WRITABLE_SCHEMA);
255 if (!recover_db_.AttachDatabase(db_path, "corrupt", InternalApiToken())) {
256 RecordRecoveryEvent(RECOVERY_FAILED_ATTACH);
257 base::UmaHistogramSparse("Sqlite.RecoveryAttachError",
258 recover_db_.GetErrorCode());
262 RecordRecoveryEvent(RECOVERY_SUCCESS_INIT);
266 bool Recovery::Backup() {
268 CHECK(recover_db_.is_open());
270 // TODO(shess): Some of the failure cases here may need further
271 // exploration. Just as elsewhere, persistent problems probably
272 // need to be razed, while anything which might succeed on a future
273 // run probably should be allowed to try. But since Raze() uses the
274 // same approach, even that wouldn't work when this code fails.
276 // The documentation for the backup system indicate a relatively
277 // small number of errors are expected:
278 // SQLITE_BUSY - cannot lock the destination database. This should
279 // only happen if someone has another handle to the
280 // database, Chromium generally doesn't do that.
281 // SQLITE_LOCKED - someone locked the source database. Should be
282 // impossible (perhaps anti-virus could?).
283 // SQLITE_READONLY - destination is read-only.
284 // SQLITE_IOERR - since source database is temporary, probably
285 // indicates that the destination contains blocks
286 // throwing errors, or gross filesystem errors.
287 // SQLITE_NOMEM - out of memory, should be transient.
289 // AFAICT, SQLITE_BUSY and SQLITE_NOMEM could perhaps be considered
290 // transient, with SQLITE_LOCKED being unclear.
292 // SQLITE_READONLY and SQLITE_IOERR are probably persistent, with a
293 // strong chance that Raze() would not resolve them. If Delete()
294 // deletes the database file, the code could then re-open the file
295 // and attempt the backup again.
297 // For now, this code attempts a best effort and records histograms
298 // to inform future development.
300 // Backup the original db from the recovered db.
301 const char* kMain = "main";
302 sqlite3_backup* backup =
303 sqlite3_backup_init(db_->db(InternalApiToken()), kMain,
304 recover_db_.db(InternalApiToken()), kMain);
306 RecordRecoveryEvent(RECOVERY_FAILED_BACKUP_INIT);
308 // Error code is in the destination database handle.
309 int err = sqlite3_extended_errcode(db_->db(InternalApiToken()));
310 base::UmaHistogramSparse("Sqlite.RecoveryHandle", err);
311 LOG(ERROR) << "sqlite3_backup_init() failed: "
312 << sqlite3_errmsg(db_->db(InternalApiToken()));
317 // -1 backs up the entire database.
318 int rc = sqlite3_backup_step(backup, -1);
319 int pages = sqlite3_backup_pagecount(backup);
320 // TODO(shess): sqlite3_backup_finish() appears to allow returning a
321 // different value from sqlite3_backup_step(). Circle back and
322 // figure out if that can usefully inform the decision of whether to
324 sqlite3_backup_finish(backup);
327 if (rc != SQLITE_DONE) {
328 RecordRecoveryEvent(RECOVERY_FAILED_BACKUP_STEP);
329 base::UmaHistogramSparse("Sqlite.RecoveryStep", rc);
330 LOG(ERROR) << "sqlite3_backup_step() failed: "
331 << sqlite3_errmsg(db_->db(InternalApiToken()));
334 // The destination database was locked. Give up, but leave the data
335 // in place. Maybe it won't be locked next time.
336 if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {
341 // Running out of memory should be transient, retry later.
342 if (rc == SQLITE_NOMEM) {
347 // TODO(shess): For now, leave the original database alone, pending
348 // results from Sqlite.RecoveryStep. Some errors should probably
349 // route to RAZE_AND_POISON.
350 if (rc != SQLITE_DONE) {
355 // Clean up the recovery db, and terminate the main database
357 RecordRecoveryEvent(RECOVERY_SUCCESS_BACKUP);
362 void Recovery::Shutdown(Recovery::Disposition raze) {
367 if (raze == RAZE_AND_POISON) {
369 } else if (raze == POISON) {
375 bool Recovery::AutoRecoverTable(const char* table_name,
376 size_t* rows_recovered) {
377 // Query the info for the recovered table in database [main].
379 base::StringPrintf("PRAGMA main.table_info(%s)", table_name));
380 Statement s(db()->GetUniqueStatement(query.c_str()));
382 // The columns of the recover virtual table.
383 std::vector<std::string> create_column_decls;
385 // The columns to select from the recover virtual table when copying
386 // to the recovered table.
387 std::vector<std::string> insert_columns;
389 // If PRIMARY KEY is a single INTEGER column, then it is an alias
390 // for ROWID. The primary key can be compound, so this can only be
391 // determined after processing all column data and tracking what is
392 // seen. |pk_column_count| counts the columns in the primary key.
393 // |rowid_decl| stores the ROWID version of the last INTEGER column
394 // seen, which is at |rowid_ofs| in |create_column_decls|.
395 size_t pk_column_count = 0;
396 size_t rowid_ofs = 0; // Only valid if rowid_decl is set.
397 std::string rowid_decl; // ROWID version of column |rowid_ofs|.
400 const std::string column_name(s.ColumnString(1));
401 const std::string column_type(s.ColumnString(2));
402 const int default_type = s.ColumnType(4);
403 const bool default_is_null = (default_type == COLUMN_TYPE_NULL);
404 const int pk_column = s.ColumnInt(5);
406 // http://www.sqlite.org/pragma.html#pragma_table_info documents column 5 as
407 // the 1-based index of the column in the primary key, otherwise 0.
411 // Construct column declaration as "name type [optional constraint]".
412 std::string column_decl = column_name;
414 // SQLite's affinity detection is documented at:
415 // http://www.sqlite.org/datatype3.html#affname
416 // The gist of it is that CHAR, TEXT, and INT use substring matches.
417 // TODO(shess): It would be nice to unit test the type handling,
418 // but it is not obvious to me how to write a test which would
419 // fail appropriately when something was broken. It would have to
420 // somehow use data which would allow detecting the various type
421 // coercions which happen. If STRICT could be enabled, type
422 // mismatches could be detected by which rows are filtered.
423 if (column_type.find("INT") != std::string::npos) {
424 if (pk_column == 1) {
425 rowid_ofs = create_column_decls.size();
426 rowid_decl = column_name + " ROWID";
428 column_decl += " INTEGER";
429 } else if (column_type.find("CHAR") != std::string::npos ||
430 column_type.find("TEXT") != std::string::npos) {
431 column_decl += " TEXT";
432 } else if (column_type == "BLOB") {
433 column_decl += " BLOB";
434 } else if (column_type.find("DOUB") != std::string::npos) {
435 column_decl += " FLOAT";
437 // TODO(shess): AFAICT, there remain:
438 // - contains("CLOB") -> TEXT
439 // - contains("REAL") -> FLOAT
440 // - contains("FLOA") -> FLOAT
441 // - other -> "NUMERIC"
442 // Just code those in as they come up.
443 NOTREACHED() << " Unsupported type " << column_type;
444 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_UNRECOGNIZED_TYPE);
448 create_column_decls.push_back(column_decl);
450 // Per the NOTE in the header file, convert NULL values to the
451 // DEFAULT. All columns could be IFNULL(column_name,default), but
452 // the NULL case would require special handling either way.
453 if (default_is_null) {
454 insert_columns.push_back(column_name);
456 // The default value appears to be pre-quoted, as if it is
457 // literally from the sqlite_master CREATE statement.
458 std::string default_value = s.ColumnString(4);
459 insert_columns.push_back(base::StringPrintf(
460 "IFNULL(%s,%s)", column_name.c_str(), default_value.c_str()));
464 // Receiving no column information implies that the table doesn't exist.
465 if (create_column_decls.empty()) {
466 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_MISSING_TABLE);
470 // If the PRIMARY KEY was a single INTEGER column, convert it to ROWID.
471 if (pk_column_count == 1 && !rowid_decl.empty())
472 create_column_decls[rowid_ofs] = rowid_decl;
474 std::string recover_create(base::StringPrintf(
475 "CREATE VIRTUAL TABLE temp.recover_%s USING recover(corrupt.%s, %s)",
478 base::JoinString(create_column_decls, ",").c_str()));
480 // INSERT OR IGNORE means that it will drop rows resulting from constraint
481 // violations. INSERT OR REPLACE only handles UNIQUE constraint violations.
482 std::string recover_insert(base::StringPrintf(
483 "INSERT OR IGNORE INTO main.%s SELECT %s FROM temp.recover_%s",
485 base::JoinString(insert_columns, ",").c_str(),
488 std::string recover_drop(base::StringPrintf(
489 "DROP TABLE temp.recover_%s", table_name));
491 if (!db()->Execute(recover_create.c_str())) {
492 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_CREATE);
496 if (!db()->Execute(recover_insert.c_str())) {
497 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_INSERT);
498 ignore_result(db()->Execute(recover_drop.c_str()));
502 *rows_recovered = db()->GetLastChangeCount();
504 // TODO(shess): Is leaving the recover table around a breaker?
505 if (!db()->Execute(recover_drop.c_str())) {
506 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_DROP);
509 RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVER);
513 bool Recovery::SetupMeta() {
514 static const char kCreateSql[] =
515 "CREATE VIRTUAL TABLE temp.recover_meta USING recover"
519 "value ANY" // Whatever is stored.
521 if (!db()->Execute(kCreateSql)) {
522 RecordRecoveryEvent(RECOVERY_FAILED_META_CREATE);
525 RecordRecoveryEvent(RECOVERY_SUCCESS_SETUP_META);
529 bool Recovery::GetMetaVersionNumber(int* version) {
531 // TODO(shess): DCHECK(db()->DoesTableExist("temp.recover_meta"));
532 // Unfortunately, DoesTableExist() queries sqlite_master, not
533 // sqlite_temp_master.
535 static const char kVersionSql[] =
536 "SELECT value FROM temp.recover_meta WHERE key = 'version'";
537 sql::Statement recovery_version(db()->GetUniqueStatement(kVersionSql));
538 if (!recovery_version.Step()) {
539 if (!recovery_version.Succeeded()) {
540 RecordRecoveryEvent(RECOVERY_FAILED_META_QUERY);
542 RecordRecoveryEvent(RECOVERY_FAILED_META_NO_VERSION);
547 RecordRecoveryEvent(RECOVERY_SUCCESS_META_VERSION);
548 *version = recovery_version.ColumnInt(0);
554 // Collect statements from [corrupt.sqlite_master.sql] which start with |prefix|
555 // (which should be a valid SQL string ending with the space before a table
556 // name), then apply the statements to [main]. Skip any table named
557 // 'sqlite_sequence', as that table is created on demand by SQLite if any tables
558 // use AUTOINCREMENT.
560 // Returns |true| if all of the matching items were created in the main
561 // database. Returns |false| if an item fails on creation, or if the corrupt
562 // database schema cannot be queried.
563 bool SchemaCopyHelper(Database* db, const char* prefix) {
564 const size_t prefix_len = strlen(prefix);
565 DCHECK_EQ(' ', prefix[prefix_len-1]);
567 sql::Statement s(db->GetUniqueStatement(
568 "SELECT DISTINCT sql FROM corrupt.sqlite_master "
569 "WHERE name<>'sqlite_sequence'"));
571 std::string sql = s.ColumnString(0);
573 // Skip statements that don't start with |prefix|.
574 if (sql.compare(0, prefix_len, prefix) != 0)
577 sql.insert(prefix_len, "main.");
578 if (!db->Execute(sql.c_str())) {
579 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_SCHEMACREATE);
583 if (!s.Succeeded()) {
584 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_SCHEMASELECT);
592 // This method is derived from SQLite's vacuum.c. VACUUM operates very
593 // similarily, creating a new database, populating the schema, then copying the
596 // TODO(shess): This conservatively uses Rollback() rather than Unrecoverable().
597 // With Rollback(), it is expected that the database will continue to generate
598 // errors. Change the failure cases to Unrecoverable() if/when histogram
599 // results indicate that everything is working reasonably.
602 std::unique_ptr<Recovery> Recovery::BeginRecoverDatabase(
604 const base::FilePath& db_path) {
605 std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path);
607 // Close the underlying sqlite* handle. Windows does not allow deleting
608 // open files, and all platforms block opening a second sqlite3* handle
609 // against a database when exclusive locking is set.
612 // Histograms from Recovery::Begin() show all current failures are in
613 // attaching the corrupt database, with 2/3 being SQLITE_NOTADB. Don't
614 // delete the database except for that specific failure case.
617 if (!probe_db.OpenInMemory() ||
618 probe_db.AttachDatabase(db_path, "corrupt", InternalApiToken()) ||
619 probe_db.GetErrorCode() != SQLITE_NOTADB) {
620 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_BEGIN);
625 // The database has invalid data in the SQLite header, so it is almost
626 // certainly not recoverable without manual intervention (and likely not
627 // recoverable _with_ manual intervention). Clear away the broken database.
628 if (!sql::Database::Delete(db_path)) {
629 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_DELETE);
633 // Windows deletion is complicated by file scanners and malware - sometimes
634 // Delete() appears to succeed, even though the file remains. The following
635 // attempts to track if this happens often enough to cause concern.
638 if (!probe_db.Open(db_path)) {
639 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN);
642 if (!probe_db.Execute("PRAGMA auto_vacuum")) {
643 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY);
648 // The rest of the recovery code could be run on the re-opened database, but
649 // the database is empty, so there would be no point.
650 RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE);
655 // This code silently fails to recover fts3 virtual tables. At this time no
656 // browser database contain fts3 tables. Just to be safe, complain loudly if
657 // the database contains virtual tables.
659 // fts3 has an [x_segdir] table containing a column [end_block INTEGER]. But
660 // it actually stores either an integer or a text containing a pair of
661 // integers separated by a space. AutoRecoverTable() trusts the INTEGER tag
662 // when setting up the recover vtable, so those rows get dropped. Setting
663 // that column to ANY may work.
665 sql::Statement s(db->GetUniqueStatement(
666 "SELECT 1 FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE %'"));
667 DCHECK(!s.Step()) << "Recovery of virtual tables not supported";
671 // TODO(shess): vacuum.c turns off checks and foreign keys.
673 // TODO(shess): vacuum.c turns synchronous=OFF for the target. I do not fully
674 // understand this, as the temporary db should not have a journal file at all.
675 // Perhaps it does in case of cache spill?
677 // Copy table schema from [corrupt] to [main].
678 if (!SchemaCopyHelper(recovery->db(), "CREATE TABLE ") ||
679 !SchemaCopyHelper(recovery->db(), "CREATE INDEX ") ||
680 !SchemaCopyHelper(recovery->db(), "CREATE UNIQUE INDEX ")) {
681 // No RecordRecoveryEvent() here because SchemaCopyHelper() already did.
682 Recovery::Rollback(std::move(recovery));
686 // Run auto-recover against each table, skipping the sequence table. This is
687 // necessary because table recovery can create the sequence table as a side
688 // effect, so recovering that table inline could lead to duplicate data.
690 sql::Statement s(recovery->db()->GetUniqueStatement(
691 "SELECT name FROM sqlite_master WHERE sql LIKE 'CREATE TABLE %' "
692 "AND name!='sqlite_sequence'"));
694 const std::string name = s.ColumnString(0);
695 size_t rows_recovered;
696 if (!recovery->AutoRecoverTable(name.c_str(), &rows_recovered)) {
697 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_TABLE);
698 Recovery::Rollback(std::move(recovery));
702 if (!s.Succeeded()) {
703 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NAMESELECT);
704 Recovery::Rollback(std::move(recovery));
709 // Overwrite any sequences created.
710 if (recovery->db()->DoesTableExist("corrupt.sqlite_sequence")) {
711 ignore_result(recovery->db()->Execute("DELETE FROM main.sqlite_sequence"));
712 size_t rows_recovered;
713 if (!recovery->AutoRecoverTable("sqlite_sequence", &rows_recovered)) {
714 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_SEQUENCE);
715 Recovery::Rollback(std::move(recovery));
720 // Copy triggers and views directly to sqlite_master. Any tables they refer
721 // to should already exist.
722 static const char kCreateMetaItemsSql[] =
723 "INSERT INTO main.sqlite_master "
724 "SELECT type, name, tbl_name, rootpage, sql "
725 "FROM corrupt.sqlite_master WHERE type='view' OR type='trigger'";
726 if (!recovery->db()->Execute(kCreateMetaItemsSql)) {
727 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_AUX);
728 Recovery::Rollback(std::move(recovery));
732 RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVERDB);
736 void Recovery::RecoverDatabase(Database* db, const base::FilePath& db_path) {
737 std::unique_ptr<sql::Recovery> recovery = BeginRecoverDatabase(db, db_path);
739 // ignore_result() because BeginRecoverDatabase() and Recovered() already
740 // provide suitable histogram coverage.
742 ignore_result(Recovery::Recovered(std::move(recovery)));
745 void Recovery::RecoverDatabaseWithMetaVersion(Database* db,
746 const base::FilePath& db_path) {
747 std::unique_ptr<sql::Recovery> recovery = BeginRecoverDatabase(db, db_path);
752 if (!recovery->SetupMeta() || !recovery->GetMetaVersionNumber(&version)) {
753 sql::Recovery::Unrecoverable(std::move(recovery));
754 RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_META_VERSION);
758 // ignore_result() because BeginRecoverDatabase() and Recovered() already
759 // provide suitable histogram coverage.
760 ignore_result(Recovery::Recovered(std::move(recovery)));
764 bool Recovery::ShouldRecover(int extended_error) {
765 // Trim extended error codes.
766 int error = extended_error & 0xFF;
769 // SQLITE_NOTADB happens if the SQLite header is broken. Some earlier
770 // versions of SQLite return this where other versions return
771 // SQLITE_CORRUPT, which is a recoverable case. Later versions only
772 // return this error only in unrecoverable cases, in which case recovery
773 // will fail with no changes to the database, so there's no harm in
774 // attempting recovery in this case.
778 // SQLITE_CORRUPT generally means that the database is readable as a
779 // SQLite database, but some inconsistency has been detected by SQLite.
780 // In many cases the inconsistency is relatively trivial, such as if an
781 // index refers to a row which was deleted, in which case most or even all
782 // of the data can be recovered. This can also be reported if parts of
783 // the file have been overwritten with garbage data, in which recovery
784 // should be able to recover partial data.
787 // TODO(shess): Possible future options for automated fixing:
788 // - SQLITE_CANTOPEN - delete the broken symlink or directory.
789 // - SQLITE_PERM - permissions could be fixed.
790 // - SQLITE_READONLY - permissions could be fixed.
791 // - SQLITE_IOERR - rewrite using new blocks.
792 // - SQLITE_FULL - recover in memory and rewrite subset of data.