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