Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1072)

Side by Side Diff: sql/recovery_unittest.cc

Issue 1669453003: [sql] Clean up sql::Recovery unit test. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@zzsql_recover_noextended_review
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
60 // to create the table or index. For certain automatic SQLite 60 // to create the table or index. For certain automatic SQLite
61 // structures with no sql, the name is used. 61 // structures with no sql, the name is used.
62 std::string GetSchema(sql::Connection* db) { 62 std::string GetSchema(sql::Connection* db) {
63 const char kSql[] = 63 const char kSql[] =
64 "SELECT COALESCE(sql, name) FROM sqlite_master ORDER BY 1"; 64 "SELECT COALESCE(sql, name) FROM sqlite_master ORDER BY 1";
65 return ExecuteWithResults(db, kSql, "|", "\n"); 65 return ExecuteWithResults(db, kSql, "|", "\n");
66 } 66 }
67 67
68 using SQLRecoveryTest = sql::SQLTestBase; 68 using SQLRecoveryTest = sql::SQLTestBase;
69 69
70 // Baseline sql::Recovery test covering the different ways to dispose of the
71 // scoped pointer received from sql::Recovery::Begin().
70 TEST_F(SQLRecoveryTest, RecoverBasic) { 72 TEST_F(SQLRecoveryTest, RecoverBasic) {
71 const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; 73 const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
72 const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')"; 74 const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')";
75 const char kAltInsertSql[] = "INSERT INTO x VALUES ('That was a test')";
73 ASSERT_TRUE(db().Execute(kCreateSql)); 76 ASSERT_TRUE(db().Execute(kCreateSql));
74 ASSERT_TRUE(db().Execute(kInsertSql)); 77 ASSERT_TRUE(db().Execute(kInsertSql));
75 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); 78 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
76 79
77 // If the Recovery handle goes out of scope without being 80 // If the Recovery handle goes out of scope without being
78 // Recovered(), the database is razed. 81 // Recovered(), the database is razed.
79 { 82 {
80 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); 83 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path());
81 ASSERT_TRUE(recovery.get()); 84 ASSERT_TRUE(recovery.get());
82 } 85 }
(...skipping 29 matching lines...) Expand all
112 recovery = sql::Recovery::Begin(&db(), db_path()); 115 recovery = sql::Recovery::Begin(&db(), db_path());
113 ASSERT_FALSE(recovery.get()); 116 ASSERT_FALSE(recovery.get());
114 } 117 }
115 ASSERT_TRUE(Reopen()); 118 ASSERT_TRUE(Reopen());
116 119
117 // Recreate the database. 120 // Recreate the database.
118 ASSERT_TRUE(db().Execute(kCreateSql)); 121 ASSERT_TRUE(db().Execute(kCreateSql));
119 ASSERT_TRUE(db().Execute(kInsertSql)); 122 ASSERT_TRUE(db().Execute(kInsertSql));
120 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); 123 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
121 124
125 // Unrecovered table to distinguish from recovered database.
126 ASSERT_TRUE(db().Execute("CREATE TABLE y (c INTEGER)"));
127 ASSERT_NE("CREATE TABLE x (t TEXT)", GetSchema(&db()));
128
122 // Recovered() replaces the original with the "recovered" version. 129 // Recovered() replaces the original with the "recovered" version.
123 { 130 {
124 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); 131 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path());
125 ASSERT_TRUE(recovery.get()); 132 ASSERT_TRUE(recovery.get());
126 133
127 // Create the new version of the table. 134 // Create the new version of the table.
128 ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); 135 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
129 136
130 // Insert different data to distinguish from original database. 137 // Insert different data to distinguish from original database.
131 const char kAltInsertSql[] = "INSERT INTO x VALUES ('That was a test')";
132 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); 138 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql));
133 139
134 // Successfully recovered. 140 // Successfully recovered.
135 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); 141 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
136 } 142 }
137 EXPECT_FALSE(db().is_open()); 143 EXPECT_FALSE(db().is_open());
138 ASSERT_TRUE(Reopen()); 144 ASSERT_TRUE(Reopen());
139 EXPECT_TRUE(db().is_open()); 145 EXPECT_TRUE(db().is_open());
140 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); 146 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
141 147
142 const char* kXSql = "SELECT * FROM x ORDER BY 1"; 148 const char* kXSql = "SELECT * FROM x ORDER BY 1";
143 ASSERT_EQ("That was a test", 149 ASSERT_EQ("That was a test",
144 ExecuteWithResults(&db(), kXSql, "|", "\n")); 150 ExecuteWithResults(&db(), kXSql, "|", "\n"));
151
152 // Reset the database contents.
153 ASSERT_TRUE(db().Execute("DELETE FROM x"));
154 ASSERT_TRUE(db().Execute(kInsertSql));
155
156 // Rollback() discards recovery progress and leaves the database as it was.
157 {
158 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path());
159 ASSERT_TRUE(recovery.get());
160
161 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
162 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql));
163
164 sql::Recovery::Rollback(std::move(recovery));
165 }
166 EXPECT_FALSE(db().is_open());
167 ASSERT_TRUE(Reopen());
168 EXPECT_TRUE(db().is_open());
169 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
170
171 ASSERT_EQ("This is a test",
172 ExecuteWithResults(&db(), kXSql, "|", "\n"));
145 } 173 }
146 174
147 // The recovery virtual table is only supported for Chromium's SQLite. 175 // The recovery virtual table is only supported for Chromium's SQLite.
148 #if !defined(USE_SYSTEM_SQLITE) 176 #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
149 177
150 // Run recovery through its paces on a valid database. 178 // Test operation of the virtual table used by sql::Recovery.
151 TEST_F(SQLRecoveryTest, VirtualTable) { 179 TEST_F(SQLRecoveryTest, VirtualTable) {
152 const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; 180 const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
153 ASSERT_TRUE(db().Execute(kCreateSql)); 181 ASSERT_TRUE(db().Execute(kCreateSql));
154 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')")); 182 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')"));
155 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test')")); 183 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test')"));
156 184
157 // Successfully recover the database. 185 // Successfully recover the database.
158 { 186 {
159 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); 187 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path());
160 188
(...skipping 21 matching lines...) Expand all
182 // data should be recovered. 210 // data should be recovered.
183 ASSERT_TRUE(Reopen()); 211 ASSERT_TRUE(Reopen());
184 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); 212 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
185 213
186 const char* kXSql = "SELECT * FROM x ORDER BY 1"; 214 const char* kXSql = "SELECT * FROM x ORDER BY 1";
187 ASSERT_EQ("That was a test\nThis is a test", 215 ASSERT_EQ("That was a test\nThis is a test",
188 ExecuteWithResults(&db(), kXSql, "|", "\n")); 216 ExecuteWithResults(&db(), kXSql, "|", "\n"));
189 } 217 }
190 218
191 void RecoveryCallback(sql::Connection* db, const base::FilePath& db_path, 219 void RecoveryCallback(sql::Connection* db, const base::FilePath& db_path,
220 const char* create_table, const char* create_index,
192 int* record_error, int error, sql::Statement* stmt) { 221 int* record_error, int error, sql::Statement* stmt) {
193 *record_error = error; 222 *record_error = error;
194 223
195 // Clear the error callback to prevent reentrancy. 224 // Clear the error callback to prevent reentrancy.
196 db->reset_error_callback(); 225 db->reset_error_callback();
197 226
198 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); 227 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path);
199 ASSERT_TRUE(recovery.get()); 228 ASSERT_TRUE(recovery.get());
200 229
201 const char kRecoveryCreateSql[] = 230 ASSERT_TRUE(recovery->db()->Execute(create_table));
202 "CREATE VIRTUAL TABLE temp.recover_x using recover(" 231 ASSERT_TRUE(recovery->db()->Execute(create_index));
203 " corrupt.x,"
204 " id INTEGER STRICT,"
205 " v INTEGER STRICT"
206 ")";
207 const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)";
208 const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)";
209 232
210 // Replicate data over. 233 size_t rows = 0;
211 const char kRecoveryCopySql[] = 234 ASSERT_TRUE(recovery->AutoRecoverTable("x", &rows));
212 "INSERT OR REPLACE INTO x SELECT id, v FROM recover_x";
213
214 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql));
215 ASSERT_TRUE(recovery->db()->Execute(kCreateTable));
216 ASSERT_TRUE(recovery->db()->Execute(kCreateIndex));
217 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql));
218 235
219 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); 236 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
220 } 237 }
221 238
222 // Build a database, corrupt it by making an index reference to 239 // Build a database, corrupt it by making an index reference to
223 // deleted row, then recover when a query selects that row. 240 // deleted row, then recover when a query selects that row.
224 TEST_F(SQLRecoveryTest, RecoverCorruptIndex) { 241 TEST_F(SQLRecoveryTest, RecoverCorruptIndex) {
225 const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)"; 242 const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)";
226 const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)"; 243 const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)";
227 ASSERT_TRUE(db().Execute(kCreateTable)); 244 ASSERT_TRUE(db().Execute(kCreateTable));
(...skipping 18 matching lines...) Expand all
246 db().Close(); 263 db().Close();
247 264
248 // Delete a row from the table, while leaving the index entry which 265 // Delete a row from the table, while leaving the index entry which
249 // references it. 266 // references it.
250 const char kDeleteSql[] = "DELETE FROM x WHERE id = 0"; 267 const char kDeleteSql[] = "DELETE FROM x WHERE id = 0";
251 ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "x_id", kDeleteSql)); 268 ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "x_id", kDeleteSql));
252 269
253 ASSERT_TRUE(Reopen()); 270 ASSERT_TRUE(Reopen());
254 271
255 int error = SQLITE_OK; 272 int error = SQLITE_OK;
256 db().set_error_callback(base::Bind(&RecoveryCallback, 273 db().set_error_callback(base::Bind(&RecoveryCallback, &db(), db_path(),
257 &db(), db_path(), &error)); 274 kCreateTable, kCreateIndex, &error));
258 275
259 // This works before the callback is called. 276 // This works before the callback is called.
260 const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master"; 277 const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master";
261 EXPECT_TRUE(db().IsSQLValid(kTrivialSql)); 278 EXPECT_TRUE(db().IsSQLValid(kTrivialSql));
262 279
263 // TODO(shess): Could this be delete? Anything which fails should work. 280 // TODO(shess): Could this be delete? Anything which fails should work.
264 const char kSelectSql[] = "SELECT v FROM x WHERE id = 0"; 281 const char kSelectSql[] = "SELECT v FROM x WHERE id = 0";
265 ASSERT_FALSE(db().Execute(kSelectSql)); 282 ASSERT_FALSE(db().Execute(kSelectSql));
266 EXPECT_EQ(SQLITE_CORRUPT, error); 283 EXPECT_EQ(SQLITE_CORRUPT, error);
267 284
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
302 } 319 }
303 320
304 ASSERT_TRUE(db().CommitTransaction()); 321 ASSERT_TRUE(db().CommitTransaction());
305 } 322 }
306 db().Close(); 323 db().Close();
307 324
308 // Delete a row from the index while leaving a table entry. 325 // Delete a row from the index while leaving a table entry.
309 const char kDeleteSql[] = "DELETE FROM x WHERE id = 0"; 326 const char kDeleteSql[] = "DELETE FROM x WHERE id = 0";
310 ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "x", kDeleteSql)); 327 ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "x", kDeleteSql));
311 328
312 // TODO(shess): Figure out a query which causes SQLite to notice 329 ASSERT_TRUE(Reopen());
313 // this organically. Meanwhile, just handle it manually.
314 330
315 ASSERT_TRUE(Reopen()); 331 int error = SQLITE_OK;
332 db().set_error_callback(base::Bind(&RecoveryCallback, &db(), db_path(),
333 kCreateTable, kCreateIndex, &error));
316 334
317 // Index shows one less than originally inserted. 335 // Index shows one less than originally inserted.
318 const char kCountSql[] = "SELECT COUNT (*) FROM x"; 336 const char kCountSql[] = "SELECT COUNT (*) FROM x";
319 EXPECT_EQ("9", ExecuteWithResults(&db(), kCountSql, "|", ",")); 337 EXPECT_EQ("9", ExecuteWithResults(&db(), kCountSql, "|", ","));
320 338
321 // A full table scan shows all of the original data. Using column [v] to 339 // A full table scan shows all of the original data. Using column [v] to
322 // force use of the table rather than the index. 340 // force use of the table rather than the index.
323 const char kDistinctSql[] = "SELECT DISTINCT COUNT (v) FROM x"; 341 const char kDistinctSql[] = "SELECT DISTINCT COUNT (v) FROM x";
324 EXPECT_EQ("10", ExecuteWithResults(&db(), kDistinctSql, "|", ",")); 342 EXPECT_EQ("10", ExecuteWithResults(&db(), kDistinctSql, "|", ","));
325 343
326 // Insert id 0 again. Since it is not in the index, the insert 344 // Insert id 0 again. Since it is not in the index, the insert
327 // succeeds, but results in a duplicate value in the table. 345 // succeeds, but results in a duplicate value in the table.
328 const char kInsertSql[] = "INSERT INTO x (id, v) VALUES (0, 100)"; 346 const char kInsertSql[] = "INSERT INTO x (id, v) VALUES (0, 100)";
329 ASSERT_TRUE(db().Execute(kInsertSql)); 347 ASSERT_TRUE(db().Execute(kInsertSql));
330 348
331 // Duplication is visible. 349 // Duplication is visible.
332 EXPECT_EQ("10", ExecuteWithResults(&db(), kCountSql, "|", ",")); 350 EXPECT_EQ("10", ExecuteWithResults(&db(), kCountSql, "|", ","));
333 EXPECT_EQ("11", ExecuteWithResults(&db(), kDistinctSql, "|", ",")); 351 EXPECT_EQ("11", ExecuteWithResults(&db(), kDistinctSql, "|", ","));
334 352
335 // This works before the callback is called. 353 // This works before the callback is called.
336 const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master"; 354 const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master";
337 EXPECT_TRUE(db().IsSQLValid(kTrivialSql)); 355 EXPECT_TRUE(db().IsSQLValid(kTrivialSql));
338 356
339 // Call the recovery callback manually. 357 // TODO(shess): Figure out a statement which causes SQLite to notice the
340 int error = SQLITE_OK; 358 // corruption. SELECT doesn't see errors because missing index values aren't
341 RecoveryCallback(&db(), db_path(), &error, SQLITE_CORRUPT, NULL); 359 // visible. UPDATE or DELETE against v=0 don't see errors, even though the
342 EXPECT_EQ(SQLITE_CORRUPT, error); 360 // index item is missing. I suspect SQLite only deletes the key in these
361 // cases, but doesn't verify that one or more keys were deleted.
362 ASSERT_FALSE(db().Execute("INSERT INTO x (id, v) VALUES (0, 101)"));
363 EXPECT_EQ(SQLITE_CONSTRAINT_UNIQUE, error);
343 364
344 // Database handle has been poisoned. 365 // Database handle has been poisoned.
345 EXPECT_FALSE(db().IsSQLValid(kTrivialSql)); 366 EXPECT_FALSE(db().IsSQLValid(kTrivialSql));
346 367
347 ASSERT_TRUE(Reopen()); 368 ASSERT_TRUE(Reopen());
348 369
349 // The recovered table has consistency between the index and the table. 370 // The recovered table has consistency between the index and the table.
350 EXPECT_EQ("10", ExecuteWithResults(&db(), kCountSql, "|", ",")); 371 EXPECT_EQ("10", ExecuteWithResults(&db(), kCountSql, "|", ","));
351 EXPECT_EQ("10", ExecuteWithResults(&db(), kDistinctSql, "|", ",")); 372 EXPECT_EQ("10", ExecuteWithResults(&db(), kDistinctSql, "|", ","));
352 373
(...skipping 370 matching lines...) Expand 10 before | Expand all | Expand 10 after
723 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); 744 scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path());
724 ASSERT_TRUE(recovery.get()); 745 ASSERT_TRUE(recovery.get());
725 746
726 // In the current implementation, the PRAGMA successfully runs with no result 747 // In the current implementation, the PRAGMA successfully runs with no result
727 // rows. Running with a single result of |0| is also acceptable. 748 // rows. Running with a single result of |0| is also acceptable.
728 sql::Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size")); 749 sql::Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size"));
729 EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0)); 750 EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0));
730 } 751 }
731 752
732 } // namespace 753 } // namespace
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698