Index: sql/recovery_unittest.cc |
diff --git a/sql/recovery_unittest.cc b/sql/recovery_unittest.cc |
index 73d146fac1f1c61b7336b4a44dff7f0950d4f506..4990e8388d9aee7be0d6d347197430c97557e0d9 100644 |
--- a/sql/recovery_unittest.cc |
+++ b/sql/recovery_unittest.cc |
@@ -67,9 +67,12 @@ std::string GetSchema(sql::Connection* db) { |
using SQLRecoveryTest = sql::SQLTestBase; |
+// Baseline sql::Recovery test covering the different ways to dispose of the |
+// scoped pointer received from sql::Recovery::Begin(). |
TEST_F(SQLRecoveryTest, RecoverBasic) { |
const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; |
const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')"; |
+ const char kAltInsertSql[] = "INSERT INTO x VALUES ('That was a test')"; |
ASSERT_TRUE(db().Execute(kCreateSql)); |
ASSERT_TRUE(db().Execute(kInsertSql)); |
ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
@@ -119,6 +122,10 @@ TEST_F(SQLRecoveryTest, RecoverBasic) { |
ASSERT_TRUE(db().Execute(kInsertSql)); |
ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
+ // Unrecovered table to distinguish from recovered database. |
+ ASSERT_TRUE(db().Execute("CREATE TABLE y (c INTEGER)")); |
+ ASSERT_NE("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
+ |
// Recovered() replaces the original with the "recovered" version. |
{ |
scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
@@ -128,7 +135,6 @@ TEST_F(SQLRecoveryTest, RecoverBasic) { |
ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
// Insert different data to distinguish from original database. |
- const char kAltInsertSql[] = "INSERT INTO x VALUES ('That was a test')"; |
ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); |
// Successfully recovered. |
@@ -142,12 +148,34 @@ TEST_F(SQLRecoveryTest, RecoverBasic) { |
const char* kXSql = "SELECT * FROM x ORDER BY 1"; |
ASSERT_EQ("That was a test", |
ExecuteWithResults(&db(), kXSql, "|", "\n")); |
+ |
+ // Reset the database contents. |
+ ASSERT_TRUE(db().Execute("DELETE FROM x")); |
+ ASSERT_TRUE(db().Execute(kInsertSql)); |
+ |
+ // Rollback() discards recovery progress and leaves the database as it was. |
+ { |
+ scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); |
+ ASSERT_TRUE(recovery.get()); |
+ |
+ ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); |
+ ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); |
+ |
+ sql::Recovery::Rollback(std::move(recovery)); |
+ } |
+ EXPECT_FALSE(db().is_open()); |
+ ASSERT_TRUE(Reopen()); |
+ EXPECT_TRUE(db().is_open()); |
+ ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); |
+ |
+ ASSERT_EQ("This is a test", |
+ ExecuteWithResults(&db(), kXSql, "|", "\n")); |
} |
// The recovery virtual table is only supported for Chromium's SQLite. |
#if !defined(USE_SYSTEM_SQLITE) |
Ryan Hamilton
2016/02/04 20:24:19
Can you remove this now since you're not using the
Scott Hess - ex-Googler
2016/02/04 20:47:50
AutoRecoverTable() is itself implemented in terms
|
-// Run recovery through its paces on a valid database. |
+// Test operation of the virtual table used by sql::Recovery. |
TEST_F(SQLRecoveryTest, VirtualTable) { |
const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; |
ASSERT_TRUE(db().Execute(kCreateSql)); |
@@ -189,6 +217,7 @@ TEST_F(SQLRecoveryTest, VirtualTable) { |
} |
void RecoveryCallback(sql::Connection* db, const base::FilePath& db_path, |
+ const char* create_table, const char* create_index, |
int* record_error, int error, sql::Statement* stmt) { |
*record_error = error; |
@@ -198,23 +227,11 @@ void RecoveryCallback(sql::Connection* db, const base::FilePath& db_path, |
scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); |
ASSERT_TRUE(recovery.get()); |
- const char kRecoveryCreateSql[] = |
- "CREATE VIRTUAL TABLE temp.recover_x using recover(" |
- " corrupt.x," |
- " id INTEGER STRICT," |
- " v INTEGER STRICT" |
- ")"; |
- const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)"; |
- const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)"; |
- |
- // Replicate data over. |
- const char kRecoveryCopySql[] = |
- "INSERT OR REPLACE INTO x SELECT id, v FROM recover_x"; |
+ ASSERT_TRUE(recovery->db()->Execute(create_table)); |
+ ASSERT_TRUE(recovery->db()->Execute(create_index)); |
- ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql)); |
- ASSERT_TRUE(recovery->db()->Execute(kCreateTable)); |
- ASSERT_TRUE(recovery->db()->Execute(kCreateIndex)); |
- ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql)); |
+ size_t rows = 0; |
+ ASSERT_TRUE(recovery->AutoRecoverTable("x", &rows)); |
ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); |
} |
@@ -253,8 +270,8 @@ TEST_F(SQLRecoveryTest, RecoverCorruptIndex) { |
ASSERT_TRUE(Reopen()); |
int error = SQLITE_OK; |
- db().set_error_callback(base::Bind(&RecoveryCallback, |
- &db(), db_path(), &error)); |
+ db().set_error_callback(base::Bind(&RecoveryCallback, &db(), db_path(), |
+ kCreateTable, kCreateIndex, &error)); |
// This works before the callback is called. |
const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master"; |
@@ -309,11 +326,12 @@ TEST_F(SQLRecoveryTest, RecoverCorruptTable) { |
const char kDeleteSql[] = "DELETE FROM x WHERE id = 0"; |
ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "x", kDeleteSql)); |
- // TODO(shess): Figure out a query which causes SQLite to notice |
- // this organically. Meanwhile, just handle it manually. |
- |
ASSERT_TRUE(Reopen()); |
+ int error = SQLITE_OK; |
+ db().set_error_callback(base::Bind(&RecoveryCallback, &db(), db_path(), |
+ kCreateTable, kCreateIndex, &error)); |
+ |
// Index shows one less than originally inserted. |
const char kCountSql[] = "SELECT COUNT (*) FROM x"; |
EXPECT_EQ("9", ExecuteWithResults(&db(), kCountSql, "|", ",")); |
@@ -336,10 +354,13 @@ TEST_F(SQLRecoveryTest, RecoverCorruptTable) { |
const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master"; |
EXPECT_TRUE(db().IsSQLValid(kTrivialSql)); |
- // Call the recovery callback manually. |
- int error = SQLITE_OK; |
- RecoveryCallback(&db(), db_path(), &error, SQLITE_CORRUPT, NULL); |
- EXPECT_EQ(SQLITE_CORRUPT, error); |
+ // TODO(shess): Figure out a statement which causes SQLite to notice the |
+ // corruption. SELECT doesn't see errors because missing index values aren't |
+ // visible. UPDATE or DELETE against v=0 don't see errors, even though the |
+ // index item is missing. I suspect SQLite only deletes the key in these |
+ // cases, but doesn't verify that one or more keys were deleted. |
+ ASSERT_FALSE(db().Execute("INSERT INTO x (id, v) VALUES (0, 101)")); |
+ EXPECT_EQ(SQLITE_CONSTRAINT_UNIQUE, error); |
// Database handle has been poisoned. |
EXPECT_FALSE(db().IsSQLValid(kTrivialSql)); |