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