blob: c03ebb2b391b3a920ea576d5636e2a7e6fba94e4 [file] [log] [blame]
[email protected]8d409412013-07-19 18:25:301// 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#ifndef SQL_RECOVERY_H_
6#define SQL_RECOVERY_H_
7
avi0b519202015-12-21 07:25:198#include <stddef.h>
9
tfarina720d4f32015-05-11 22:31:2610#include "base/macros.h"
[email protected]8d409412013-07-19 18:25:3011#include "sql/connection.h"
12
13namespace base {
14class FilePath;
15}
16
17namespace sql {
18
19// Recovery module for sql/. The basic idea is to create a fresh
20// database and populate it with the recovered contents of the
21// original database. If recovery is successful, the recovered
22// database is backed up over the original database. If recovery is
23// not successful, the original database is razed. In either case,
24// the original handle is poisoned so that operations on the stack do
25// not accidentally disrupt the restored data.
26//
27// {
28// scoped_ptr<sql::Recovery> r =
29// sql::Recovery::Begin(orig_db, orig_db_path);
30// if (r) {
[email protected]ae4f1622013-12-08 06:49:1231// // Create the schema to recover to. On failure, clear the
32// // database.
33// if (!r.db()->Execute(kCreateSchemaSql)) {
34// sql::Recovery::Unrecoverable(r.Pass());
35// return;
[email protected]8d409412013-07-19 18:25:3036// }
[email protected]ae4f1622013-12-08 06:49:1237//
38// // Recover data in "mytable".
39// size_t rows_recovered = 0;
40// if (!r.AutoRecoverTable("mytable", 0, &rows_recovered)) {
41// sql::Recovery::Unrecoverable(r.Pass());
42// return;
43// }
44//
45// // Manually cleanup additional constraints.
46// if (!r.db()->Execute(kCleanupSql)) {
47// sql::Recovery::Unrecoverable(r.Pass());
48// return;
49// }
50//
51// // Commit the recovered data to the original database file.
52// sql::Recovery::Recovered(r.Pass());
[email protected]8d409412013-07-19 18:25:3053// }
54// }
55//
56// If Recovered() is not called, then RazeAndClose() is called on
57// orig_db.
58
59class SQL_EXPORT Recovery {
60 public:
61 ~Recovery();
62
[email protected]df5d95c42013-10-07 22:24:2263 // This module is intended to be used in concert with a virtual
64 // table module (see third_party/sqlite/src/src/recover.c). If the
65 // build defines USE_SYSTEM_SQLITE, this module will not be present.
66 // TODO(shess): I am still debating how to handle this - perhaps it
67 // will just imply Unrecoverable(). This is exposed to allow tests
68 // to adapt to the cases, please do not rely on it in production
69 // code.
70 static bool FullRecoverySupported();
71
[email protected]8d409412013-07-19 18:25:3072 // Begin the recovery process by opening a temporary database handle
73 // and attach the existing database to it at "corrupt". To prevent
74 // deadlock, all transactions on |connection| are rolled back.
75 //
76 // Returns NULL in case of failure, with no cleanup done on the
77 // original connection (except for breaking the transactions). The
78 // caller should Raze() or otherwise cleanup as appropriate.
79 //
80 // TODO(shess): Later versions of SQLite allow extracting the path
81 // from the connection.
82 // TODO(shess): Allow specifying the connection point?
83 static scoped_ptr<Recovery> Begin(
84 Connection* connection,
85 const base::FilePath& db_path) WARN_UNUSED_RESULT;
86
87 // Mark recovery completed by replicating the recovery database over
88 // the original database, then closing the recovery database. The
89 // original database handle is poisoned, causing future calls
90 // against it to fail.
91 //
92 // If Recovered() is not called, the destructor will call
93 // Unrecoverable().
94 //
[email protected]74cdede2013-09-25 05:39:5795 // TODO(shess): At this time, this function can fail while leaving
[email protected]8d409412013-07-19 18:25:3096 // the original database intact. Figure out which failure cases
97 // should go to RazeAndClose() instead.
98 static bool Recovered(scoped_ptr<Recovery> r) WARN_UNUSED_RESULT;
99
100 // Indicate that the database is unrecoverable. The original
101 // database is razed, and the handle poisoned.
102 static void Unrecoverable(scoped_ptr<Recovery> r);
103
[email protected]74cdede2013-09-25 05:39:57104 // When initially developing recovery code, sometimes the possible
105 // database states are not well-understood without further
106 // diagnostics. Abandon recovery but do not raze the original
107 // database.
108 // NOTE(shess): Only call this when adding recovery support. In the
109 // steady state, all databases should progress to recovered or razed.
110 static void Rollback(scoped_ptr<Recovery> r);
111
[email protected]8d409412013-07-19 18:25:30112 // Handle to the temporary recovery database.
113 sql::Connection* db() { return &recover_db_; }
114
[email protected]a8848a72013-11-18 04:18:47115 // Attempt to recover the named table from the corrupt database into
116 // the recovery database using a temporary recover virtual table.
117 // The virtual table schema is derived from the named table's schema
118 // in database [main]. Data is copied using INSERT OR REPLACE, so
119 // duplicates overwrite each other.
120 //
121 // |extend_columns| allows recovering tables which have excess
122 // columns relative to the target schema. The recover virtual table
123 // treats more data than specified as a sign of corruption.
124 //
125 // Returns true if all operations succeeded, with the number of rows
126 // recovered in |*rows_recovered|.
127 //
128 // NOTE(shess): Due to a flaw in the recovery virtual table, at this
129 // time this code injects the DEFAULT value of the target table in
130 // locations where the recovery table returns NULL. This is not
131 // entirely correct, because it happens both when there is a short
132 // row (correct) but also where there is an actual NULL value
133 // (incorrect).
134 //
135 // TODO(shess): Flag for INSERT OR REPLACE vs IGNORE.
136 // TODO(shess): Handle extended table names.
137 bool AutoRecoverTable(const char* table_name,
138 size_t extend_columns,
139 size_t* rows_recovered);
140
141 // Setup a recover virtual table at temp.recover_meta, reading from
142 // corrupt.meta. Returns true if created.
143 // TODO(shess): Perhaps integrate into Begin().
144 // TODO(shess): Add helpers to fetch additional items from the meta
145 // table as needed.
146 bool SetupMeta();
147
148 // Fetch the version number from temp.recover_meta. Returns false
149 // if the query fails, or if there is no version row. Otherwise
150 // returns true, with the version in |*version_number|.
151 //
152 // Only valid to call after successful SetupMeta().
153 bool GetMetaVersionNumber(int* version_number);
154
[email protected]8d409412013-07-19 18:25:30155 private:
156 explicit Recovery(Connection* connection);
157
158 // Setup the recovery database handle for Begin(). Returns false in
159 // case anything failed.
160 bool Init(const base::FilePath& db_path) WARN_UNUSED_RESULT;
161
162 // Copy the recovered database over the original database.
163 bool Backup() WARN_UNUSED_RESULT;
164
165 // Close the recovery database, and poison the original handle.
166 // |raze| controls whether the original database is razed or just
167 // poisoned.
168 enum Disposition {
169 RAZE_AND_POISON,
170 POISON,
171 };
172 void Shutdown(Disposition raze);
173
174 Connection* db_; // Original database connection.
175 Connection recover_db_; // Recovery connection.
176
177 DISALLOW_COPY_AND_ASSIGN(Recovery);
178};
179
180} // namespace sql
181
182#endif // SQL_RECOVERY_H_