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