| 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 |
| 9 #include <memory> |
| 8 #include <string> | 10 #include <string> |
| 9 #include <utility> | 11 #include <utility> |
| 10 | 12 |
| 11 #include "base/bind.h" | 13 #include "base/bind.h" |
| 12 #include "base/files/file_path.h" | 14 #include "base/files/file_path.h" |
| 13 #include "base/files/file_util.h" | 15 #include "base/files/file_util.h" |
| 14 #include "base/files/scoped_temp_dir.h" | 16 #include "base/files/scoped_temp_dir.h" |
| 15 #include "base/path_service.h" | 17 #include "base/path_service.h" |
| 16 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
| 17 #include "sql/connection.h" | 19 #include "sql/connection.h" |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 73 const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; | 75 const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; |
| 74 const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')"; | 76 const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')"; |
| 75 const char kAltInsertSql[] = "INSERT INTO x VALUES ('That was a test')"; | 77 const char kAltInsertSql[] = "INSERT INTO x VALUES ('That was a test')"; |
| 76 ASSERT_TRUE(db().Execute(kCreateSql)); | 78 ASSERT_TRUE(db().Execute(kCreateSql)); |
| 77 ASSERT_TRUE(db().Execute(kInsertSql)); | 79 ASSERT_TRUE(db().Execute(kInsertSql)); |
| 78 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); | 80 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 79 | 81 |
| 80 // If the Recovery handle goes out of scope without being | 82 // If the Recovery handle goes out of scope without being |
| 81 // Recovered(), the database is razed. | 83 // Recovered(), the database is razed. |
| 82 { | 84 { |
| 83 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 85 std::unique_ptr<sql::Recovery> recovery = |
| 86 sql::Recovery::Begin(&db(), db_path()); |
| 84 ASSERT_TRUE(recovery.get()); | 87 ASSERT_TRUE(recovery.get()); |
| 85 } | 88 } |
| 86 EXPECT_FALSE(db().is_open()); | 89 EXPECT_FALSE(db().is_open()); |
| 87 ASSERT_TRUE(Reopen()); | 90 ASSERT_TRUE(Reopen()); |
| 88 EXPECT_TRUE(db().is_open()); | 91 EXPECT_TRUE(db().is_open()); |
| 89 ASSERT_EQ("", GetSchema(&db())); | 92 ASSERT_EQ("", GetSchema(&db())); |
| 90 | 93 |
| 91 // Recreate the database. | 94 // Recreate the database. |
| 92 ASSERT_TRUE(db().Execute(kCreateSql)); | 95 ASSERT_TRUE(db().Execute(kCreateSql)); |
| 93 ASSERT_TRUE(db().Execute(kInsertSql)); | 96 ASSERT_TRUE(db().Execute(kInsertSql)); |
| 94 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); | 97 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 95 | 98 |
| 96 // Unrecoverable() also razes. | 99 // Unrecoverable() also razes. |
| 97 { | 100 { |
| 98 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 101 std::unique_ptr<sql::Recovery> recovery = |
| 102 sql::Recovery::Begin(&db(), db_path()); |
| 99 ASSERT_TRUE(recovery.get()); | 103 ASSERT_TRUE(recovery.get()); |
| 100 sql::Recovery::Unrecoverable(std::move(recovery)); | 104 sql::Recovery::Unrecoverable(std::move(recovery)); |
| 101 | 105 |
| 102 // TODO(shess): Test that calls to recover.db() start failing. | 106 // TODO(shess): Test that calls to recover.db() start failing. |
| 103 } | 107 } |
| 104 EXPECT_FALSE(db().is_open()); | 108 EXPECT_FALSE(db().is_open()); |
| 105 ASSERT_TRUE(Reopen()); | 109 ASSERT_TRUE(Reopen()); |
| 106 EXPECT_TRUE(db().is_open()); | 110 EXPECT_TRUE(db().is_open()); |
| 107 ASSERT_EQ("", GetSchema(&db())); | 111 ASSERT_EQ("", GetSchema(&db())); |
| 108 | 112 |
| 109 // Attempting to recover a previously-recovered handle fails early. | 113 // Attempting to recover a previously-recovered handle fails early. |
| 110 { | 114 { |
| 111 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 115 std::unique_ptr<sql::Recovery> recovery = |
| 116 sql::Recovery::Begin(&db(), db_path()); |
| 112 ASSERT_TRUE(recovery.get()); | 117 ASSERT_TRUE(recovery.get()); |
| 113 recovery.reset(); | 118 recovery.reset(); |
| 114 | 119 |
| 115 recovery = sql::Recovery::Begin(&db(), db_path()); | 120 recovery = sql::Recovery::Begin(&db(), db_path()); |
| 116 ASSERT_FALSE(recovery.get()); | 121 ASSERT_FALSE(recovery.get()); |
| 117 } | 122 } |
| 118 ASSERT_TRUE(Reopen()); | 123 ASSERT_TRUE(Reopen()); |
| 119 | 124 |
| 120 // Recreate the database. | 125 // Recreate the database. |
| 121 ASSERT_TRUE(db().Execute(kCreateSql)); | 126 ASSERT_TRUE(db().Execute(kCreateSql)); |
| 122 ASSERT_TRUE(db().Execute(kInsertSql)); | 127 ASSERT_TRUE(db().Execute(kInsertSql)); |
| 123 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); | 128 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 124 | 129 |
| 125 // Unrecovered table to distinguish from recovered database. | 130 // Unrecovered table to distinguish from recovered database. |
| 126 ASSERT_TRUE(db().Execute("CREATE TABLE y (c INTEGER)")); | 131 ASSERT_TRUE(db().Execute("CREATE TABLE y (c INTEGER)")); |
| 127 ASSERT_NE("CREATE TABLE x (t TEXT)", GetSchema(&db())); | 132 ASSERT_NE("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 128 | 133 |
| 129 // Recovered() replaces the original with the "recovered" version. | 134 // Recovered() replaces the original with the "recovered" version. |
| 130 { | 135 { |
| 131 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 136 std::unique_ptr<sql::Recovery> recovery = |
| 137 sql::Recovery::Begin(&db(), db_path()); |
| 132 ASSERT_TRUE(recovery.get()); | 138 ASSERT_TRUE(recovery.get()); |
| 133 | 139 |
| 134 // Create the new version of the table. | 140 // Create the new version of the table. |
| 135 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 141 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 136 | 142 |
| 137 // Insert different data to distinguish from original database. | 143 // Insert different data to distinguish from original database. |
| 138 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); | 144 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); |
| 139 | 145 |
| 140 // Successfully recovered. | 146 // Successfully recovered. |
| 141 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); | 147 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 142 } | 148 } |
| 143 EXPECT_FALSE(db().is_open()); | 149 EXPECT_FALSE(db().is_open()); |
| 144 ASSERT_TRUE(Reopen()); | 150 ASSERT_TRUE(Reopen()); |
| 145 EXPECT_TRUE(db().is_open()); | 151 EXPECT_TRUE(db().is_open()); |
| 146 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); | 152 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 147 | 153 |
| 148 const char* kXSql = "SELECT * FROM x ORDER BY 1"; | 154 const char* kXSql = "SELECT * FROM x ORDER BY 1"; |
| 149 ASSERT_EQ("That was a test", | 155 ASSERT_EQ("That was a test", |
| 150 ExecuteWithResults(&db(), kXSql, "|", "\n")); | 156 ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 151 | 157 |
| 152 // Reset the database contents. | 158 // Reset the database contents. |
| 153 ASSERT_TRUE(db().Execute("DELETE FROM x")); | 159 ASSERT_TRUE(db().Execute("DELETE FROM x")); |
| 154 ASSERT_TRUE(db().Execute(kInsertSql)); | 160 ASSERT_TRUE(db().Execute(kInsertSql)); |
| 155 | 161 |
| 156 // Rollback() discards recovery progress and leaves the database as it was. | 162 // Rollback() discards recovery progress and leaves the database as it was. |
| 157 { | 163 { |
| 158 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 164 std::unique_ptr<sql::Recovery> recovery = |
| 165 sql::Recovery::Begin(&db(), db_path()); |
| 159 ASSERT_TRUE(recovery.get()); | 166 ASSERT_TRUE(recovery.get()); |
| 160 | 167 |
| 161 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 168 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 162 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); | 169 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); |
| 163 | 170 |
| 164 sql::Recovery::Rollback(std::move(recovery)); | 171 sql::Recovery::Rollback(std::move(recovery)); |
| 165 } | 172 } |
| 166 EXPECT_FALSE(db().is_open()); | 173 EXPECT_FALSE(db().is_open()); |
| 167 ASSERT_TRUE(Reopen()); | 174 ASSERT_TRUE(Reopen()); |
| 168 EXPECT_TRUE(db().is_open()); | 175 EXPECT_TRUE(db().is_open()); |
| 169 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); | 176 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 170 | 177 |
| 171 ASSERT_EQ("This is a test", | 178 ASSERT_EQ("This is a test", |
| 172 ExecuteWithResults(&db(), kXSql, "|", "\n")); | 179 ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 173 } | 180 } |
| 174 | 181 |
| 175 // Test operation of the virtual table used by sql::Recovery. | 182 // Test operation of the virtual table used by sql::Recovery. |
| 176 TEST_F(SQLRecoveryTest, VirtualTable) { | 183 TEST_F(SQLRecoveryTest, VirtualTable) { |
| 177 const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; | 184 const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; |
| 178 ASSERT_TRUE(db().Execute(kCreateSql)); | 185 ASSERT_TRUE(db().Execute(kCreateSql)); |
| 179 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')")); | 186 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')")); |
| 180 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test')")); | 187 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test')")); |
| 181 | 188 |
| 182 // Successfully recover the database. | 189 // Successfully recover the database. |
| 183 { | 190 { |
| 184 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 191 std::unique_ptr<sql::Recovery> recovery = |
| 192 sql::Recovery::Begin(&db(), db_path()); |
| 185 | 193 |
| 186 // Tables to recover original DB, now at [corrupt]. | 194 // Tables to recover original DB, now at [corrupt]. |
| 187 const char kRecoveryCreateSql[] = | 195 const char kRecoveryCreateSql[] = |
| 188 "CREATE VIRTUAL TABLE temp.recover_x using recover(" | 196 "CREATE VIRTUAL TABLE temp.recover_x using recover(" |
| 189 " corrupt.x," | 197 " corrupt.x," |
| 190 " t TEXT STRICT" | 198 " t TEXT STRICT" |
| 191 ")"; | 199 ")"; |
| 192 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql)); | 200 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql)); |
| 193 | 201 |
| 194 // Re-create the original schema. | 202 // Re-create the original schema. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 214 } | 222 } |
| 215 | 223 |
| 216 void RecoveryCallback(sql::Connection* db, const base::FilePath& db_path, | 224 void RecoveryCallback(sql::Connection* db, const base::FilePath& db_path, |
| 217 const char* create_table, const char* create_index, | 225 const char* create_table, const char* create_index, |
| 218 int* record_error, int error, sql::Statement* stmt) { | 226 int* record_error, int error, sql::Statement* stmt) { |
| 219 *record_error = error; | 227 *record_error = error; |
| 220 | 228 |
| 221 // Clear the error callback to prevent reentrancy. | 229 // Clear the error callback to prevent reentrancy. |
| 222 db->reset_error_callback(); | 230 db->reset_error_callback(); |
| 223 | 231 |
| 224 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); | 232 std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); |
| 225 ASSERT_TRUE(recovery.get()); | 233 ASSERT_TRUE(recovery.get()); |
| 226 | 234 |
| 227 ASSERT_TRUE(recovery->db()->Execute(create_table)); | 235 ASSERT_TRUE(recovery->db()->Execute(create_table)); |
| 228 ASSERT_TRUE(recovery->db()->Execute(create_index)); | 236 ASSERT_TRUE(recovery->db()->Execute(create_index)); |
| 229 | 237 |
| 230 size_t rows = 0; | 238 size_t rows = 0; |
| 231 ASSERT_TRUE(recovery->AutoRecoverTable("x", &rows)); | 239 ASSERT_TRUE(recovery->AutoRecoverTable("x", &rows)); |
| 232 | 240 |
| 233 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); | 241 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 234 } | 242 } |
| (...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 379 const int kCompatibleVersion = 2; | 387 const int kCompatibleVersion = 2; |
| 380 | 388 |
| 381 { | 389 { |
| 382 sql::MetaTable meta; | 390 sql::MetaTable meta; |
| 383 EXPECT_TRUE(meta.Init(&db(), kVersion, kCompatibleVersion)); | 391 EXPECT_TRUE(meta.Init(&db(), kVersion, kCompatibleVersion)); |
| 384 EXPECT_EQ(kVersion, meta.GetVersionNumber()); | 392 EXPECT_EQ(kVersion, meta.GetVersionNumber()); |
| 385 } | 393 } |
| 386 | 394 |
| 387 // Test expected case where everything works. | 395 // Test expected case where everything works. |
| 388 { | 396 { |
| 389 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 397 std::unique_ptr<sql::Recovery> recovery = |
| 398 sql::Recovery::Begin(&db(), db_path()); |
| 390 EXPECT_TRUE(recovery->SetupMeta()); | 399 EXPECT_TRUE(recovery->SetupMeta()); |
| 391 int version = 0; | 400 int version = 0; |
| 392 EXPECT_TRUE(recovery->GetMetaVersionNumber(&version)); | 401 EXPECT_TRUE(recovery->GetMetaVersionNumber(&version)); |
| 393 EXPECT_EQ(kVersion, version); | 402 EXPECT_EQ(kVersion, version); |
| 394 | 403 |
| 395 sql::Recovery::Rollback(std::move(recovery)); | 404 sql::Recovery::Rollback(std::move(recovery)); |
| 396 } | 405 } |
| 397 ASSERT_TRUE(Reopen()); // Handle was poisoned. | 406 ASSERT_TRUE(Reopen()); // Handle was poisoned. |
| 398 | 407 |
| 399 // Test version row missing. | 408 // Test version row missing. |
| 400 EXPECT_TRUE(db().Execute("DELETE FROM meta WHERE key = 'version'")); | 409 EXPECT_TRUE(db().Execute("DELETE FROM meta WHERE key = 'version'")); |
| 401 { | 410 { |
| 402 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 411 std::unique_ptr<sql::Recovery> recovery = |
| 412 sql::Recovery::Begin(&db(), db_path()); |
| 403 EXPECT_TRUE(recovery->SetupMeta()); | 413 EXPECT_TRUE(recovery->SetupMeta()); |
| 404 int version = 0; | 414 int version = 0; |
| 405 EXPECT_FALSE(recovery->GetMetaVersionNumber(&version)); | 415 EXPECT_FALSE(recovery->GetMetaVersionNumber(&version)); |
| 406 EXPECT_EQ(0, version); | 416 EXPECT_EQ(0, version); |
| 407 | 417 |
| 408 sql::Recovery::Rollback(std::move(recovery)); | 418 sql::Recovery::Rollback(std::move(recovery)); |
| 409 } | 419 } |
| 410 ASSERT_TRUE(Reopen()); // Handle was poisoned. | 420 ASSERT_TRUE(Reopen()); // Handle was poisoned. |
| 411 | 421 |
| 412 // Test meta table missing. | 422 // Test meta table missing. |
| 413 EXPECT_TRUE(db().Execute("DROP TABLE meta")); | 423 EXPECT_TRUE(db().Execute("DROP TABLE meta")); |
| 414 { | 424 { |
| 415 sql::ScopedErrorIgnorer ignore_errors; | 425 sql::ScopedErrorIgnorer ignore_errors; |
| 416 ignore_errors.IgnoreError(SQLITE_CORRUPT); // From virtual table. | 426 ignore_errors.IgnoreError(SQLITE_CORRUPT); // From virtual table. |
| 417 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 427 std::unique_ptr<sql::Recovery> recovery = |
| 428 sql::Recovery::Begin(&db(), db_path()); |
| 418 EXPECT_FALSE(recovery->SetupMeta()); | 429 EXPECT_FALSE(recovery->SetupMeta()); |
| 419 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors()); | 430 ASSERT_TRUE(ignore_errors.CheckIgnoredErrors()); |
| 420 } | 431 } |
| 421 } | 432 } |
| 422 | 433 |
| 423 // Baseline AutoRecoverTable() test. | 434 // Baseline AutoRecoverTable() test. |
| 424 TEST_F(SQLRecoveryTest, AutoRecoverTable) { | 435 TEST_F(SQLRecoveryTest, AutoRecoverTable) { |
| 425 // BIGINT and VARCHAR to test type affinity. | 436 // BIGINT and VARCHAR to test type affinity. |
| 426 const char kCreateSql[] = "CREATE TABLE x (id BIGINT, t TEXT, v VARCHAR)"; | 437 const char kCreateSql[] = "CREATE TABLE x (id BIGINT, t TEXT, v VARCHAR)"; |
| 427 ASSERT_TRUE(db().Execute(kCreateSql)); | 438 ASSERT_TRUE(db().Execute(kCreateSql)); |
| 428 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (11, 'This is', 'a test')")); | 439 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (11, 'This is', 'a test')")); |
| 429 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5, 'That was', 'a test')")); | 440 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5, 'That was', 'a test')")); |
| 430 | 441 |
| 431 // Save aside a copy of the original schema and data. | 442 // Save aside a copy of the original schema and data. |
| 432 const std::string orig_schema(GetSchema(&db())); | 443 const std::string orig_schema(GetSchema(&db())); |
| 433 const char kXSql[] = "SELECT * FROM x ORDER BY 1"; | 444 const char kXSql[] = "SELECT * FROM x ORDER BY 1"; |
| 434 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n")); | 445 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 435 | 446 |
| 436 // Create a lame-duck table which will not be propagated by recovery to | 447 // Create a lame-duck table which will not be propagated by recovery to |
| 437 // detect that the recovery code actually ran. | 448 // detect that the recovery code actually ran. |
| 438 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)")); | 449 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)")); |
| 439 ASSERT_NE(orig_schema, GetSchema(&db())); | 450 ASSERT_NE(orig_schema, GetSchema(&db())); |
| 440 | 451 |
| 441 { | 452 { |
| 442 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 453 std::unique_ptr<sql::Recovery> recovery = |
| 454 sql::Recovery::Begin(&db(), db_path()); |
| 443 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 455 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 444 | 456 |
| 445 // Save a copy of the temp db's schema before recovering the table. | 457 // Save a copy of the temp db's schema before recovering the table. |
| 446 const char kTempSchemaSql[] = "SELECT name, sql FROM sqlite_temp_master"; | 458 const char kTempSchemaSql[] = "SELECT name, sql FROM sqlite_temp_master"; |
| 447 const std::string temp_schema( | 459 const std::string temp_schema( |
| 448 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n")); | 460 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n")); |
| 449 | 461 |
| 450 size_t rows = 0; | 462 size_t rows = 0; |
| 451 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); | 463 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); |
| 452 EXPECT_EQ(2u, rows); | 464 EXPECT_EQ(2u, rows); |
| 453 | 465 |
| 454 // Test that any additional temp tables were cleaned up. | 466 // Test that any additional temp tables were cleaned up. |
| 455 EXPECT_EQ(temp_schema, | 467 EXPECT_EQ(temp_schema, |
| 456 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n")); | 468 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n")); |
| 457 | 469 |
| 458 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); | 470 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 459 } | 471 } |
| 460 | 472 |
| 461 // Since the database was not corrupt, the entire schema and all | 473 // Since the database was not corrupt, the entire schema and all |
| 462 // data should be recovered. | 474 // data should be recovered. |
| 463 ASSERT_TRUE(Reopen()); | 475 ASSERT_TRUE(Reopen()); |
| 464 ASSERT_EQ(orig_schema, GetSchema(&db())); | 476 ASSERT_EQ(orig_schema, GetSchema(&db())); |
| 465 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); | 477 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 466 | 478 |
| 467 // Recovery fails if the target table doesn't exist. | 479 // Recovery fails if the target table doesn't exist. |
| 468 { | 480 { |
| 469 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 481 std::unique_ptr<sql::Recovery> recovery = |
| 482 sql::Recovery::Begin(&db(), db_path()); |
| 470 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 483 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 471 | 484 |
| 472 // TODO(shess): Should this failure implicitly lead to Raze()? | 485 // TODO(shess): Should this failure implicitly lead to Raze()? |
| 473 size_t rows = 0; | 486 size_t rows = 0; |
| 474 EXPECT_FALSE(recovery->AutoRecoverTable("y", &rows)); | 487 EXPECT_FALSE(recovery->AutoRecoverTable("y", &rows)); |
| 475 | 488 |
| 476 sql::Recovery::Unrecoverable(std::move(recovery)); | 489 sql::Recovery::Unrecoverable(std::move(recovery)); |
| 477 } | 490 } |
| 478 } | 491 } |
| 479 | 492 |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 516 std::string final_data(orig_data); | 529 std::string final_data(orig_data); |
| 517 size_t pos; | 530 size_t pos; |
| 518 while ((pos = final_schema.find("'a''a'")) != std::string::npos) { | 531 while ((pos = final_schema.find("'a''a'")) != std::string::npos) { |
| 519 final_schema.replace(pos, 6, "'c''c'"); | 532 final_schema.replace(pos, 6, "'c''c'"); |
| 520 } | 533 } |
| 521 while ((pos = final_data.find("5|a'a")) != std::string::npos) { | 534 while ((pos = final_data.find("5|a'a")) != std::string::npos) { |
| 522 final_data.replace(pos, 5, "5|c'c"); | 535 final_data.replace(pos, 5, "5|c'c"); |
| 523 } | 536 } |
| 524 | 537 |
| 525 { | 538 { |
| 526 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 539 std::unique_ptr<sql::Recovery> recovery = |
| 540 sql::Recovery::Begin(&db(), db_path()); |
| 527 // Different default to detect which table provides the default. | 541 // Different default to detect which table provides the default. |
| 528 ASSERT_TRUE(recovery->db()->Execute(final_schema.c_str())); | 542 ASSERT_TRUE(recovery->db()->Execute(final_schema.c_str())); |
| 529 | 543 |
| 530 size_t rows = 0; | 544 size_t rows = 0; |
| 531 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); | 545 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); |
| 532 EXPECT_EQ(4u, rows); | 546 EXPECT_EQ(4u, rows); |
| 533 | 547 |
| 534 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); | 548 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 535 } | 549 } |
| 536 | 550 |
| (...skipping 16 matching lines...) Expand all Loading... |
| 553 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5, null)")); | 567 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5, null)")); |
| 554 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (15, 'this is a test')")); | 568 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (15, 'this is a test')")); |
| 555 | 569 |
| 556 // Create a lame-duck table which will not be propagated by recovery to | 570 // Create a lame-duck table which will not be propagated by recovery to |
| 557 // detect that the recovery code actually ran. | 571 // detect that the recovery code actually ran. |
| 558 ASSERT_EQ(kOrigSchema, GetSchema(&db())); | 572 ASSERT_EQ(kOrigSchema, GetSchema(&db())); |
| 559 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)")); | 573 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)")); |
| 560 ASSERT_NE(kOrigSchema, GetSchema(&db())); | 574 ASSERT_NE(kOrigSchema, GetSchema(&db())); |
| 561 | 575 |
| 562 { | 576 { |
| 563 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 577 std::unique_ptr<sql::Recovery> recovery = |
| 578 sql::Recovery::Begin(&db(), db_path()); |
| 564 ASSERT_TRUE(recovery->db()->Execute(kFinalSchema)); | 579 ASSERT_TRUE(recovery->db()->Execute(kFinalSchema)); |
| 565 | 580 |
| 566 size_t rows = 0; | 581 size_t rows = 0; |
| 567 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); | 582 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); |
| 568 EXPECT_EQ(1u, rows); | 583 EXPECT_EQ(1u, rows); |
| 569 | 584 |
| 570 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); | 585 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 571 } | 586 } |
| 572 | 587 |
| 573 // The schema should be the same, but only one row of data should | 588 // The schema should be the same, but only one row of data should |
| (...skipping 18 matching lines...) Expand all Loading... |
| 592 const std::string orig_schema(GetSchema(&db())); | 607 const std::string orig_schema(GetSchema(&db())); |
| 593 const char kXSql[] = "SELECT * FROM x ORDER BY 1"; | 608 const char kXSql[] = "SELECT * FROM x ORDER BY 1"; |
| 594 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n")); | 609 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 595 | 610 |
| 596 // Create a lame-duck table which will not be propagated by recovery to | 611 // Create a lame-duck table which will not be propagated by recovery to |
| 597 // detect that the recovery code actually ran. | 612 // detect that the recovery code actually ran. |
| 598 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)")); | 613 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)")); |
| 599 ASSERT_NE(orig_schema, GetSchema(&db())); | 614 ASSERT_NE(orig_schema, GetSchema(&db())); |
| 600 | 615 |
| 601 { | 616 { |
| 602 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 617 std::unique_ptr<sql::Recovery> recovery = |
| 618 sql::Recovery::Begin(&db(), db_path()); |
| 603 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 619 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 604 | 620 |
| 605 size_t rows = 0; | 621 size_t rows = 0; |
| 606 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); | 622 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); |
| 607 EXPECT_EQ(2u, rows); | 623 EXPECT_EQ(2u, rows); |
| 608 | 624 |
| 609 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); | 625 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 610 } | 626 } |
| 611 | 627 |
| 612 // Since the database was not corrupt, the entire schema and all | 628 // Since the database was not corrupt, the entire schema and all |
| (...skipping 24 matching lines...) Expand all Loading... |
| 637 const std::string orig_schema(GetSchema(&db())); | 653 const std::string orig_schema(GetSchema(&db())); |
| 638 const char kXSql[] = "SELECT * FROM x ORDER BY 1"; | 654 const char kXSql[] = "SELECT * FROM x ORDER BY 1"; |
| 639 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n")); | 655 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 640 | 656 |
| 641 // Create a lame-duck table which will not be propagated by recovery to | 657 // Create a lame-duck table which will not be propagated by recovery to |
| 642 // detect that the recovery code actually ran. | 658 // detect that the recovery code actually ran. |
| 643 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)")); | 659 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)")); |
| 644 ASSERT_NE(orig_schema, GetSchema(&db())); | 660 ASSERT_NE(orig_schema, GetSchema(&db())); |
| 645 | 661 |
| 646 { | 662 { |
| 647 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 663 std::unique_ptr<sql::Recovery> recovery = |
| 664 sql::Recovery::Begin(&db(), db_path()); |
| 648 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 665 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 649 | 666 |
| 650 size_t rows = 0; | 667 size_t rows = 0; |
| 651 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); | 668 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); |
| 652 EXPECT_EQ(3u, rows); | 669 EXPECT_EQ(3u, rows); |
| 653 | 670 |
| 654 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); | 671 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 655 } | 672 } |
| 656 | 673 |
| 657 // Since the database was not corrupt, the entire schema and all | 674 // Since the database was not corrupt, the entire schema and all |
| (...skipping 25 matching lines...) Expand all Loading... |
| 683 expected_data = ExecuteWithResults(&db(), kXSql, "|", "\n"); | 700 expected_data = ExecuteWithResults(&db(), kXSql, "|", "\n"); |
| 684 | 701 |
| 685 db().RollbackTransaction(); | 702 db().RollbackTransaction(); |
| 686 } | 703 } |
| 687 | 704 |
| 688 // Following tests are pointless if the rollback didn't work. | 705 // Following tests are pointless if the rollback didn't work. |
| 689 ASSERT_EQ(orig_schema, GetSchema(&db())); | 706 ASSERT_EQ(orig_schema, GetSchema(&db())); |
| 690 | 707 |
| 691 // Recover the previous version of the table into the altered version. | 708 // Recover the previous version of the table into the altered version. |
| 692 { | 709 { |
| 693 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 710 std::unique_ptr<sql::Recovery> recovery = |
| 711 sql::Recovery::Begin(&db(), db_path()); |
| 694 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 712 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 695 ASSERT_TRUE(recovery->db()->Execute(kAlterSql)); | 713 ASSERT_TRUE(recovery->db()->Execute(kAlterSql)); |
| 696 size_t rows = 0; | 714 size_t rows = 0; |
| 697 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); | 715 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); |
| 698 EXPECT_EQ(2u, rows); | 716 EXPECT_EQ(2u, rows); |
| 699 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); | 717 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 700 } | 718 } |
| 701 | 719 |
| 702 // Since the database was not corrupt, the entire schema and all | 720 // Since the database was not corrupt, the entire schema and all |
| 703 // data should be recovered. | 721 // data should be recovered. |
| 704 ASSERT_TRUE(Reopen()); | 722 ASSERT_TRUE(Reopen()); |
| 705 ASSERT_EQ(expected_schema, GetSchema(&db())); | 723 ASSERT_EQ(expected_schema, GetSchema(&db())); |
| 706 ASSERT_EQ(expected_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); | 724 ASSERT_EQ(expected_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 707 } | 725 } |
| 708 | 726 |
| 709 // Recover a golden file where an interior page has been manually modified so | 727 // Recover a golden file where an interior page has been manually modified so |
| 710 // that the number of cells is greater than will fit on a single page. This | 728 // that the number of cells is greater than will fit on a single page. This |
| 711 // case happened in <http://crbug.com/387868>. | 729 // case happened in <http://crbug.com/387868>. |
| 712 TEST_F(SQLRecoveryTest, Bug387868) { | 730 TEST_F(SQLRecoveryTest, Bug387868) { |
| 713 base::FilePath golden_path; | 731 base::FilePath golden_path; |
| 714 ASSERT_TRUE(PathService::Get(sql::test::DIR_TEST_DATA, &golden_path)); | 732 ASSERT_TRUE(PathService::Get(sql::test::DIR_TEST_DATA, &golden_path)); |
| 715 golden_path = golden_path.AppendASCII("recovery_387868"); | 733 golden_path = golden_path.AppendASCII("recovery_387868"); |
| 716 db().Close(); | 734 db().Close(); |
| 717 ASSERT_TRUE(base::CopyFile(golden_path, db_path())); | 735 ASSERT_TRUE(base::CopyFile(golden_path, db_path())); |
| 718 ASSERT_TRUE(Reopen()); | 736 ASSERT_TRUE(Reopen()); |
| 719 | 737 |
| 720 { | 738 { |
| 721 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 739 std::unique_ptr<sql::Recovery> recovery = |
| 740 sql::Recovery::Begin(&db(), db_path()); |
| 722 ASSERT_TRUE(recovery.get()); | 741 ASSERT_TRUE(recovery.get()); |
| 723 | 742 |
| 724 // Create the new version of the table. | 743 // Create the new version of the table. |
| 725 const char kCreateSql[] = | 744 const char kCreateSql[] = |
| 726 "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)"; | 745 "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)"; |
| 727 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 746 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 728 | 747 |
| 729 size_t rows = 0; | 748 size_t rows = 0; |
| 730 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); | 749 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); |
| 731 EXPECT_EQ(43u, rows); | 750 EXPECT_EQ(43u, rows); |
| 732 | 751 |
| 733 // Successfully recovered. | 752 // Successfully recovered. |
| 734 EXPECT_TRUE(sql::Recovery::Recovered(std::move(recovery))); | 753 EXPECT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 735 } | 754 } |
| 736 } | 755 } |
| 737 | 756 |
| 738 // Memory-mapped I/O interacts poorly with I/O errors. Make sure the recovery | 757 // Memory-mapped I/O interacts poorly with I/O errors. Make sure the recovery |
| 739 // database doesn't accidentally enable it. | 758 // database doesn't accidentally enable it. |
| 740 TEST_F(SQLRecoveryTest, NoMmap) { | 759 TEST_F(SQLRecoveryTest, NoMmap) { |
| 741 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 760 std::unique_ptr<sql::Recovery> recovery = |
| 761 sql::Recovery::Begin(&db(), db_path()); |
| 742 ASSERT_TRUE(recovery.get()); | 762 ASSERT_TRUE(recovery.get()); |
| 743 | 763 |
| 744 // In the current implementation, the PRAGMA successfully runs with no result | 764 // In the current implementation, the PRAGMA successfully runs with no result |
| 745 // rows. Running with a single result of |0| is also acceptable. | 765 // rows. Running with a single result of |0| is also acceptable. |
| 746 sql::Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size")); | 766 sql::Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size")); |
| 747 EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0)); | 767 EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0)); |
| 748 } | 768 } |
| 749 | 769 |
| 750 } // namespace | 770 } // namespace |
| OLD | NEW |