| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "sql/recovery.h" | 5 #include "sql/recovery.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 #include <string> | 8 #include <string> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| (...skipping 154 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 165 } | 165 } |
| 166 EXPECT_FALSE(db().is_open()); | 166 EXPECT_FALSE(db().is_open()); |
| 167 ASSERT_TRUE(Reopen()); | 167 ASSERT_TRUE(Reopen()); |
| 168 EXPECT_TRUE(db().is_open()); | 168 EXPECT_TRUE(db().is_open()); |
| 169 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); | 169 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 170 | 170 |
| 171 ASSERT_EQ("This is a test", | 171 ASSERT_EQ("This is a test", |
| 172 ExecuteWithResults(&db(), kXSql, "|", "\n")); | 172 ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 173 } | 173 } |
| 174 | 174 |
| 175 // The recovery virtual table is only supported for Chromium's SQLite. | |
| 176 #if !defined(USE_SYSTEM_SQLITE) | |
| 177 | |
| 178 // Test operation of the virtual table used by sql::Recovery. | 175 // Test operation of the virtual table used by sql::Recovery. |
| 179 TEST_F(SQLRecoveryTest, VirtualTable) { | 176 TEST_F(SQLRecoveryTest, VirtualTable) { |
| 180 const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; | 177 const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; |
| 181 ASSERT_TRUE(db().Execute(kCreateSql)); | 178 ASSERT_TRUE(db().Execute(kCreateSql)); |
| 182 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')")); | 179 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')")); |
| 183 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test')")); | 180 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test')")); |
| 184 | 181 |
| 185 // Successfully recover the database. | 182 // Successfully recover the database. |
| 186 { | 183 { |
| 187 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 184 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 209 // Since the database was not corrupt, the entire schema and all | 206 // Since the database was not corrupt, the entire schema and all |
| 210 // data should be recovered. | 207 // data should be recovered. |
| 211 ASSERT_TRUE(Reopen()); | 208 ASSERT_TRUE(Reopen()); |
| 212 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); | 209 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 213 | 210 |
| 214 const char* kXSql = "SELECT * FROM x ORDER BY 1"; | 211 const char* kXSql = "SELECT * FROM x ORDER BY 1"; |
| 215 ASSERT_EQ("That was a test\nThis is a test", | 212 ASSERT_EQ("That was a test\nThis is a test", |
| 216 ExecuteWithResults(&db(), kXSql, "|", "\n")); | 213 ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 217 } | 214 } |
| 218 | 215 |
| 216 // Virtual table works with page sizes other than the default, with |
| 217 // sql::Recovery maintaining page size. |
| 218 TEST_F(SQLRecoveryTest, VirtualTablePageSize) { |
| 219 const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; |
| 220 ASSERT_TRUE(db().Execute(kCreateSql)); |
| 221 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')")); |
| 222 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test')")); |
| 223 |
| 224 // The default page size is the greater of SQLITE_DEFAULT_PAGE_SIZE and |
| 225 // xSectorSize() returned from the vfs. |
| 226 db().set_page_size(1024); |
| 227 ASSERT_TRUE(Reopen()); |
| 228 ASSERT_TRUE(db().Execute("VACUUM")); |
| 229 |
| 230 // Reset the page size. |
| 231 ASSERT_EQ("1024", ExecuteWithResults(&db(), "PRAGMA page_size", "|", "\n")); |
| 232 db().set_page_size(4096); |
| 233 ASSERT_TRUE(Reopen()); |
| 234 ASSERT_TRUE(db().Execute("VACUUM")); |
| 235 ASSERT_EQ("4096", ExecuteWithResults(&db(), "PRAGMA page_size", "|", "\n")); |
| 236 |
| 237 // Successfully recover the database. |
| 238 { |
| 239 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 240 |
| 241 // Tables to recover original DB, now at [corrupt]. |
| 242 const char kRecoveryCreateSql[] = |
| 243 "CREATE VIRTUAL TABLE temp.recover_x using recover(" |
| 244 " corrupt.x," |
| 245 " t TEXT STRICT" |
| 246 ")"; |
| 247 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql)); |
| 248 |
| 249 // Re-create the original schema. |
| 250 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 251 |
| 252 // Copy the data from the recovery tables to the new database. |
| 253 const char kRecoveryCopySql[] = |
| 254 "INSERT INTO x SELECT t FROM recover_x"; |
| 255 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql)); |
| 256 |
| 257 // Successfully recovered. |
| 258 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 259 } |
| 260 |
| 261 // Since the database was not corrupt, the entire schema and all |
| 262 // data should be recovered. |
| 263 ASSERT_TRUE(Reopen()); |
| 264 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 265 ASSERT_EQ("4096", ExecuteWithResults(&db(), "PRAGMA page_size", "|", "\n")); |
| 266 |
| 267 const char* kXSql = "SELECT * FROM x ORDER BY 1"; |
| 268 ASSERT_EQ("That was a test\nThis is a test", |
| 269 ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 270 } |
| 271 |
| 219 void RecoveryCallback(sql::Connection* db, const base::FilePath& db_path, | 272 void RecoveryCallback(sql::Connection* db, const base::FilePath& db_path, |
| 220 const char* create_table, const char* create_index, | 273 const char* create_table, const char* create_index, |
| 221 int* record_error, int error, sql::Statement* stmt) { | 274 int* record_error, int error, sql::Statement* stmt) { |
| 222 *record_error = error; | 275 *record_error = error; |
| 223 | 276 |
| 224 // Clear the error callback to prevent reentrancy. | 277 // Clear the error callback to prevent reentrancy. |
| 225 db->reset_error_callback(); | 278 db->reset_error_callback(); |
| 226 | 279 |
| 227 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); | 280 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); |
| 228 ASSERT_TRUE(recovery.get()); | 281 ASSERT_TRUE(recovery.get()); |
| (...skipping 478 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 707 ASSERT_TRUE(Reopen()); | 760 ASSERT_TRUE(Reopen()); |
| 708 ASSERT_EQ(expected_schema, GetSchema(&db())); | 761 ASSERT_EQ(expected_schema, GetSchema(&db())); |
| 709 ASSERT_EQ(expected_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); | 762 ASSERT_EQ(expected_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 710 } | 763 } |
| 711 | 764 |
| 712 // Recover a golden file where an interior page has been manually modified so | 765 // Recover a golden file where an interior page has been manually modified so |
| 713 // that the number of cells is greater than will fit on a single page. This | 766 // that the number of cells is greater than will fit on a single page. This |
| 714 // case happened in <http://crbug.com/387868>. | 767 // case happened in <http://crbug.com/387868>. |
| 715 TEST_F(SQLRecoveryTest, Bug387868) { | 768 TEST_F(SQLRecoveryTest, Bug387868) { |
| 716 base::FilePath golden_path; | 769 base::FilePath golden_path; |
| 717 ASSERT_TRUE(PathService::Get(sql::test::DIR_TEST_DATA, &golden_path)); | 770 ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &golden_path)); |
| 771 golden_path = golden_path.AppendASCII("sql"); |
| 772 golden_path = golden_path.AppendASCII("test"); |
| 773 golden_path = golden_path.AppendASCII("data"); |
| 718 golden_path = golden_path.AppendASCII("recovery_387868"); | 774 golden_path = golden_path.AppendASCII("recovery_387868"); |
| 719 db().Close(); | 775 db().Close(); |
| 720 ASSERT_TRUE(base::CopyFile(golden_path, db_path())); | 776 ASSERT_TRUE(base::CopyFile(golden_path, db_path())); |
| 721 ASSERT_TRUE(Reopen()); | 777 ASSERT_TRUE(Reopen()); |
| 722 | 778 |
| 723 { | 779 { |
| 724 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 780 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 725 ASSERT_TRUE(recovery.get()); | 781 ASSERT_TRUE(recovery.get()); |
| 726 | 782 |
| 727 // Create the new version of the table. | 783 // Create the new version of the table. |
| 728 const char kCreateSql[] = | 784 const char kCreateSql[] = |
| 729 "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)"; | 785 "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)"; |
| 730 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 786 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 731 | 787 |
| 732 size_t rows = 0; | 788 size_t rows = 0; |
| 733 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); | 789 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); |
| 734 EXPECT_EQ(43u, rows); | 790 EXPECT_EQ(43u, rows); |
| 735 | 791 |
| 736 // Successfully recovered. | 792 // Successfully recovered. |
| 737 EXPECT_TRUE(sql::Recovery::Recovered(std::move(recovery))); | 793 EXPECT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 738 } | 794 } |
| 739 } | 795 } |
| 740 #endif // !defined(USE_SYSTEM_SQLITE) | |
| 741 | 796 |
| 742 // Memory-mapped I/O interacts poorly with I/O errors. Make sure the recovery | 797 // Memory-mapped I/O interacts poorly with I/O errors. Make sure the recovery |
| 743 // database doesn't accidentally enable it. | 798 // database doesn't accidentally enable it. |
| 744 TEST_F(SQLRecoveryTest, NoMmap) { | 799 TEST_F(SQLRecoveryTest, NoMmap) { |
| 745 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 800 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 746 ASSERT_TRUE(recovery.get()); | 801 ASSERT_TRUE(recovery.get()); |
| 747 | 802 |
| 748 // In the current implementation, the PRAGMA successfully runs with no result | 803 // In the current implementation, the PRAGMA successfully runs with no result |
| 749 // rows. Running with a single result of |0| is also acceptable. | 804 // rows. Running with a single result of |0| is also acceptable. |
| 750 sql::Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size")); | 805 sql::Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size")); |
| 751 EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0)); | 806 EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0)); |
| 752 } | 807 } |
| 753 | 808 |
| 754 } // namespace | 809 } // namespace |
| OLD | NEW |