[email protected] | 8d40941 | 2013-07-19 18:25:30 | [diff] [blame] | 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 | |
| 5 | #ifndef SQL_RECOVERY_H_ |
| 6 | #define SQL_RECOVERY_H_ |
| 7 | |
| 8 | #include "base/basictypes.h" |
| 9 | |
| 10 | #include "sql/connection.h" |
| 11 | |
| 12 | namespace base { |
| 13 | class FilePath; |
| 14 | } |
| 15 | |
| 16 | namespace 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 | |
| 40 | class SQL_EXPORT Recovery { |
| 41 | public: |
| 42 | ~Recovery(); |
| 43 | |
| 44 | // Begin the recovery process by opening a temporary database handle |
| 45 | // and attach the existing database to it at "corrupt". To prevent |
| 46 | // deadlock, all transactions on |connection| are rolled back. |
| 47 | // |
| 48 | // Returns NULL in case of failure, with no cleanup done on the |
| 49 | // original connection (except for breaking the transactions). The |
| 50 | // caller should Raze() or otherwise cleanup as appropriate. |
| 51 | // |
| 52 | // TODO(shess): Later versions of SQLite allow extracting the path |
| 53 | // from the connection. |
| 54 | // TODO(shess): Allow specifying the connection point? |
| 55 | static scoped_ptr<Recovery> Begin( |
| 56 | Connection* connection, |
| 57 | const base::FilePath& db_path) WARN_UNUSED_RESULT; |
| 58 | |
| 59 | // Mark recovery completed by replicating the recovery database over |
| 60 | // the original database, then closing the recovery database. The |
| 61 | // original database handle is poisoned, causing future calls |
| 62 | // against it to fail. |
| 63 | // |
| 64 | // If Recovered() is not called, the destructor will call |
| 65 | // Unrecoverable(). |
| 66 | // |
[email protected] | 74cdede | 2013-09-25 05:39:57 | [diff] [blame^] | 67 | // TODO(shess): At this time, this function can fail while leaving |
[email protected] | 8d40941 | 2013-07-19 18:25:30 | [diff] [blame] | 68 | // the original database intact. Figure out which failure cases |
| 69 | // should go to RazeAndClose() instead. |
| 70 | static bool Recovered(scoped_ptr<Recovery> r) WARN_UNUSED_RESULT; |
| 71 | |
| 72 | // Indicate that the database is unrecoverable. The original |
| 73 | // database is razed, and the handle poisoned. |
| 74 | static void Unrecoverable(scoped_ptr<Recovery> r); |
| 75 | |
[email protected] | 74cdede | 2013-09-25 05:39:57 | [diff] [blame^] | 76 | // When initially developing recovery code, sometimes the possible |
| 77 | // database states are not well-understood without further |
| 78 | // diagnostics. Abandon recovery but do not raze the original |
| 79 | // database. |
| 80 | // NOTE(shess): Only call this when adding recovery support. In the |
| 81 | // steady state, all databases should progress to recovered or razed. |
| 82 | static void Rollback(scoped_ptr<Recovery> r); |
| 83 | |
[email protected] | 8d40941 | 2013-07-19 18:25:30 | [diff] [blame] | 84 | // Handle to the temporary recovery database. |
| 85 | sql::Connection* db() { return &recover_db_; } |
| 86 | |
| 87 | private: |
| 88 | explicit Recovery(Connection* connection); |
| 89 | |
| 90 | // Setup the recovery database handle for Begin(). Returns false in |
| 91 | // case anything failed. |
| 92 | bool Init(const base::FilePath& db_path) WARN_UNUSED_RESULT; |
| 93 | |
| 94 | // Copy the recovered database over the original database. |
| 95 | bool Backup() WARN_UNUSED_RESULT; |
| 96 | |
| 97 | // Close the recovery database, and poison the original handle. |
| 98 | // |raze| controls whether the original database is razed or just |
| 99 | // poisoned. |
| 100 | enum Disposition { |
| 101 | RAZE_AND_POISON, |
| 102 | POISON, |
| 103 | }; |
| 104 | void Shutdown(Disposition raze); |
| 105 | |
| 106 | Connection* db_; // Original database connection. |
| 107 | Connection recover_db_; // Recovery connection. |
| 108 | |
| 109 | DISALLOW_COPY_AND_ASSIGN(Recovery); |
| 110 | }; |
| 111 | |
| 112 | } // namespace sql |
| 113 | |
| 114 | #endif // SQL_RECOVERY_H_ |