recovery.h 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  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. #ifndef SQL_RECOVERY_H_
  5. #define SQL_RECOVERY_H_
  6. #include <stddef.h>
  7. #include <memory>
  8. #include "base/component_export.h"
  9. #include "base/memory/raw_ptr.h"
  10. #include "sql/database.h"
  11. #include "sql/internal_api_token.h"
  12. namespace base {
  13. class FilePath;
  14. }
  15. namespace sql {
  16. // Recovery module for sql/. The basic idea is to create a fresh database and
  17. // populate it with the recovered contents of the original database. If
  18. // recovery is successful, the recovered database is backed up over the original
  19. // database. If recovery is not successful, the original database is razed. In
  20. // either case, the original handle is poisoned so that operations on the stack
  21. // do not accidentally disrupt the restored data.
  22. //
  23. // RecoverDatabase() automates this, including recoverying the schema of from
  24. // the suspect database. If a database requires special handling, such as
  25. // recovering between different schema, or tables requiring post-processing,
  26. // then the module can be used manually like:
  27. //
  28. // {
  29. // std::unique_ptr<sql::Recovery> r =
  30. // sql::Recovery::Begin(orig_db, orig_db_path);
  31. // if (r) {
  32. // // Create the schema to recover to. On failure, clear the
  33. // // database.
  34. // if (!r.db()->Execute(kCreateSchemaSql)) {
  35. // sql::Recovery::Unrecoverable(std::move(r));
  36. // return;
  37. // }
  38. //
  39. // // Recover data in "mytable".
  40. // size_t rows_recovered = 0;
  41. // if (!r.AutoRecoverTable("mytable", 0, &rows_recovered)) {
  42. // sql::Recovery::Unrecoverable(std::move(r));
  43. // return;
  44. // }
  45. //
  46. // // Manually cleanup additional constraints.
  47. // if (!r.db()->Execute(kCleanupSql)) {
  48. // sql::Recovery::Unrecoverable(std::move(r));
  49. // return;
  50. // }
  51. //
  52. // // Commit the recovered data to the original database file.
  53. // sql::Recovery::Recovered(std::move(r));
  54. // }
  55. // }
  56. //
  57. // If Recovered() is not called, then RazeAndClose() is called on
  58. // orig_db.
  59. class COMPONENT_EXPORT(SQL) Recovery {
  60. public:
  61. Recovery(const Recovery&) = delete;
  62. Recovery& operator=(const Recovery&) = delete;
  63. ~Recovery();
  64. // Begin the recovery process by opening a temporary database handle
  65. // and attach the existing database to it at "corrupt". To prevent
  66. // deadlock, all transactions on |database| are rolled back.
  67. //
  68. // Returns nullptr in case of failure, with no cleanup done on the
  69. // original database (except for breaking the transactions). The
  70. // caller should Raze() or otherwise cleanup as appropriate.
  71. //
  72. // TODO(shess): Later versions of SQLite allow extracting the path
  73. // from the database.
  74. // TODO(shess): Allow specifying the connection point?
  75. [[nodiscard]] static std::unique_ptr<Recovery> Begin(
  76. Database* database,
  77. const base::FilePath& db_path);
  78. // Mark recovery completed by replicating the recovery database over
  79. // the original database, then closing the recovery database. The
  80. // original database handle is poisoned, causing future calls
  81. // against it to fail.
  82. //
  83. // If Recovered() is not called, the destructor will call
  84. // Unrecoverable().
  85. //
  86. // TODO(shess): At this time, this function can fail while leaving
  87. // the original database intact. Figure out which failure cases
  88. // should go to RazeAndClose() instead.
  89. [[nodiscard]] static bool Recovered(std::unique_ptr<Recovery> r);
  90. // Indicate that the database is unrecoverable. The original
  91. // database is razed, and the handle poisoned.
  92. static void Unrecoverable(std::unique_ptr<Recovery> r);
  93. // When initially developing recovery code, sometimes the possible
  94. // database states are not well-understood without further
  95. // diagnostics. Abandon recovery but do not raze the original
  96. // database.
  97. // NOTE(shess): Only call this when adding recovery support. In the
  98. // steady state, all databases should progress to recovered or razed.
  99. static void Rollback(std::unique_ptr<Recovery> r);
  100. // Handle to the temporary recovery database.
  101. sql::Database* db() { return &recover_db_; }
  102. // Attempt to recover the named table from the corrupt database into
  103. // the recovery database using a temporary recover virtual table.
  104. // The virtual table schema is derived from the named table's schema
  105. // in database [main]. Data is copied using INSERT OR IGNORE, so
  106. // duplicates are dropped.
  107. //
  108. // If the source table has fewer columns than the target, the target
  109. // DEFAULT value will be used for those columns.
  110. //
  111. // Returns true if all operations succeeded, with the number of rows
  112. // recovered in |*rows_recovered|.
  113. //
  114. // NOTE(shess): Due to a flaw in the recovery virtual table, at this
  115. // time this code injects the DEFAULT value of the target table in
  116. // locations where the recovery table returns nullptr. This is not
  117. // entirely correct, because it happens both when there is a short
  118. // row (correct) but also where there is an actual NULL value
  119. // (incorrect).
  120. //
  121. // TODO(shess): Flag for INSERT OR REPLACE vs IGNORE.
  122. // TODO(shess): Handle extended table names.
  123. bool AutoRecoverTable(const char* table_name, size_t* rows_recovered);
  124. // Setup a recover virtual table at temp.recover_meta, reading from
  125. // corrupt.meta. Returns true if created.
  126. // TODO(shess): Perhaps integrate into Begin().
  127. // TODO(shess): Add helpers to fetch additional items from the meta
  128. // table as needed.
  129. bool SetupMeta();
  130. // Fetch the version number from temp.recover_meta. Returns false
  131. // if the query fails, or if there is no version row. Otherwise
  132. // returns true, with the version in |*version_number|.
  133. //
  134. // Only valid to call after successful SetupMeta().
  135. bool GetMetaVersionNumber(int* version_number);
  136. // Attempt to recover the database by creating a new database with schema from
  137. // |db|, then copying over as much data as possible. If successful, the
  138. // recovery handle is returned to allow the caller to make additional changes,
  139. // such as validating constraints not expressed in the schema.
  140. //
  141. // In case of SQLITE_NOTADB, the database is deemed unrecoverable and deleted.
  142. [[nodiscard]] static std::unique_ptr<Recovery> BeginRecoverDatabase(
  143. Database* db,
  144. const base::FilePath& db_path);
  145. // Call BeginRecoverDatabase() to recover the database, then commit the
  146. // changes using Recovered(). After this call, the |db| handle will be
  147. // poisoned (though technically remaining open) so that future calls will
  148. // return errors until the handle is re-opened.
  149. static void RecoverDatabase(Database* db, const base::FilePath& db_path);
  150. // Variant on RecoverDatabase() which requires that the database have a valid
  151. // meta table with a version value. The meta version value is used by some
  152. // clients to make assertions about the database schema. If this information
  153. // cannot be determined, the database is considered unrecoverable.
  154. static void RecoverDatabaseWithMetaVersion(Database* db,
  155. const base::FilePath& db_path);
  156. // Returns true for SQLite errors which RecoverDatabase() can plausibly fix.
  157. // This does not guarantee that RecoverDatabase() will successfully recover
  158. // the database.
  159. static bool ShouldRecover(int extended_error);
  160. // Enables the "recover" SQLite extension for a database connection.
  161. //
  162. // Returns a SQLite error code.
  163. static int EnableRecoveryExtension(Database* db, InternalApiToken);
  164. private:
  165. explicit Recovery(Database* database);
  166. // Setup the recovery database handle for Begin(). Returns false in
  167. // case anything failed.
  168. [[nodiscard]] bool Init(const base::FilePath& db_path);
  169. // Copy the recovered database over the original database.
  170. [[nodiscard]] bool Backup();
  171. // Close the recovery database, and poison the original handle.
  172. // |raze| controls whether the original database is razed or just
  173. // poisoned.
  174. enum Disposition {
  175. RAZE_AND_POISON,
  176. POISON,
  177. };
  178. void Shutdown(Disposition raze);
  179. raw_ptr<Database> db_; // Original Database connection.
  180. Database recover_db_; // Recovery Database connection.
  181. };
  182. } // namespace sql
  183. #endif // SQL_RECOVERY_H_