| 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" |
| 6 |
| 5 #include <stddef.h> | 7 #include <stddef.h> |
| 6 | |
| 7 #include <string> | 8 #include <string> |
| 9 #include <utility> |
| 8 | 10 |
| 9 #include "base/bind.h" | 11 #include "base/bind.h" |
| 10 #include "base/files/file_path.h" | 12 #include "base/files/file_path.h" |
| 11 #include "base/files/file_util.h" | 13 #include "base/files/file_util.h" |
| 12 #include "base/files/scoped_temp_dir.h" | 14 #include "base/files/scoped_temp_dir.h" |
| 13 #include "base/path_service.h" | 15 #include "base/path_service.h" |
| 14 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
| 15 #include "sql/connection.h" | 17 #include "sql/connection.h" |
| 16 #include "sql/meta_table.h" | 18 #include "sql/meta_table.h" |
| 17 #include "sql/recovery.h" | |
| 18 #include "sql/statement.h" | 19 #include "sql/statement.h" |
| 19 #include "sql/test/paths.h" | 20 #include "sql/test/paths.h" |
| 20 #include "sql/test/scoped_error_ignorer.h" | 21 #include "sql/test/scoped_error_ignorer.h" |
| 21 #include "sql/test/sql_test_base.h" | 22 #include "sql/test/sql_test_base.h" |
| 22 #include "sql/test/test_helpers.h" | 23 #include "sql/test/test_helpers.h" |
| 23 #include "testing/gtest/include/gtest/gtest.h" | 24 #include "testing/gtest/include/gtest/gtest.h" |
| 24 #include "third_party/sqlite/sqlite3.h" | 25 #include "third_party/sqlite/sqlite3.h" |
| 25 | 26 |
| 26 namespace { | 27 namespace { |
| 27 | 28 |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 86 | 87 |
| 87 // Recreate the database. | 88 // Recreate the database. |
| 88 ASSERT_TRUE(db().Execute(kCreateSql)); | 89 ASSERT_TRUE(db().Execute(kCreateSql)); |
| 89 ASSERT_TRUE(db().Execute(kInsertSql)); | 90 ASSERT_TRUE(db().Execute(kInsertSql)); |
| 90 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); | 91 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 91 | 92 |
| 92 // Unrecoverable() also razes. | 93 // Unrecoverable() also razes. |
| 93 { | 94 { |
| 94 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 95 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 95 ASSERT_TRUE(recovery.get()); | 96 ASSERT_TRUE(recovery.get()); |
| 96 sql::Recovery::Unrecoverable(recovery.Pass()); | 97 sql::Recovery::Unrecoverable(std::move(recovery)); |
| 97 | 98 |
| 98 // TODO(shess): Test that calls to recover.db() start failing. | 99 // TODO(shess): Test that calls to recover.db() start failing. |
| 99 } | 100 } |
| 100 EXPECT_FALSE(db().is_open()); | 101 EXPECT_FALSE(db().is_open()); |
| 101 ASSERT_TRUE(Reopen()); | 102 ASSERT_TRUE(Reopen()); |
| 102 EXPECT_TRUE(db().is_open()); | 103 EXPECT_TRUE(db().is_open()); |
| 103 ASSERT_EQ("", GetSchema(&db())); | 104 ASSERT_EQ("", GetSchema(&db())); |
| 104 | 105 |
| 105 // Recreate the database. | 106 // Recreate the database. |
| 106 ASSERT_TRUE(db().Execute(kCreateSql)); | 107 ASSERT_TRUE(db().Execute(kCreateSql)); |
| 107 ASSERT_TRUE(db().Execute(kInsertSql)); | 108 ASSERT_TRUE(db().Execute(kInsertSql)); |
| 108 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); | 109 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 109 | 110 |
| 110 // Recovered() replaces the original with the "recovered" version. | 111 // Recovered() replaces the original with the "recovered" version. |
| 111 { | 112 { |
| 112 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 113 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 113 ASSERT_TRUE(recovery.get()); | 114 ASSERT_TRUE(recovery.get()); |
| 114 | 115 |
| 115 // Create the new version of the table. | 116 // Create the new version of the table. |
| 116 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 117 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 117 | 118 |
| 118 // Insert different data to distinguish from original database. | 119 // Insert different data to distinguish from original database. |
| 119 const char kAltInsertSql[] = "INSERT INTO x VALUES ('That was a test')"; | 120 const char kAltInsertSql[] = "INSERT INTO x VALUES ('That was a test')"; |
| 120 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); | 121 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); |
| 121 | 122 |
| 122 // Successfully recovered. | 123 // Successfully recovered. |
| 123 ASSERT_TRUE(sql::Recovery::Recovered(recovery.Pass())); | 124 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 124 } | 125 } |
| 125 EXPECT_FALSE(db().is_open()); | 126 EXPECT_FALSE(db().is_open()); |
| 126 ASSERT_TRUE(Reopen()); | 127 ASSERT_TRUE(Reopen()); |
| 127 EXPECT_TRUE(db().is_open()); | 128 EXPECT_TRUE(db().is_open()); |
| 128 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); | 129 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 129 | 130 |
| 130 const char* kXSql = "SELECT * FROM x ORDER BY 1"; | 131 const char* kXSql = "SELECT * FROM x ORDER BY 1"; |
| 131 ASSERT_EQ("That was a test", | 132 ASSERT_EQ("That was a test", |
| 132 ExecuteWithResults(&db(), kXSql, "|", "\n")); | 133 ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 133 } | 134 } |
| (...skipping 22 matching lines...) Expand all Loading... |
| 156 | 157 |
| 157 // Re-create the original schema. | 158 // Re-create the original schema. |
| 158 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 159 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 159 | 160 |
| 160 // Copy the data from the recovery tables to the new database. | 161 // Copy the data from the recovery tables to the new database. |
| 161 const char kRecoveryCopySql[] = | 162 const char kRecoveryCopySql[] = |
| 162 "INSERT INTO x SELECT t FROM recover_x"; | 163 "INSERT INTO x SELECT t FROM recover_x"; |
| 163 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql)); | 164 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql)); |
| 164 | 165 |
| 165 // Successfully recovered. | 166 // Successfully recovered. |
| 166 ASSERT_TRUE(sql::Recovery::Recovered(recovery.Pass())); | 167 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 167 } | 168 } |
| 168 | 169 |
| 169 // Since the database was not corrupt, the entire schema and all | 170 // Since the database was not corrupt, the entire schema and all |
| 170 // data should be recovered. | 171 // data should be recovered. |
| 171 ASSERT_TRUE(Reopen()); | 172 ASSERT_TRUE(Reopen()); |
| 172 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); | 173 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
| 173 | 174 |
| 174 const char* kXSql = "SELECT * FROM x ORDER BY 1"; | 175 const char* kXSql = "SELECT * FROM x ORDER BY 1"; |
| 175 ASSERT_EQ("That was a test\nThis is a test", | 176 ASSERT_EQ("That was a test\nThis is a test", |
| 176 ExecuteWithResults(&db(), kXSql, "|", "\n")); | 177 ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 197 | 198 |
| 198 // Replicate data over. | 199 // Replicate data over. |
| 199 const char kRecoveryCopySql[] = | 200 const char kRecoveryCopySql[] = |
| 200 "INSERT OR REPLACE INTO x SELECT id, v FROM recover_x"; | 201 "INSERT OR REPLACE INTO x SELECT id, v FROM recover_x"; |
| 201 | 202 |
| 202 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql)); | 203 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql)); |
| 203 ASSERT_TRUE(recovery->db()->Execute(kCreateTable)); | 204 ASSERT_TRUE(recovery->db()->Execute(kCreateTable)); |
| 204 ASSERT_TRUE(recovery->db()->Execute(kCreateIndex)); | 205 ASSERT_TRUE(recovery->db()->Execute(kCreateIndex)); |
| 205 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql)); | 206 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql)); |
| 206 | 207 |
| 207 ASSERT_TRUE(sql::Recovery::Recovered(recovery.Pass())); | 208 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 208 } | 209 } |
| 209 | 210 |
| 210 // Build a database, corrupt it by making an index reference to | 211 // Build a database, corrupt it by making an index reference to |
| 211 // deleted row, then recover when a query selects that row. | 212 // deleted row, then recover when a query selects that row. |
| 212 TEST_F(SQLRecoveryTest, RecoverCorruptIndex) { | 213 TEST_F(SQLRecoveryTest, RecoverCorruptIndex) { |
| 213 const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)"; | 214 const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)"; |
| 214 const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)"; | 215 const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)"; |
| 215 ASSERT_TRUE(db().Execute(kCreateTable)); | 216 ASSERT_TRUE(db().Execute(kCreateTable)); |
| 216 ASSERT_TRUE(db().Execute(kCreateIndex)); | 217 ASSERT_TRUE(db().Execute(kCreateIndex)); |
| 217 | 218 |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 354 } | 355 } |
| 355 | 356 |
| 356 // Test expected case where everything works. | 357 // Test expected case where everything works. |
| 357 { | 358 { |
| 358 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 359 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 359 EXPECT_TRUE(recovery->SetupMeta()); | 360 EXPECT_TRUE(recovery->SetupMeta()); |
| 360 int version = 0; | 361 int version = 0; |
| 361 EXPECT_TRUE(recovery->GetMetaVersionNumber(&version)); | 362 EXPECT_TRUE(recovery->GetMetaVersionNumber(&version)); |
| 362 EXPECT_EQ(kVersion, version); | 363 EXPECT_EQ(kVersion, version); |
| 363 | 364 |
| 364 sql::Recovery::Rollback(recovery.Pass()); | 365 sql::Recovery::Rollback(std::move(recovery)); |
| 365 } | 366 } |
| 366 ASSERT_TRUE(Reopen()); // Handle was poisoned. | 367 ASSERT_TRUE(Reopen()); // Handle was poisoned. |
| 367 | 368 |
| 368 // Test version row missing. | 369 // Test version row missing. |
| 369 EXPECT_TRUE(db().Execute("DELETE FROM meta WHERE key = 'version'")); | 370 EXPECT_TRUE(db().Execute("DELETE FROM meta WHERE key = 'version'")); |
| 370 { | 371 { |
| 371 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 372 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 372 EXPECT_TRUE(recovery->SetupMeta()); | 373 EXPECT_TRUE(recovery->SetupMeta()); |
| 373 int version = 0; | 374 int version = 0; |
| 374 EXPECT_FALSE(recovery->GetMetaVersionNumber(&version)); | 375 EXPECT_FALSE(recovery->GetMetaVersionNumber(&version)); |
| 375 EXPECT_EQ(0, version); | 376 EXPECT_EQ(0, version); |
| 376 | 377 |
| 377 sql::Recovery::Rollback(recovery.Pass()); | 378 sql::Recovery::Rollback(std::move(recovery)); |
| 378 } | 379 } |
| 379 ASSERT_TRUE(Reopen()); // Handle was poisoned. | 380 ASSERT_TRUE(Reopen()); // Handle was poisoned. |
| 380 | 381 |
| 381 // Test meta table missing. | 382 // Test meta table missing. |
| 382 EXPECT_TRUE(db().Execute("DROP TABLE meta")); | 383 EXPECT_TRUE(db().Execute("DROP TABLE meta")); |
| 383 { | 384 { |
| 384 sql::ScopedErrorIgnorer ignore_errors; | 385 sql::ScopedErrorIgnorer ignore_errors; |
| 385 ignore_errors.IgnoreError(SQLITE_CORRUPT); // From virtual table. | 386 ignore_errors.IgnoreError(SQLITE_CORRUPT); // From virtual table. |
| 386 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 387 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 387 EXPECT_FALSE(recovery->SetupMeta()); | 388 EXPECT_FALSE(recovery->SetupMeta()); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 417 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n")); | 418 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n")); |
| 418 | 419 |
| 419 size_t rows = 0; | 420 size_t rows = 0; |
| 420 EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); | 421 EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); |
| 421 EXPECT_EQ(2u, rows); | 422 EXPECT_EQ(2u, rows); |
| 422 | 423 |
| 423 // Test that any additional temp tables were cleaned up. | 424 // Test that any additional temp tables were cleaned up. |
| 424 EXPECT_EQ(temp_schema, | 425 EXPECT_EQ(temp_schema, |
| 425 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n")); | 426 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n")); |
| 426 | 427 |
| 427 ASSERT_TRUE(sql::Recovery::Recovered(recovery.Pass())); | 428 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 428 } | 429 } |
| 429 | 430 |
| 430 // Since the database was not corrupt, the entire schema and all | 431 // Since the database was not corrupt, the entire schema and all |
| 431 // data should be recovered. | 432 // data should be recovered. |
| 432 ASSERT_TRUE(Reopen()); | 433 ASSERT_TRUE(Reopen()); |
| 433 ASSERT_EQ(orig_schema, GetSchema(&db())); | 434 ASSERT_EQ(orig_schema, GetSchema(&db())); |
| 434 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); | 435 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 435 | 436 |
| 436 // Recovery fails if the target table doesn't exist. | 437 // Recovery fails if the target table doesn't exist. |
| 437 { | 438 { |
| 438 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 439 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 439 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 440 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 440 | 441 |
| 441 // TODO(shess): Should this failure implicitly lead to Raze()? | 442 // TODO(shess): Should this failure implicitly lead to Raze()? |
| 442 size_t rows = 0; | 443 size_t rows = 0; |
| 443 EXPECT_FALSE(recovery->AutoRecoverTable("y", 0, &rows)); | 444 EXPECT_FALSE(recovery->AutoRecoverTable("y", 0, &rows)); |
| 444 | 445 |
| 445 sql::Recovery::Unrecoverable(recovery.Pass()); | 446 sql::Recovery::Unrecoverable(std::move(recovery)); |
| 446 } | 447 } |
| 447 } | 448 } |
| 448 | 449 |
| 449 // Test that default values correctly replace nulls. The recovery | 450 // Test that default values correctly replace nulls. The recovery |
| 450 // virtual table reads directly from the database, so DEFAULT is not | 451 // virtual table reads directly from the database, so DEFAULT is not |
| 451 // interpretted at that level. | 452 // interpretted at that level. |
| 452 TEST_F(SQLRecoveryTest, AutoRecoverTableWithDefault) { | 453 TEST_F(SQLRecoveryTest, AutoRecoverTableWithDefault) { |
| 453 ASSERT_TRUE(db().Execute("CREATE TABLE x (id INTEGER)")); | 454 ASSERT_TRUE(db().Execute("CREATE TABLE x (id INTEGER)")); |
| 454 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5)")); | 455 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5)")); |
| 455 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (15)")); | 456 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (15)")); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 493 | 494 |
| 494 { | 495 { |
| 495 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 496 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 496 // Different default to detect which table provides the default. | 497 // Different default to detect which table provides the default. |
| 497 ASSERT_TRUE(recovery->db()->Execute(final_schema.c_str())); | 498 ASSERT_TRUE(recovery->db()->Execute(final_schema.c_str())); |
| 498 | 499 |
| 499 size_t rows = 0; | 500 size_t rows = 0; |
| 500 EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); | 501 EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); |
| 501 EXPECT_EQ(4u, rows); | 502 EXPECT_EQ(4u, rows); |
| 502 | 503 |
| 503 ASSERT_TRUE(sql::Recovery::Recovered(recovery.Pass())); | 504 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 504 } | 505 } |
| 505 | 506 |
| 506 // Since the database was not corrupt, the entire schema and all | 507 // Since the database was not corrupt, the entire schema and all |
| 507 // data should be recovered. | 508 // data should be recovered. |
| 508 ASSERT_TRUE(Reopen()); | 509 ASSERT_TRUE(Reopen()); |
| 509 ASSERT_EQ(final_schema, GetSchema(&db())); | 510 ASSERT_EQ(final_schema, GetSchema(&db())); |
| 510 ASSERT_EQ(final_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); | 511 ASSERT_EQ(final_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 511 } | 512 } |
| 512 | 513 |
| 513 // Test that rows with NULL in a NOT NULL column are filtered | 514 // Test that rows with NULL in a NOT NULL column are filtered |
| (...skipping 15 matching lines...) Expand all Loading... |
| 529 ASSERT_NE(kOrigSchema, GetSchema(&db())); | 530 ASSERT_NE(kOrigSchema, GetSchema(&db())); |
| 530 | 531 |
| 531 { | 532 { |
| 532 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 533 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 533 ASSERT_TRUE(recovery->db()->Execute(kFinalSchema)); | 534 ASSERT_TRUE(recovery->db()->Execute(kFinalSchema)); |
| 534 | 535 |
| 535 size_t rows = 0; | 536 size_t rows = 0; |
| 536 EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); | 537 EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); |
| 537 EXPECT_EQ(1u, rows); | 538 EXPECT_EQ(1u, rows); |
| 538 | 539 |
| 539 ASSERT_TRUE(sql::Recovery::Recovered(recovery.Pass())); | 540 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 540 } | 541 } |
| 541 | 542 |
| 542 // The schema should be the same, but only one row of data should | 543 // The schema should be the same, but only one row of data should |
| 543 // have been recovered. | 544 // have been recovered. |
| 544 ASSERT_TRUE(Reopen()); | 545 ASSERT_TRUE(Reopen()); |
| 545 ASSERT_EQ(kFinalSchema, GetSchema(&db())); | 546 ASSERT_EQ(kFinalSchema, GetSchema(&db())); |
| 546 const char kXSql[] = "SELECT * FROM x ORDER BY 1"; | 547 const char kXSql[] = "SELECT * FROM x ORDER BY 1"; |
| 547 ASSERT_EQ("15|this is a test", ExecuteWithResults(&db(), kXSql, "|", "\n")); | 548 ASSERT_EQ("15|this is a test", ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 548 } | 549 } |
| 549 | 550 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 568 ASSERT_NE(orig_schema, GetSchema(&db())); | 569 ASSERT_NE(orig_schema, GetSchema(&db())); |
| 569 | 570 |
| 570 { | 571 { |
| 571 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 572 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 572 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 573 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 573 | 574 |
| 574 size_t rows = 0; | 575 size_t rows = 0; |
| 575 EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); | 576 EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); |
| 576 EXPECT_EQ(2u, rows); | 577 EXPECT_EQ(2u, rows); |
| 577 | 578 |
| 578 ASSERT_TRUE(sql::Recovery::Recovered(recovery.Pass())); | 579 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 579 } | 580 } |
| 580 | 581 |
| 581 // Since the database was not corrupt, the entire schema and all | 582 // Since the database was not corrupt, the entire schema and all |
| 582 // data should be recovered. | 583 // data should be recovered. |
| 583 ASSERT_TRUE(Reopen()); | 584 ASSERT_TRUE(Reopen()); |
| 584 ASSERT_EQ(orig_schema, GetSchema(&db())); | 585 ASSERT_EQ(orig_schema, GetSchema(&db())); |
| 585 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); | 586 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 586 } | 587 } |
| 587 | 588 |
| 588 // Test that a compound primary key doesn't fire the ROWID code. | 589 // Test that a compound primary key doesn't fire the ROWID code. |
| (...skipping 24 matching lines...) Expand all Loading... |
| 613 ASSERT_NE(orig_schema, GetSchema(&db())); | 614 ASSERT_NE(orig_schema, GetSchema(&db())); |
| 614 | 615 |
| 615 { | 616 { |
| 616 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 617 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 617 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 618 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 618 | 619 |
| 619 size_t rows = 0; | 620 size_t rows = 0; |
| 620 EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); | 621 EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); |
| 621 EXPECT_EQ(3u, rows); | 622 EXPECT_EQ(3u, rows); |
| 622 | 623 |
| 623 ASSERT_TRUE(sql::Recovery::Recovered(recovery.Pass())); | 624 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 624 } | 625 } |
| 625 | 626 |
| 626 // Since the database was not corrupt, the entire schema and all | 627 // Since the database was not corrupt, the entire schema and all |
| 627 // data should be recovered. | 628 // data should be recovered. |
| 628 ASSERT_TRUE(Reopen()); | 629 ASSERT_TRUE(Reopen()); |
| 629 ASSERT_EQ(orig_schema, GetSchema(&db())); | 630 ASSERT_EQ(orig_schema, GetSchema(&db())); |
| 630 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); | 631 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 631 } | 632 } |
| 632 | 633 |
| 633 // Test |extend_columns| support. | 634 // Test |extend_columns| support. |
| (...skipping 13 matching lines...) Expand all Loading... |
| 647 ASSERT_TRUE(db().Execute("UPDATE x SET t1 = 'a test'")); | 648 ASSERT_TRUE(db().Execute("UPDATE x SET t1 = 'a test'")); |
| 648 ASSERT_NE(orig_schema, GetSchema(&db())); | 649 ASSERT_NE(orig_schema, GetSchema(&db())); |
| 649 ASSERT_NE(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); | 650 ASSERT_NE(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 650 | 651 |
| 651 { | 652 { |
| 652 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 653 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 653 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 654 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 654 size_t rows = 0; | 655 size_t rows = 0; |
| 655 EXPECT_TRUE(recovery->AutoRecoverTable("x", 1, &rows)); | 656 EXPECT_TRUE(recovery->AutoRecoverTable("x", 1, &rows)); |
| 656 EXPECT_EQ(2u, rows); | 657 EXPECT_EQ(2u, rows); |
| 657 ASSERT_TRUE(sql::Recovery::Recovered(recovery.Pass())); | 658 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 658 } | 659 } |
| 659 | 660 |
| 660 // Since the database was not corrupt, the entire schema and all | 661 // Since the database was not corrupt, the entire schema and all |
| 661 // data should be recovered. | 662 // data should be recovered. |
| 662 ASSERT_TRUE(Reopen()); | 663 ASSERT_TRUE(Reopen()); |
| 663 ASSERT_EQ(orig_schema, GetSchema(&db())); | 664 ASSERT_EQ(orig_schema, GetSchema(&db())); |
| 664 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); | 665 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); |
| 665 } | 666 } |
| 666 | 667 |
| 667 // Recover a golden file where an interior page has been manually modified so | 668 // Recover a golden file where an interior page has been manually modified so |
| (...skipping 14 matching lines...) Expand all Loading... |
| 682 // Create the new version of the table. | 683 // Create the new version of the table. |
| 683 const char kCreateSql[] = | 684 const char kCreateSql[] = |
| 684 "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)"; | 685 "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)"; |
| 685 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); | 686 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
| 686 | 687 |
| 687 size_t rows = 0; | 688 size_t rows = 0; |
| 688 EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); | 689 EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); |
| 689 EXPECT_EQ(43u, rows); | 690 EXPECT_EQ(43u, rows); |
| 690 | 691 |
| 691 // Successfully recovered. | 692 // Successfully recovered. |
| 692 EXPECT_TRUE(sql::Recovery::Recovered(recovery.Pass())); | 693 EXPECT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
| 693 } | 694 } |
| 694 } | 695 } |
| 695 #endif // !defined(USE_SYSTEM_SQLITE) | 696 #endif // !defined(USE_SYSTEM_SQLITE) |
| 696 | 697 |
| 697 // Memory-mapped I/O interacts poorly with I/O errors. Make sure the recovery | 698 // Memory-mapped I/O interacts poorly with I/O errors. Make sure the recovery |
| 698 // database doesn't accidentally enable it. | 699 // database doesn't accidentally enable it. |
| 699 TEST_F(SQLRecoveryTest, NoMmap) { | 700 TEST_F(SQLRecoveryTest, NoMmap) { |
| 700 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); | 701 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
| 701 ASSERT_TRUE(recovery.get()); | 702 ASSERT_TRUE(recovery.get()); |
| 702 | 703 |
| 703 // In the current implementation, the PRAGMA successfully runs with no result | 704 // In the current implementation, the PRAGMA successfully runs with no result |
| 704 // rows. Running with a single result of |0| is also acceptable. | 705 // rows. Running with a single result of |0| is also acceptable. |
| 705 sql::Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size")); | 706 sql::Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size")); |
| 706 EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0)); | 707 EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0)); |
| 707 } | 708 } |
| 708 | 709 |
| 709 } // namespace | 710 } // namespace |
| OLD | NEW |