Index: sql/recovery_unittest.cc |
diff --git a/sql/recovery_unittest.cc b/sql/recovery_unittest.cc |
index b215777560dd55674a828204359d087cfeec8a4f..bbd65e36923246563c01eab7dab655167a482d81 100644 |
--- a/sql/recovery_unittest.cc |
+++ b/sql/recovery_unittest.cc |
@@ -767,4 +767,69 @@ TEST_F(SQLRecoveryTest, NoMmap) { |
EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0)); |
} |
+TEST_F(SQLRecoveryTest, RecoverDatabaseOrRaze) { |
+ // As a side effect AUTOINCREMENT creates the sqlite_sequence table for |
Mark P
2016/04/19 23:34:28
optional nit: comma after effect
Scott Hess - ex-Googler
2016/05/13 21:24:36
Done.
|
+ // RecoverDatabaseOrRaze() to handle. |
+ ASSERT_TRUE(db().Execute( |
+ "CREATE TABLE x (id INTEGER PRIMARY KEY AUTOINCREMENT, v TEXT)")); |
+ EXPECT_TRUE(db().Execute("INSERT INTO x (v) VALUES ('turtle')")); |
+ EXPECT_TRUE(db().Execute("INSERT INTO x (v) VALUES ('truck')")); |
+ EXPECT_TRUE(db().Execute("INSERT INTO x (v) VALUES ('trailer')")); |
+ |
+ // This table needs index and a unique index to work. |
+ ASSERT_TRUE(db().Execute("CREATE TABLE y (name TEXT, v TEXT)")); |
+ ASSERT_TRUE(db().Execute("CREATE UNIQUE INDEX y_name ON y(name)")); |
+ ASSERT_TRUE(db().Execute("CREATE INDEX y_v ON y(v)")); |
+ EXPECT_TRUE(db().Execute("INSERT INTO y VALUES ('jim', 'telephone')")); |
+ EXPECT_TRUE(db().Execute("INSERT INTO y VALUES ('bob', 'truck')")); |
+ EXPECT_TRUE(db().Execute("INSERT INTO y VALUES ('dean', 'trailer')")); |
+ |
+ // View which is the intersection of [x.v] and [y.v]. |
+ ASSERT_TRUE(db().Execute( |
+ "CREATE VIEW v AS SELECT x.v FROM x, y WHERE x.v = y.v")); |
+ |
+ // When an element is deleted from [x], trigger a delete on [y]. Between the |
+ // BEGIN and END, [old] stands for the deleted rows from [x]. |
+ ASSERT_TRUE(db().Execute("CREATE TRIGGER t AFTER DELETE ON x " |
+ "BEGIN DELETE FROM y WHERE y.v = old.v; END")); |
+ |
+ // Save aside a copy of the original schema, verifying that it has the created |
+ // items plus the sqlite_sequence table. |
+ const std::string orig_schema(GetSchema(&db())); |
+ ASSERT_EQ(6U, std::count(orig_schema.begin(), orig_schema.end(), '\n')); |
+ |
+ const char kXSql[] = "SELECT * FROM x ORDER BY 1"; |
+ const char kYSql[] = "SELECT * FROM y ORDER BY 1"; |
+ const char kVSql[] = "SELECT * FROM v ORDER BY 1"; |
+ EXPECT_EQ("1|turtle\n2|truck\n3|trailer", |
+ ExecuteWithResults(&db(), kXSql, "|", "\n")); |
+ EXPECT_EQ("bob|truck\ndean|trailer\njim|telephone", |
+ ExecuteWithResults(&db(), kYSql, "|", "\n")); |
+ EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db(), kVSql, "|", "\n")); |
+ |
+ // Database handle is valid before recovery, poisoned after. |
+ const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master"; |
+ EXPECT_TRUE(db().IsSQLValid(kTrivialSql)); |
+ sql::Recovery::RecoverDatabaseOrRaze(&db(), db_path()); |
+ EXPECT_FALSE(db().IsSQLValid(kTrivialSql)); |
+ |
+ // Since the database was not corrupt, the entire schema and all |
+ // data should be recovered. |
+ ASSERT_TRUE(Reopen()); |
+ ASSERT_EQ(orig_schema, GetSchema(&db())); |
+ EXPECT_EQ("1|turtle\n2|truck\n3|trailer", |
+ ExecuteWithResults(&db(), kXSql, "|", "\n")); |
+ EXPECT_EQ("bob|truck\ndean|trailer\njim|telephone", |
+ ExecuteWithResults(&db(), kYSql, "|", "\n")); |
+ EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db(), kVSql, "|", "\n")); |
+ |
+ // Test that the trigger works. |
+ ASSERT_TRUE(db().Execute("DELETE FROM x WHERE v = 'truck'")); |
+ EXPECT_EQ("1|turtle\n3|trailer", |
+ ExecuteWithResults(&db(), kXSql, "|", "\n")); |
+ EXPECT_EQ("dean|trailer\njim|telephone", |
+ ExecuteWithResults(&db(), kYSql, "|", "\n")); |
+ EXPECT_EQ("trailer", ExecuteWithResults(&db(), kVSql, "|", "\n")); |
+} |
+ |
} // namespace |