[M85 Migration] Add an evas gl option for rotation
[platform/framework/web/chromium-efl.git] / sql / recovery.cc
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.
4
5 #include "sql/recovery.h"
6
7 #include <stddef.h>
8
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/recover_module/module.h"
18 #include "sql/statement.h"
19 #include "third_party/sqlite/sqlite3.h"
20
21 namespace sql {
22
23 namespace {
24
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,
31
32   // Failed to open temporary database to recover into.
33   RECOVERY_FAILED_OPEN_TEMPORARY,
34
35   // Failed to initialize recover vtable system.
36   RECOVERY_FAILED_VIRTUAL_TABLE_INIT,
37
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,
41
42   // Failed attempting to enable writable_schema.
43   RECOVERY_FAILED_WRITABLE_SCHEMA,
44
45   // Failed to attach the corrupt database to the temporary database.
46   RECOVERY_FAILED_ATTACH,
47
48   // Backup() successfully completed.
49   RECOVERY_SUCCESS_BACKUP,
50
51   // Failed sqlite3_backup_init().  Error code in Sqlite.RecoveryHandle.
52   RECOVERY_FAILED_BACKUP_INIT,
53
54   // Failed sqlite3_backup_step().  Error code in Sqlite.RecoveryStep.
55   RECOVERY_FAILED_BACKUP_STEP,
56
57   // AutoRecoverTable() successfully completed.
58   RECOVERY_SUCCESS_AUTORECOVER,
59
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,
63
64   // The target table does not exist.
65   RECOVERY_FAILED_AUTORECOVER_MISSING_TABLE,
66
67   // The recovery virtual table creation failed.
68   RECOVERY_FAILED_AUTORECOVER_CREATE,
69
70   // Copying data from the recovery table to the target table failed.
71   RECOVERY_FAILED_AUTORECOVER_INSERT,
72
73   // Dropping the recovery virtual table failed.
74   RECOVERY_FAILED_AUTORECOVER_DROP,
75
76   // SetupMeta() successfully completed.
77   RECOVERY_SUCCESS_SETUP_META,
78
79   // Failure creating recovery meta table.
80   RECOVERY_FAILED_META_CREATE,
81
82   // GetMetaVersionNumber() successfully completed.
83   RECOVERY_SUCCESS_META_VERSION,
84
85   // Failed in querying recovery meta table.
86   RECOVERY_FAILED_META_QUERY,
87
88   // No version key in recovery meta table.
89   RECOVERY_FAILED_META_NO_VERSION,
90
91   // Automatically recovered entire database successfully.
92   RECOVERY_SUCCESS_AUTORECOVERDB,
93
94   // Database was so broken recovery couldn't be entered.
95   RECOVERY_FAILED_AUTORECOVERDB_BEGIN,
96
97   // Failed to schema from corrupt database.
98   RECOVERY_FAILED_AUTORECOVERDB_SCHEMASELECT,
99
100   // Failed to create copy of schema in recovery database.
101   RECOVERY_FAILED_AUTORECOVERDB_SCHEMACREATE,
102
103   // Failed querying tables to recover.  Should be impossible.
104   RECOVERY_FAILED_AUTORECOVERDB_NAMESELECT,
105
106   // Failed to recover an individual table.
107   RECOVERY_FAILED_AUTORECOVERDB_TABLE,
108
109   // Failed to recover [sqlite_sequence] table.
110   RECOVERY_FAILED_AUTORECOVERDB_SEQUENCE,
111
112   // Failed to recover triggers or views or virtual tables.
113   RECOVERY_FAILED_AUTORECOVERDB_AUX,
114
115   // After SQLITE_NOTADB failure setting up for recovery, Delete() failed.
116   RECOVERY_FAILED_AUTORECOVERDB_NOTADB_DELETE,
117
118   // After SQLITE_NOTADB failure setting up for recovery, Delete() succeeded
119   // then Open() failed.
120   RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN,
121
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,
125
126   // After SQLITE_NOTADB failure setting up for recovery, the database was
127   // successfully deleted.
128   RECOVERY_SUCCESS_AUTORECOVERDB_NOTADB_DELETE,
129
130   // Failed to find required [meta.version] information.
131   RECOVERY_FAILED_AUTORECOVERDB_META_VERSION,
132
133   // Add new items before this one, always keep this one at the end.
134   RECOVERY_EVENT_MAX,
135 };
136
137 void RecordRecoveryEvent(RecoveryEventType recovery_event) {
138   UMA_HISTOGRAM_ENUMERATION("Sqlite.RecoveryEvents",
139                             recovery_event, RECOVERY_EVENT_MAX);
140 }
141
142 }  // namespace
143
144 // static
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
149   // same recovery.
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>();
155   }
156
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>();
163   }
164
165   return recovery;
166 }
167
168 // static
169 bool Recovery::Recovered(std::unique_ptr<Recovery> r) {
170   return r->Backup();
171 }
172
173 // static
174 void Recovery::Unrecoverable(std::unique_ptr<Recovery> r) {
175   CHECK(r->db_);
176   // ~Recovery() will RAZE_AND_POISON.
177 }
178
179 // static
180 void Recovery::Rollback(std::unique_ptr<Recovery> r) {
181   // TODO(shess): HISTOGRAM to track?  Or just have people crash out?
182   // Crash and dump?
183   r->Shutdown(POISON);
184 }
185
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());
189
190   // Files with I/O errors cannot be safely memory-mapped.
191   recover_db_.set_mmap_disabled();
192
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.
197 }
198
199 Recovery::~Recovery() {
200   Shutdown(RAZE_AND_POISON);
201 }
202
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());
207
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
215   // more complicated.
216   db_->RollbackAllTransactions();
217
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
222   // corruption.
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"));
229
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);
236     return false;
237   }
238
239   // Enable the recover virtual table for this connection.
240   int rc = EnableRecoveryExtension(&recover_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();
245     return false;
246   }
247
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);
252     return false;
253   }
254
255   if (!recover_db_.AttachDatabase(db_path, "corrupt", InternalApiToken())) {
256     RecordRecoveryEvent(RECOVERY_FAILED_ATTACH);
257     base::UmaHistogramSparse("Sqlite.RecoveryAttachError",
258                              recover_db_.GetErrorCode());
259     return false;
260   }
261
262   RecordRecoveryEvent(RECOVERY_SUCCESS_INIT);
263   return true;
264 }
265
266 bool Recovery::Backup() {
267   CHECK(db_);
268   CHECK(recover_db_.is_open());
269
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.
275   //
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.
288   //
289   // AFAICT, SQLITE_BUSY and SQLITE_NOMEM could perhaps be considered
290   // transient, with SQLITE_LOCKED being unclear.
291   //
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.
296   //
297   // For now, this code attempts a best effort and records histograms
298   // to inform future development.
299
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);
305   if (!backup) {
306     RecordRecoveryEvent(RECOVERY_FAILED_BACKUP_INIT);
307
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()));
313
314     return false;
315   }
316
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
323   // retry or not.
324   sqlite3_backup_finish(backup);
325   DCHECK_GT(pages, 0);
326
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()));
332   }
333
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) {
337     Shutdown(POISON);
338     return false;
339   }
340
341   // Running out of memory should be transient, retry later.
342   if (rc == SQLITE_NOMEM) {
343     Shutdown(POISON);
344     return false;
345   }
346
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) {
351     Shutdown(POISON);
352     return false;
353   }
354
355   // Clean up the recovery db, and terminate the main database
356   // connection.
357   RecordRecoveryEvent(RECOVERY_SUCCESS_BACKUP);
358   Shutdown(POISON);
359   return true;
360 }
361
362 void Recovery::Shutdown(Recovery::Disposition raze) {
363   if (!db_)
364     return;
365
366   recover_db_.Close();
367   if (raze == RAZE_AND_POISON) {
368     db_->RazeAndClose();
369   } else if (raze == POISON) {
370     db_->Poison();
371   }
372   db_ = nullptr;
373 }
374
375 bool Recovery::AutoRecoverTable(const char* table_name,
376                                 size_t* rows_recovered) {
377   // Query the info for the recovered table in database [main].
378   std::string query(
379       base::StringPrintf("PRAGMA main.table_info(%s)", table_name));
380   Statement s(db()->GetUniqueStatement(query.c_str()));
381
382   // The columns of the recover virtual table.
383   std::vector<std::string> create_column_decls;
384
385   // The columns to select from the recover virtual table when copying
386   // to the recovered table.
387   std::vector<std::string> insert_columns;
388
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|.
398
399   while (s.Step()) {
400     const std::string column_name(s.ColumnString(1));
401     const std::string column_type(s.ColumnString(2));
402     const ColumnType default_type = s.GetColumnType(4);
403     const bool default_is_null = (default_type == ColumnType::kNull);
404     const int pk_column = s.ColumnInt(5);
405
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.
408     if (pk_column > 0)
409       ++pk_column_count;
410
411     // Construct column declaration as "name type [optional constraint]".
412     std::string column_decl = column_name;
413
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";
427       }
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";
436     } else {
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);
445       return false;
446     }
447
448     create_column_decls.push_back(column_decl);
449
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);
455     } else {
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()));
461     }
462   }
463
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);
467     return false;
468   }
469
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;
473
474   std::string recover_create(base::StringPrintf(
475       "CREATE VIRTUAL TABLE temp.recover_%s USING recover(corrupt.%s, %s)",
476       table_name,
477       table_name,
478       base::JoinString(create_column_decls, ",").c_str()));
479
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",
484       table_name,
485       base::JoinString(insert_columns, ",").c_str(),
486       table_name));
487
488   std::string recover_drop(base::StringPrintf(
489       "DROP TABLE temp.recover_%s", table_name));
490
491   if (!db()->Execute(recover_create.c_str())) {
492     RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_CREATE);
493     return false;
494   }
495
496   if (!db()->Execute(recover_insert.c_str())) {
497     RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVER_INSERT);
498     ignore_result(db()->Execute(recover_drop.c_str()));
499     return false;
500   }
501
502   *rows_recovered = db()->GetLastChangeCount();
503
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);
507     return false;
508   }
509   RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVER);
510   return true;
511 }
512
513 bool Recovery::SetupMeta() {
514   static const char kCreateSql[] =
515       "CREATE VIRTUAL TABLE temp.recover_meta USING recover"
516       "("
517       "corrupt.meta,"
518       "key TEXT NOT NULL,"
519       "value ANY"  // Whatever is stored.
520       ")";
521   if (!db()->Execute(kCreateSql)) {
522     RecordRecoveryEvent(RECOVERY_FAILED_META_CREATE);
523     return false;
524   }
525   RecordRecoveryEvent(RECOVERY_SUCCESS_SETUP_META);
526   return true;
527 }
528
529 bool Recovery::GetMetaVersionNumber(int* version) {
530   DCHECK(version);
531   // TODO(shess): DCHECK(db()->DoesTableExist("temp.recover_meta"));
532   // Unfortunately, DoesTableExist() queries sqlite_master, not
533   // sqlite_temp_master.
534
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);
541     } else {
542       RecordRecoveryEvent(RECOVERY_FAILED_META_NO_VERSION);
543     }
544     return false;
545   }
546
547   RecordRecoveryEvent(RECOVERY_SUCCESS_META_VERSION);
548   *version = recovery_version.ColumnInt(0);
549   return true;
550 }
551
552 namespace {
553
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.
559 //
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]);
566
567   sql::Statement s(db->GetUniqueStatement(
568       "SELECT DISTINCT sql FROM corrupt.sqlite_master "
569       "WHERE name<>'sqlite_sequence'"));
570   while (s.Step()) {
571     std::string sql = s.ColumnString(0);
572
573     // Skip statements that don't start with |prefix|.
574     if (sql.compare(0, prefix_len, prefix) != 0)
575       continue;
576
577     sql.insert(prefix_len, "main.");
578     if (!db->Execute(sql.c_str())) {
579       RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_SCHEMACREATE);
580       return false;
581     }
582   }
583   if (!s.Succeeded()) {
584     RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_SCHEMASELECT);
585     return false;
586   }
587   return true;
588 }
589
590 }  // namespace
591
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
594 // data.
595 //
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.
600 //
601 // static
602 std::unique_ptr<Recovery> Recovery::BeginRecoverDatabase(
603     Database* db,
604     const base::FilePath& db_path) {
605   std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path);
606   if (!recovery) {
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.
610     db->Poison();
611
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.
615     {
616       Database probe_db;
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);
621         return nullptr;
622       }
623     }
624
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);
630       return nullptr;
631     }
632
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.
636     {
637       Database probe_db;
638       if (!probe_db.Open(db_path)) {
639         RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_REOPEN);
640         return nullptr;
641       }
642       if (!probe_db.Execute("PRAGMA auto_vacuum")) {
643         RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NOTADB_QUERY);
644         return nullptr;
645       }
646     }
647
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);
651     return nullptr;
652   }
653
654 #if DCHECK_IS_ON()
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.
658   //
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.
664   if (db->is_open()) {
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";
668   }
669 #endif
670
671   // TODO(shess): vacuum.c turns off checks and foreign keys.
672
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?
676
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));
683     return nullptr;
684   }
685
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.
689   {
690     sql::Statement s(recovery->db()->GetUniqueStatement(
691         "SELECT name FROM sqlite_master WHERE sql LIKE 'CREATE TABLE %' "
692         "AND name!='sqlite_sequence'"));
693     while (s.Step()) {
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));
699         return nullptr;
700       }
701     }
702     if (!s.Succeeded()) {
703       RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_NAMESELECT);
704       Recovery::Rollback(std::move(recovery));
705       return nullptr;
706     }
707   }
708
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));
716       return nullptr;
717     }
718   }
719
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));
729     return nullptr;
730   }
731
732   RecordRecoveryEvent(RECOVERY_SUCCESS_AUTORECOVERDB);
733   return recovery;
734 }
735
736 void Recovery::RecoverDatabase(Database* db, const base::FilePath& db_path) {
737   std::unique_ptr<sql::Recovery> recovery = BeginRecoverDatabase(db, db_path);
738
739   // ignore_result() because BeginRecoverDatabase() and Recovered() already
740   // provide suitable histogram coverage.
741   if (recovery)
742     ignore_result(Recovery::Recovered(std::move(recovery)));
743 }
744
745 void Recovery::RecoverDatabaseWithMetaVersion(Database* db,
746                                               const base::FilePath& db_path) {
747   std::unique_ptr<sql::Recovery> recovery = BeginRecoverDatabase(db, db_path);
748   if (!recovery)
749     return;
750
751   int version = 0;
752   if (!recovery->SetupMeta() || !recovery->GetMetaVersionNumber(&version)) {
753     sql::Recovery::Unrecoverable(std::move(recovery));
754     RecordRecoveryEvent(RECOVERY_FAILED_AUTORECOVERDB_META_VERSION);
755     return;
756   }
757
758   // ignore_result() because BeginRecoverDatabase() and Recovered() already
759   // provide suitable histogram coverage.
760   ignore_result(Recovery::Recovered(std::move(recovery)));
761 }
762
763 // static
764 bool Recovery::ShouldRecover(int extended_error) {
765   // Trim extended error codes.
766   int error = extended_error & 0xFF;
767   switch (error) {
768     case SQLITE_NOTADB:
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.
775       return true;
776
777     case SQLITE_CORRUPT:
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.
785       return true;
786
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.
793
794     default:
795       return false;
796   }
797 }
798
799 // static
800 int Recovery::EnableRecoveryExtension(Database* db, InternalApiToken) {
801   return sql::recover::RegisterRecoverExtension(db->db(InternalApiToken()));
802 }
803
804 }  // namespace sql