OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 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 #include "base/file_util.h" |
| 6 #include "base/files/scoped_temp_dir.h" |
| 7 #include "base/logging.h" |
| 8 #include "base/strings/stringprintf.h" |
| 9 #include "sql/connection.h" |
| 10 #include "sql/meta_table.h" |
| 11 #include "sql/recovery.h" |
| 12 #include "sql/statement.h" |
| 13 #include "sql/test/scoped_error_ignorer.h" |
| 14 #include "testing/gtest/include/gtest/gtest.h" |
| 15 #include "third_party/sqlite/sqlite3.h" |
| 16 |
| 17 namespace { |
| 18 |
| 19 // Helper to return the count of items in sqlite_master. Return -1 in |
| 20 // case of error. |
| 21 int SqliteMasterCount(sql::Connection* db) { |
| 22 const char* kMasterCount = "SELECT COUNT(*) FROM sqlite_master"; |
| 23 sql::Statement s(db->GetUniqueStatement(kMasterCount)); |
| 24 return s.Step() ? s.ColumnInt(0) : -1; |
| 25 } |
| 26 |
| 27 // Helper to execute a statement and return stringified results. |
| 28 std::string ExecuteWithResults(sql::Connection* db, |
| 29 const char* sql, |
| 30 const char* column_sep, |
| 31 const char* row_sep) { |
| 32 sql::Statement s(db->GetUniqueStatement(sql)); |
| 33 std::string ret; |
| 34 while (s.Step()) { |
| 35 for (int i = 0; i < s.ColumnCount(); ++i) { |
| 36 if (i > 0) |
| 37 ret += column_sep; |
| 38 ret += s.ColumnString(i); |
| 39 } |
| 40 ret += row_sep; |
| 41 } |
| 42 return ret; |
| 43 } |
| 44 |
| 45 void DumpMaster(sql::Connection* db, const char* name) { |
| 46 std::string kSql = base::StringPrintf( |
| 47 "SELECT type, name, tbl_name, sql FROM %s ORDER BY 1,2,3,4", |
| 48 name); |
| 49 std::string ret = ExecuteWithResults(db, kSql.c_str(), "|", "\n"); |
| 50 if (ret.empty()) { |
| 51 LOG(ERROR) << name << ": <empty>"; |
| 52 } else { |
| 53 LOG(ERROR) << name << ":\n" << ret; |
| 54 } |
| 55 } |
| 56 |
| 57 std::string GetSchema(sql::Connection* db) { |
| 58 const char kSql[] = |
| 59 "SELECT COALESCE(sql, name) FROM sqlite_master ORDER BY 1"; |
| 60 return ExecuteWithResults(db, kSql, "|", "\n"); |
| 61 } |
| 62 |
| 63 class SQLRecoverTest : public testing::Test { |
| 64 public: |
| 65 SQLRecoverTest() {} |
| 66 |
| 67 virtual void SetUp() { |
| 68 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| 69 ASSERT_TRUE(db_.Open(db_path())); |
| 70 } |
| 71 |
| 72 virtual void TearDown() { |
| 73 db_.Close(); |
| 74 } |
| 75 |
| 76 sql::Connection& db() { return db_; } |
| 77 |
| 78 base::FilePath db_path() { |
| 79 return temp_dir_.path().AppendASCII("SQLRecoverTest.db"); |
| 80 } |
| 81 |
| 82 bool Reopen() { |
| 83 db_.Close(); |
| 84 return db_.Open(db_path()); |
| 85 } |
| 86 |
| 87 private: |
| 88 base::ScopedTempDir temp_dir_; |
| 89 sql::Connection db_; |
| 90 }; |
| 91 |
| 92 TEST_F(SQLRecoverTest, RecoverBasic) { |
| 93 const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; |
| 94 const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')"; |
| 95 ASSERT_TRUE(db().Execute(kCreateSql)); |
| 96 ASSERT_TRUE(db().Execute(kInsertSql)); |
| 97 ASSERT_EQ(GetSchema(&db()), "CREATE TABLE x (t TEXT)\n"); |
| 98 |
| 99 // If the Recovery object goes out of scope without being marked |
| 100 // Recovered(), it clears the original database entirely. |
| 101 { |
| 102 sql::Recovery recovery(&db()); |
| 103 ASSERT_TRUE(recovery.Open(db_path())); |
| 104 } |
| 105 |
| 106 // If the Recovery object goes out of scope without being marked |
| 107 // Recovered(), it clears the original database entirely. |
| 108 ASSERT_TRUE(Reopen()); |
| 109 ASSERT_EQ(0, SqliteMasterCount(&db())); |
| 110 |
| 111 ASSERT_TRUE(db().Execute(kCreateSql)); |
| 112 ASSERT_TRUE(db().Execute(kInsertSql)); |
| 113 ASSERT_EQ(GetSchema(&db()), "CREATE TABLE x (t TEXT)\n"); |
| 114 |
| 115 // Unrecoverable() clears the original database entirely and poisons |
| 116 // the connection. |
| 117 { |
| 118 sql::Recovery recovery(&db()); |
| 119 ASSERT_TRUE(recovery.Open(db_path())); |
| 120 recovery.Unrecoverable(); |
| 121 |
| 122 // TODO(shess): Test that calls to recover.db() start failing. |
| 123 } |
| 124 |
| 125 // Unrecoverable() clears the original database entirely. |
| 126 ASSERT_TRUE(Reopen()); |
| 127 ASSERT_EQ(0, SqliteMasterCount(&db())); |
| 128 |
| 129 ASSERT_TRUE(db().Execute(kCreateSql)); |
| 130 ASSERT_TRUE(db().Execute(kInsertSql)); |
| 131 ASSERT_EQ(GetSchema(&db()), "CREATE TABLE x (t TEXT)\n"); |
| 132 |
| 133 // Successfully recover the database by copying from the existing |
| 134 // database. |
| 135 { |
| 136 sql::Recovery recovery(&db()); |
| 137 ASSERT_TRUE(recovery.Open(db_path())); |
| 138 |
| 139 // Create the new version of the table. |
| 140 ASSERT_TRUE(recovery.db()->Execute(kCreateSql)); |
| 141 |
| 142 const char kRecoverSql[] = "INSERT INTO x SELECT t FROM corrupt.x"; |
| 143 ASSERT_TRUE(recovery.db()->Execute(kRecoverSql)); |
| 144 |
| 145 // Successfully recovered. |
| 146 ASSERT_TRUE(recovery.Recovered()); |
| 147 } |
| 148 |
| 149 // Since the database was not corrupt, the entire schema and all |
| 150 // data should be recovered. |
| 151 ASSERT_TRUE(Reopen()); |
| 152 ASSERT_EQ(GetSchema(&db()), "CREATE TABLE x (t TEXT)\n"); |
| 153 |
| 154 const char* kXSql = "SELECT * FROM x ORDER BY 1"; |
| 155 ASSERT_EQ(ExecuteWithResults(&db(), kXSql, "|", "\n"), |
| 156 "This is a test\n"); |
| 157 } |
| 158 |
| 159 } // namespace |
OLD | NEW |