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