blob: 66df81935791a1e8da344397255c40306ce0a458 [file] [log] [blame]
[email protected]8d409412013-07-19 18:25:301// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
dchenge48600452015-12-28 02:24:505#include "sql/recovery.h"
avi0b519202015-12-21 07:25:196
dchenge48600452015-12-28 02:24:507#include <stddef.h>
mostynbd82cd9952016-04-11 20:05:348
9#include <memory>
[email protected]ae4f1622013-12-08 06:49:1210#include <string>
dchenge48600452015-12-28 02:24:5011#include <utility>
[email protected]ae4f1622013-12-08 06:49:1212
[email protected]dd325f052013-08-06 02:37:4013#include "base/bind.h"
[email protected]ae4f1622013-12-08 06:49:1214#include "base/files/file_path.h"
thestig22dfc4012014-09-05 08:29:4415#include "base/files/file_util.h"
[email protected]8d409412013-07-19 18:25:3016#include "base/files/scoped_temp_dir.h"
[email protected]cfb821612014-07-10 00:48:0617#include "base/path_service.h"
[email protected]a8848a72013-11-18 04:18:4718#include "base/strings/string_number_conversions.h"
shess63188112016-08-27 10:26:2319#include "base/test/histogram_tester.h"
[email protected]8d409412013-07-19 18:25:3020#include "sql/connection.h"
21#include "sql/meta_table.h"
[email protected]8d409412013-07-19 18:25:3022#include "sql/statement.h"
[email protected]cfb821612014-07-10 00:48:0623#include "sql/test/paths.h"
shess976814402016-06-21 06:56:2524#include "sql/test/scoped_error_expecter.h"
erg102ceb412015-06-20 01:38:1325#include "sql/test/sql_test_base.h"
[email protected]ae4f1622013-12-08 06:49:1226#include "sql/test/test_helpers.h"
[email protected]8d409412013-07-19 18:25:3027#include "testing/gtest/include/gtest/gtest.h"
28#include "third_party/sqlite/sqlite3.h"
29
30namespace {
31
shess1f955b182016-10-25 22:59:0932using sql::test::ExecuteWithResults;
33using sql::test::ExecuteWithResult;
[email protected]8d409412013-07-19 18:25:3034
35// Dump consistent human-readable representation of the database
36// schema. For tables or indices, this will contain the sql command
37// to create the table or index. For certain automatic SQLite
38// structures with no sql, the name is used.
39std::string GetSchema(sql::Connection* db) {
40 const char kSql[] =
41 "SELECT COALESCE(sql, name) FROM sqlite_master ORDER BY 1";
42 return ExecuteWithResults(db, kSql, "|", "\n");
43}
44
erg102ceb412015-06-20 01:38:1345using SQLRecoveryTest = sql::SQLTestBase;
[email protected]8d409412013-07-19 18:25:3046
shess6ac39542016-02-04 20:56:2247// Baseline sql::Recovery test covering the different ways to dispose of the
48// scoped pointer received from sql::Recovery::Begin().
[email protected]8d409412013-07-19 18:25:3049TEST_F(SQLRecoveryTest, RecoverBasic) {
50 const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
51 const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')";
shess6ac39542016-02-04 20:56:2252 const char kAltInsertSql[] = "INSERT INTO x VALUES ('That was a test')";
[email protected]8d409412013-07-19 18:25:3053 ASSERT_TRUE(db().Execute(kCreateSql));
54 ASSERT_TRUE(db().Execute(kInsertSql));
55 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
56
57 // If the Recovery handle goes out of scope without being
58 // Recovered(), the database is razed.
59 {
mostynbd82cd9952016-04-11 20:05:3460 std::unique_ptr<sql::Recovery> recovery =
61 sql::Recovery::Begin(&db(), db_path());
[email protected]8d409412013-07-19 18:25:3062 ASSERT_TRUE(recovery.get());
63 }
64 EXPECT_FALSE(db().is_open());
65 ASSERT_TRUE(Reopen());
66 EXPECT_TRUE(db().is_open());
67 ASSERT_EQ("", GetSchema(&db()));
68
69 // Recreate the database.
70 ASSERT_TRUE(db().Execute(kCreateSql));
71 ASSERT_TRUE(db().Execute(kInsertSql));
72 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
73
74 // Unrecoverable() also razes.
75 {
mostynbd82cd9952016-04-11 20:05:3476 std::unique_ptr<sql::Recovery> recovery =
77 sql::Recovery::Begin(&db(), db_path());
[email protected]8d409412013-07-19 18:25:3078 ASSERT_TRUE(recovery.get());
dchenge48600452015-12-28 02:24:5079 sql::Recovery::Unrecoverable(std::move(recovery));
[email protected]8d409412013-07-19 18:25:3080
81 // TODO(shess): Test that calls to recover.db() start failing.
82 }
83 EXPECT_FALSE(db().is_open());
84 ASSERT_TRUE(Reopen());
85 EXPECT_TRUE(db().is_open());
86 ASSERT_EQ("", GetSchema(&db()));
87
shess874ea1bd2016-02-02 05:15:0688 // Attempting to recover a previously-recovered handle fails early.
89 {
mostynbd82cd9952016-04-11 20:05:3490 std::unique_ptr<sql::Recovery> recovery =
91 sql::Recovery::Begin(&db(), db_path());
shess874ea1bd2016-02-02 05:15:0692 ASSERT_TRUE(recovery.get());
93 recovery.reset();
94
95 recovery = sql::Recovery::Begin(&db(), db_path());
96 ASSERT_FALSE(recovery.get());
97 }
98 ASSERT_TRUE(Reopen());
99
[email protected]8d409412013-07-19 18:25:30100 // Recreate the database.
101 ASSERT_TRUE(db().Execute(kCreateSql));
102 ASSERT_TRUE(db().Execute(kInsertSql));
103 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
104
shess6ac39542016-02-04 20:56:22105 // Unrecovered table to distinguish from recovered database.
106 ASSERT_TRUE(db().Execute("CREATE TABLE y (c INTEGER)"));
107 ASSERT_NE("CREATE TABLE x (t TEXT)", GetSchema(&db()));
108
[email protected]8d409412013-07-19 18:25:30109 // Recovered() replaces the original with the "recovered" version.
110 {
mostynbd82cd9952016-04-11 20:05:34111 std::unique_ptr<sql::Recovery> recovery =
112 sql::Recovery::Begin(&db(), db_path());
[email protected]8d409412013-07-19 18:25:30113 ASSERT_TRUE(recovery.get());
114
115 // Create the new version of the table.
116 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
117
118 // Insert different data to distinguish from original database.
[email protected]8d409412013-07-19 18:25:30119 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql));
120
121 // Successfully recovered.
dchenge48600452015-12-28 02:24:50122 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]8d409412013-07-19 18:25:30123 }
124 EXPECT_FALSE(db().is_open());
125 ASSERT_TRUE(Reopen());
126 EXPECT_TRUE(db().is_open());
127 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
128
129 const char* kXSql = "SELECT * FROM x ORDER BY 1";
shess1f955b182016-10-25 22:59:09130 ASSERT_EQ("That was a test", ExecuteWithResult(&db(), kXSql));
shess6ac39542016-02-04 20:56:22131
132 // Reset the database contents.
133 ASSERT_TRUE(db().Execute("DELETE FROM x"));
134 ASSERT_TRUE(db().Execute(kInsertSql));
135
136 // Rollback() discards recovery progress and leaves the database as it was.
137 {
mostynbd82cd9952016-04-11 20:05:34138 std::unique_ptr<sql::Recovery> recovery =
139 sql::Recovery::Begin(&db(), db_path());
shess6ac39542016-02-04 20:56:22140 ASSERT_TRUE(recovery.get());
141
142 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
143 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql));
144
145 sql::Recovery::Rollback(std::move(recovery));
146 }
147 EXPECT_FALSE(db().is_open());
148 ASSERT_TRUE(Reopen());
149 EXPECT_TRUE(db().is_open());
150 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
151
shess1f955b182016-10-25 22:59:09152 ASSERT_EQ("This is a test", ExecuteWithResult(&db(), kXSql));
[email protected]8d409412013-07-19 18:25:30153}
154
shess6ac39542016-02-04 20:56:22155// Test operation of the virtual table used by sql::Recovery.
[email protected]dd325f052013-08-06 02:37:40156TEST_F(SQLRecoveryTest, VirtualTable) {
157 const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
158 ASSERT_TRUE(db().Execute(kCreateSql));
159 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')"));
160 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test')"));
161
162 // Successfully recover the database.
163 {
mostynbd82cd9952016-04-11 20:05:34164 std::unique_ptr<sql::Recovery> recovery =
165 sql::Recovery::Begin(&db(), db_path());
[email protected]dd325f052013-08-06 02:37:40166
167 // Tables to recover original DB, now at [corrupt].
168 const char kRecoveryCreateSql[] =
169 "CREATE VIRTUAL TABLE temp.recover_x using recover("
170 " corrupt.x,"
171 " t TEXT STRICT"
172 ")";
173 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql));
174
175 // Re-create the original schema.
176 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
177
178 // Copy the data from the recovery tables to the new database.
179 const char kRecoveryCopySql[] =
180 "INSERT INTO x SELECT t FROM recover_x";
181 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql));
182
183 // Successfully recovered.
dchenge48600452015-12-28 02:24:50184 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]dd325f052013-08-06 02:37:40185 }
186
187 // Since the database was not corrupt, the entire schema and all
188 // data should be recovered.
189 ASSERT_TRUE(Reopen());
190 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
191
192 const char* kXSql = "SELECT * FROM x ORDER BY 1";
193 ASSERT_EQ("That was a test\nThis is a test",
194 ExecuteWithResults(&db(), kXSql, "|", "\n"));
195}
196
197void RecoveryCallback(sql::Connection* db, const base::FilePath& db_path,
shess6ac39542016-02-04 20:56:22198 const char* create_table, const char* create_index,
[email protected]dd325f052013-08-06 02:37:40199 int* record_error, int error, sql::Statement* stmt) {
200 *record_error = error;
201
202 // Clear the error callback to prevent reentrancy.
203 db->reset_error_callback();
204
mostynbd82cd9952016-04-11 20:05:34205 std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path);
[email protected]dd325f052013-08-06 02:37:40206 ASSERT_TRUE(recovery.get());
207
shess6ac39542016-02-04 20:56:22208 ASSERT_TRUE(recovery->db()->Execute(create_table));
209 ASSERT_TRUE(recovery->db()->Execute(create_index));
[email protected]dd325f052013-08-06 02:37:40210
shess6ac39542016-02-04 20:56:22211 size_t rows = 0;
212 ASSERT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]dd325f052013-08-06 02:37:40213
dchenge48600452015-12-28 02:24:50214 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]dd325f052013-08-06 02:37:40215}
216
217// Build a database, corrupt it by making an index reference to
218// deleted row, then recover when a query selects that row.
219TEST_F(SQLRecoveryTest, RecoverCorruptIndex) {
220 const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)";
221 const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)";
222 ASSERT_TRUE(db().Execute(kCreateTable));
223 ASSERT_TRUE(db().Execute(kCreateIndex));
224
225 // Insert a bit of data.
226 {
227 ASSERT_TRUE(db().BeginTransaction());
228
229 const char kInsertSql[] = "INSERT INTO x (id, v) VALUES (?, ?)";
230 sql::Statement s(db().GetUniqueStatement(kInsertSql));
231 for (int i = 0; i < 10; ++i) {
232 s.Reset(true);
233 s.BindInt(0, i);
234 s.BindInt(1, i);
235 EXPECT_FALSE(s.Step());
236 EXPECT_TRUE(s.Succeeded());
237 }
238
239 ASSERT_TRUE(db().CommitTransaction());
240 }
[email protected]dd325f052013-08-06 02:37:40241 db().Close();
242
[email protected]ae4f1622013-12-08 06:49:12243 // Delete a row from the table, while leaving the index entry which
244 // references it.
245 const char kDeleteSql[] = "DELETE FROM x WHERE id = 0";
246 ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "x_id", kDeleteSql));
[email protected]dd325f052013-08-06 02:37:40247
248 ASSERT_TRUE(Reopen());
249
250 int error = SQLITE_OK;
tzikd16d2192018-03-07 08:58:36251 db().set_error_callback(base::BindRepeating(
252 &RecoveryCallback, &db(), db_path(), kCreateTable, kCreateIndex, &error));
[email protected]dd325f052013-08-06 02:37:40253
254 // This works before the callback is called.
255 const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master";
256 EXPECT_TRUE(db().IsSQLValid(kTrivialSql));
257
258 // TODO(shess): Could this be delete? Anything which fails should work.
259 const char kSelectSql[] = "SELECT v FROM x WHERE id = 0";
260 ASSERT_FALSE(db().Execute(kSelectSql));
261 EXPECT_EQ(SQLITE_CORRUPT, error);
262
263 // Database handle has been poisoned.
264 EXPECT_FALSE(db().IsSQLValid(kTrivialSql));
265
266 ASSERT_TRUE(Reopen());
267
268 // The recovered table should reflect the deletion.
269 const char kSelectAllSql[] = "SELECT v FROM x ORDER BY id";
270 EXPECT_EQ("1,2,3,4,5,6,7,8,9",
271 ExecuteWithResults(&db(), kSelectAllSql, "|", ","));
272
273 // The failing statement should now succeed, with no results.
274 EXPECT_EQ("", ExecuteWithResults(&db(), kSelectSql, "|", ","));
275}
276
277// Build a database, corrupt it by making a table contain a row not
278// referenced by the index, then recover the database.
279TEST_F(SQLRecoveryTest, RecoverCorruptTable) {
280 const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)";
281 const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)";
282 ASSERT_TRUE(db().Execute(kCreateTable));
283 ASSERT_TRUE(db().Execute(kCreateIndex));
284
285 // Insert a bit of data.
286 {
287 ASSERT_TRUE(db().BeginTransaction());
288
289 const char kInsertSql[] = "INSERT INTO x (id, v) VALUES (?, ?)";
290 sql::Statement s(db().GetUniqueStatement(kInsertSql));
291 for (int i = 0; i < 10; ++i) {
292 s.Reset(true);
293 s.BindInt(0, i);
294 s.BindInt(1, i);
295 EXPECT_FALSE(s.Step());
296 EXPECT_TRUE(s.Succeeded());
297 }
298
299 ASSERT_TRUE(db().CommitTransaction());
300 }
[email protected]dd325f052013-08-06 02:37:40301 db().Close();
302
[email protected]ae4f1622013-12-08 06:49:12303 // Delete a row from the index while leaving a table entry.
304 const char kDeleteSql[] = "DELETE FROM x WHERE id = 0";
305 ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "x", kDeleteSql));
[email protected]dd325f052013-08-06 02:37:40306
[email protected]dd325f052013-08-06 02:37:40307 ASSERT_TRUE(Reopen());
308
shess6ac39542016-02-04 20:56:22309 int error = SQLITE_OK;
tzikd16d2192018-03-07 08:58:36310 db().set_error_callback(base::BindRepeating(
311 &RecoveryCallback, &db(), db_path(), kCreateTable, kCreateIndex, &error));
shess6ac39542016-02-04 20:56:22312
[email protected]dd325f052013-08-06 02:37:40313 // Index shows one less than originally inserted.
314 const char kCountSql[] = "SELECT COUNT (*) FROM x";
shess1f955b182016-10-25 22:59:09315 EXPECT_EQ("9", ExecuteWithResult(&db(), kCountSql));
[email protected]dd325f052013-08-06 02:37:40316
Scott Hessdcf120482015-02-10 21:33:29317 // A full table scan shows all of the original data. Using column [v] to
318 // force use of the table rather than the index.
319 const char kDistinctSql[] = "SELECT DISTINCT COUNT (v) FROM x";
shess1f955b182016-10-25 22:59:09320 EXPECT_EQ("10", ExecuteWithResult(&db(), kDistinctSql));
[email protected]dd325f052013-08-06 02:37:40321
322 // Insert id 0 again. Since it is not in the index, the insert
323 // succeeds, but results in a duplicate value in the table.
324 const char kInsertSql[] = "INSERT INTO x (id, v) VALUES (0, 100)";
325 ASSERT_TRUE(db().Execute(kInsertSql));
326
327 // Duplication is visible.
shess1f955b182016-10-25 22:59:09328 EXPECT_EQ("10", ExecuteWithResult(&db(), kCountSql));
329 EXPECT_EQ("11", ExecuteWithResult(&db(), kDistinctSql));
[email protected]dd325f052013-08-06 02:37:40330
331 // This works before the callback is called.
332 const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master";
333 EXPECT_TRUE(db().IsSQLValid(kTrivialSql));
334
shess6ac39542016-02-04 20:56:22335 // TODO(shess): Figure out a statement which causes SQLite to notice the
336 // corruption. SELECT doesn't see errors because missing index values aren't
337 // visible. UPDATE or DELETE against v=0 don't see errors, even though the
338 // index item is missing. I suspect SQLite only deletes the key in these
339 // cases, but doesn't verify that one or more keys were deleted.
340 ASSERT_FALSE(db().Execute("INSERT INTO x (id, v) VALUES (0, 101)"));
341 EXPECT_EQ(SQLITE_CONSTRAINT_UNIQUE, error);
[email protected]dd325f052013-08-06 02:37:40342
343 // Database handle has been poisoned.
344 EXPECT_FALSE(db().IsSQLValid(kTrivialSql));
345
346 ASSERT_TRUE(Reopen());
347
348 // The recovered table has consistency between the index and the table.
shess1f955b182016-10-25 22:59:09349 EXPECT_EQ("10", ExecuteWithResult(&db(), kCountSql));
350 EXPECT_EQ("10", ExecuteWithResult(&db(), kDistinctSql));
[email protected]dd325f052013-08-06 02:37:40351
shess806f4992016-02-04 21:12:09352 // Only one of the values is retained.
[email protected]dd325f052013-08-06 02:37:40353 const char kSelectSql[] = "SELECT v FROM x WHERE id = 0";
shess1f955b182016-10-25 22:59:09354 const std::string results = ExecuteWithResult(&db(), kSelectSql);
shess806f4992016-02-04 21:12:09355 EXPECT_TRUE(results=="100" || results=="0") << "Actual results: " << results;
[email protected]dd325f052013-08-06 02:37:40356}
[email protected]a8848a72013-11-18 04:18:47357
358TEST_F(SQLRecoveryTest, Meta) {
359 const int kVersion = 3;
360 const int kCompatibleVersion = 2;
361
362 {
363 sql::MetaTable meta;
364 EXPECT_TRUE(meta.Init(&db(), kVersion, kCompatibleVersion));
365 EXPECT_EQ(kVersion, meta.GetVersionNumber());
366 }
367
368 // Test expected case where everything works.
369 {
mostynbd82cd9952016-04-11 20:05:34370 std::unique_ptr<sql::Recovery> recovery =
371 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47372 EXPECT_TRUE(recovery->SetupMeta());
373 int version = 0;
374 EXPECT_TRUE(recovery->GetMetaVersionNumber(&version));
375 EXPECT_EQ(kVersion, version);
376
dchenge48600452015-12-28 02:24:50377 sql::Recovery::Rollback(std::move(recovery));
[email protected]a8848a72013-11-18 04:18:47378 }
379 ASSERT_TRUE(Reopen()); // Handle was poisoned.
380
381 // Test version row missing.
382 EXPECT_TRUE(db().Execute("DELETE FROM meta WHERE key = 'version'"));
383 {
mostynbd82cd9952016-04-11 20:05:34384 std::unique_ptr<sql::Recovery> recovery =
385 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47386 EXPECT_TRUE(recovery->SetupMeta());
387 int version = 0;
388 EXPECT_FALSE(recovery->GetMetaVersionNumber(&version));
389 EXPECT_EQ(0, version);
390
dchenge48600452015-12-28 02:24:50391 sql::Recovery::Rollback(std::move(recovery));
[email protected]a8848a72013-11-18 04:18:47392 }
393 ASSERT_TRUE(Reopen()); // Handle was poisoned.
394
395 // Test meta table missing.
396 EXPECT_TRUE(db().Execute("DROP TABLE meta"));
397 {
shess976814402016-06-21 06:56:25398 sql::test::ScopedErrorExpecter expecter;
399 expecter.ExpectError(SQLITE_CORRUPT); // From virtual table.
mostynbd82cd9952016-04-11 20:05:34400 std::unique_ptr<sql::Recovery> recovery =
401 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47402 EXPECT_FALSE(recovery->SetupMeta());
shess976814402016-06-21 06:56:25403 ASSERT_TRUE(expecter.SawExpectedErrors());
[email protected]a8848a72013-11-18 04:18:47404 }
405}
406
407// Baseline AutoRecoverTable() test.
408TEST_F(SQLRecoveryTest, AutoRecoverTable) {
409 // BIGINT and VARCHAR to test type affinity.
410 const char kCreateSql[] = "CREATE TABLE x (id BIGINT, t TEXT, v VARCHAR)";
411 ASSERT_TRUE(db().Execute(kCreateSql));
412 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (11, 'This is', 'a test')"));
413 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5, 'That was', 'a test')"));
414
415 // Save aside a copy of the original schema and data.
416 const std::string orig_schema(GetSchema(&db()));
417 const char kXSql[] = "SELECT * FROM x ORDER BY 1";
418 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n"));
419
420 // Create a lame-duck table which will not be propagated by recovery to
421 // detect that the recovery code actually ran.
422 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
423 ASSERT_NE(orig_schema, GetSchema(&db()));
424
425 {
mostynbd82cd9952016-04-11 20:05:34426 std::unique_ptr<sql::Recovery> recovery =
427 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47428 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
429
430 // Save a copy of the temp db's schema before recovering the table.
431 const char kTempSchemaSql[] = "SELECT name, sql FROM sqlite_temp_master";
432 const std::string temp_schema(
433 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n"));
434
435 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44436 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]a8848a72013-11-18 04:18:47437 EXPECT_EQ(2u, rows);
438
439 // Test that any additional temp tables were cleaned up.
440 EXPECT_EQ(temp_schema,
441 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n"));
442
dchenge48600452015-12-28 02:24:50443 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]a8848a72013-11-18 04:18:47444 }
445
446 // Since the database was not corrupt, the entire schema and all
447 // data should be recovered.
448 ASSERT_TRUE(Reopen());
449 ASSERT_EQ(orig_schema, GetSchema(&db()));
450 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
451
452 // Recovery fails if the target table doesn't exist.
453 {
mostynbd82cd9952016-04-11 20:05:34454 std::unique_ptr<sql::Recovery> recovery =
455 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47456 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
457
458 // TODO(shess): Should this failure implicitly lead to Raze()?
459 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44460 EXPECT_FALSE(recovery->AutoRecoverTable("y", &rows));
[email protected]a8848a72013-11-18 04:18:47461
dchenge48600452015-12-28 02:24:50462 sql::Recovery::Unrecoverable(std::move(recovery));
[email protected]a8848a72013-11-18 04:18:47463 }
464}
465
466// Test that default values correctly replace nulls. The recovery
467// virtual table reads directly from the database, so DEFAULT is not
468// interpretted at that level.
469TEST_F(SQLRecoveryTest, AutoRecoverTableWithDefault) {
470 ASSERT_TRUE(db().Execute("CREATE TABLE x (id INTEGER)"));
471 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5)"));
472 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (15)"));
473
474 // ALTER effectively leaves the new columns NULL in the first two
475 // rows. The row with 17 will get the default injected at insert
476 // time, while the row with 42 will get the actual value provided.
477 // Embedded "'" to make sure default-handling continues to be quoted
478 // correctly.
479 ASSERT_TRUE(db().Execute("ALTER TABLE x ADD COLUMN t TEXT DEFAULT 'a''a'"));
480 ASSERT_TRUE(db().Execute("ALTER TABLE x ADD COLUMN b BLOB DEFAULT x'AA55'"));
481 ASSERT_TRUE(db().Execute("ALTER TABLE x ADD COLUMN i INT DEFAULT 93"));
482 ASSERT_TRUE(db().Execute("INSERT INTO x (id) VALUES (17)"));
483 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (42, 'b', x'1234', 12)"));
484
485 // Save aside a copy of the original schema and data.
486 const std::string orig_schema(GetSchema(&db()));
487 const char kXSql[] = "SELECT * FROM x ORDER BY 1";
488 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n"));
489
490 // Create a lame-duck table which will not be propagated by recovery to
491 // detect that the recovery code actually ran.
492 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
493 ASSERT_NE(orig_schema, GetSchema(&db()));
494
495 // Mechanically adjust the stored schema and data to allow detecting
496 // where the default value is coming from. The target table is just
497 // like the original with the default for [t] changed, to signal
498 // defaults coming from the recovery system. The two %5 rows should
499 // get the target-table default for [t], while the others should get
500 // the source-table default.
501 std::string final_schema(orig_schema);
502 std::string final_data(orig_data);
503 size_t pos;
504 while ((pos = final_schema.find("'a''a'")) != std::string::npos) {
505 final_schema.replace(pos, 6, "'c''c'");
506 }
507 while ((pos = final_data.find("5|a'a")) != std::string::npos) {
508 final_data.replace(pos, 5, "5|c'c");
509 }
510
511 {
mostynbd82cd9952016-04-11 20:05:34512 std::unique_ptr<sql::Recovery> recovery =
513 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47514 // Different default to detect which table provides the default.
515 ASSERT_TRUE(recovery->db()->Execute(final_schema.c_str()));
516
517 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44518 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]a8848a72013-11-18 04:18:47519 EXPECT_EQ(4u, rows);
520
dchenge48600452015-12-28 02:24:50521 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]a8848a72013-11-18 04:18:47522 }
523
524 // Since the database was not corrupt, the entire schema and all
525 // data should be recovered.
526 ASSERT_TRUE(Reopen());
527 ASSERT_EQ(final_schema, GetSchema(&db()));
528 ASSERT_EQ(final_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
529}
530
531// Test that rows with NULL in a NOT NULL column are filtered
532// correctly. In the wild, this would probably happen due to
533// corruption, but here it is simulated by recovering a table which
534// allowed nulls into a table which does not.
535TEST_F(SQLRecoveryTest, AutoRecoverTableNullFilter) {
536 const char kOrigSchema[] = "CREATE TABLE x (id INTEGER, t TEXT)";
537 const char kFinalSchema[] = "CREATE TABLE x (id INTEGER, t TEXT NOT NULL)";
538
539 ASSERT_TRUE(db().Execute(kOrigSchema));
540 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5, null)"));
541 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (15, 'this is a test')"));
542
543 // Create a lame-duck table which will not be propagated by recovery to
544 // detect that the recovery code actually ran.
545 ASSERT_EQ(kOrigSchema, GetSchema(&db()));
546 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
547 ASSERT_NE(kOrigSchema, GetSchema(&db()));
548
549 {
mostynbd82cd9952016-04-11 20:05:34550 std::unique_ptr<sql::Recovery> recovery =
551 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47552 ASSERT_TRUE(recovery->db()->Execute(kFinalSchema));
553
554 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44555 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]a8848a72013-11-18 04:18:47556 EXPECT_EQ(1u, rows);
557
dchenge48600452015-12-28 02:24:50558 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]a8848a72013-11-18 04:18:47559 }
560
561 // The schema should be the same, but only one row of data should
562 // have been recovered.
563 ASSERT_TRUE(Reopen());
564 ASSERT_EQ(kFinalSchema, GetSchema(&db()));
565 const char kXSql[] = "SELECT * FROM x ORDER BY 1";
566 ASSERT_EQ("15|this is a test", ExecuteWithResults(&db(), kXSql, "|", "\n"));
567}
568
569// Test AutoRecoverTable with a ROWID alias.
570TEST_F(SQLRecoveryTest, AutoRecoverTableWithRowid) {
571 // The rowid alias is almost always the first column, intentionally
572 // put it later.
573 const char kCreateSql[] =
574 "CREATE TABLE x (t TEXT, id INTEGER PRIMARY KEY NOT NULL)";
575 ASSERT_TRUE(db().Execute(kCreateSql));
576 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test', null)"));
577 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test', null)"));
578
579 // Save aside a copy of the original schema and data.
580 const std::string orig_schema(GetSchema(&db()));
581 const char kXSql[] = "SELECT * FROM x ORDER BY 1";
582 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n"));
583
584 // Create a lame-duck table which will not be propagated by recovery to
585 // detect that the recovery code actually ran.
586 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
587 ASSERT_NE(orig_schema, GetSchema(&db()));
588
589 {
mostynbd82cd9952016-04-11 20:05:34590 std::unique_ptr<sql::Recovery> recovery =
591 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47592 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
593
594 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44595 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]a8848a72013-11-18 04:18:47596 EXPECT_EQ(2u, rows);
597
dchenge48600452015-12-28 02:24:50598 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]a8848a72013-11-18 04:18:47599 }
600
601 // Since the database was not corrupt, the entire schema and all
602 // data should be recovered.
603 ASSERT_TRUE(Reopen());
604 ASSERT_EQ(orig_schema, GetSchema(&db()));
605 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
606}
607
608// Test that a compound primary key doesn't fire the ROWID code.
609TEST_F(SQLRecoveryTest, AutoRecoverTableWithCompoundKey) {
610 const char kCreateSql[] =
611 "CREATE TABLE x ("
612 "id INTEGER NOT NULL,"
613 "id2 TEXT NOT NULL,"
614 "t TEXT,"
615 "PRIMARY KEY (id, id2)"
616 ")";
617 ASSERT_TRUE(db().Execute(kCreateSql));
618
619 // NOTE(shess): Do not accidentally use [id] 1, 2, 3, as those will
620 // be the ROWID values.
621 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (1, 'a', 'This is a test')"));
622 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (1, 'b', 'That was a test')"));
623 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (2, 'a', 'Another test')"));
624
625 // Save aside a copy of the original schema and data.
626 const std::string orig_schema(GetSchema(&db()));
627 const char kXSql[] = "SELECT * FROM x ORDER BY 1";
628 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n"));
629
630 // Create a lame-duck table which will not be propagated by recovery to
631 // detect that the recovery code actually ran.
632 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
633 ASSERT_NE(orig_schema, GetSchema(&db()));
634
635 {
mostynbd82cd9952016-04-11 20:05:34636 std::unique_ptr<sql::Recovery> recovery =
637 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47638 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
639
640 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44641 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]a8848a72013-11-18 04:18:47642 EXPECT_EQ(3u, rows);
643
dchenge48600452015-12-28 02:24:50644 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]a8848a72013-11-18 04:18:47645 }
646
647 // Since the database was not corrupt, the entire schema and all
648 // data should be recovered.
649 ASSERT_TRUE(Reopen());
650 ASSERT_EQ(orig_schema, GetSchema(&db()));
651 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
652}
653
shess6f68bd32016-02-04 19:29:44654// Test recovering from a table with fewer columns than the target.
655TEST_F(SQLRecoveryTest, AutoRecoverTableMissingColumns) {
[email protected]a8848a72013-11-18 04:18:47656 const char kCreateSql[] = "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)";
shess6f68bd32016-02-04 19:29:44657 const char kAlterSql[] = "ALTER TABLE x ADD COLUMN t1 TEXT DEFAULT 't'";
[email protected]a8848a72013-11-18 04:18:47658 ASSERT_TRUE(db().Execute(kCreateSql));
659 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (1, 'This is')"));
660 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (2, 'That was')"));
661
shess6f68bd32016-02-04 19:29:44662 // Generate the expected info by faking a table to match what recovery will
663 // create.
[email protected]a8848a72013-11-18 04:18:47664 const std::string orig_schema(GetSchema(&db()));
665 const char kXSql[] = "SELECT * FROM x ORDER BY 1";
shess6f68bd32016-02-04 19:29:44666 std::string expected_schema;
667 std::string expected_data;
668 {
669 ASSERT_TRUE(db().BeginTransaction());
670 ASSERT_TRUE(db().Execute(kAlterSql));
[email protected]a8848a72013-11-18 04:18:47671
shess6f68bd32016-02-04 19:29:44672 expected_schema = GetSchema(&db());
673 expected_data = ExecuteWithResults(&db(), kXSql, "|", "\n");
[email protected]a8848a72013-11-18 04:18:47674
shess6f68bd32016-02-04 19:29:44675 db().RollbackTransaction();
676 }
677
678 // Following tests are pointless if the rollback didn't work.
679 ASSERT_EQ(orig_schema, GetSchema(&db()));
680
681 // Recover the previous version of the table into the altered version.
[email protected]a8848a72013-11-18 04:18:47682 {
mostynbd82cd9952016-04-11 20:05:34683 std::unique_ptr<sql::Recovery> recovery =
684 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47685 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
shess6f68bd32016-02-04 19:29:44686 ASSERT_TRUE(recovery->db()->Execute(kAlterSql));
[email protected]a8848a72013-11-18 04:18:47687 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44688 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]a8848a72013-11-18 04:18:47689 EXPECT_EQ(2u, rows);
dchenge48600452015-12-28 02:24:50690 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]a8848a72013-11-18 04:18:47691 }
692
693 // Since the database was not corrupt, the entire schema and all
694 // data should be recovered.
695 ASSERT_TRUE(Reopen());
shess6f68bd32016-02-04 19:29:44696 ASSERT_EQ(expected_schema, GetSchema(&db()));
697 ASSERT_EQ(expected_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
[email protected]a8848a72013-11-18 04:18:47698}
[email protected]cfb821612014-07-10 00:48:06699
700// Recover a golden file where an interior page has been manually modified so
701// that the number of cells is greater than will fit on a single page. This
702// case happened in <http://crbug.com/387868>.
703TEST_F(SQLRecoveryTest, Bug387868) {
704 base::FilePath golden_path;
705 ASSERT_TRUE(PathService::Get(sql::test::DIR_TEST_DATA, &golden_path));
706 golden_path = golden_path.AppendASCII("recovery_387868");
707 db().Close();
708 ASSERT_TRUE(base::CopyFile(golden_path, db_path()));
709 ASSERT_TRUE(Reopen());
710
711 {
mostynbd82cd9952016-04-11 20:05:34712 std::unique_ptr<sql::Recovery> recovery =
713 sql::Recovery::Begin(&db(), db_path());
[email protected]cfb821612014-07-10 00:48:06714 ASSERT_TRUE(recovery.get());
715
716 // Create the new version of the table.
717 const char kCreateSql[] =
718 "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)";
719 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
720
721 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44722 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]cfb821612014-07-10 00:48:06723 EXPECT_EQ(43u, rows);
724
725 // Successfully recovered.
dchenge48600452015-12-28 02:24:50726 EXPECT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]cfb821612014-07-10 00:48:06727 }
728}
[email protected]dd325f052013-08-06 02:37:40729
shess2f3a814b2015-11-05 18:11:10730// Memory-mapped I/O interacts poorly with I/O errors. Make sure the recovery
731// database doesn't accidentally enable it.
732TEST_F(SQLRecoveryTest, NoMmap) {
mostynbd82cd9952016-04-11 20:05:34733 std::unique_ptr<sql::Recovery> recovery =
734 sql::Recovery::Begin(&db(), db_path());
shess2f3a814b2015-11-05 18:11:10735 ASSERT_TRUE(recovery.get());
736
737 // In the current implementation, the PRAGMA successfully runs with no result
738 // rows. Running with a single result of |0| is also acceptable.
739 sql::Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size"));
740 EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0));
741}
742
shessa402e752016-07-02 00:25:11743TEST_F(SQLRecoveryTest, RecoverDatabase) {
744 // As a side effect, AUTOINCREMENT creates the sqlite_sequence table for
745 // RecoverDatabase() to handle.
746 ASSERT_TRUE(db().Execute(
747 "CREATE TABLE x (id INTEGER PRIMARY KEY AUTOINCREMENT, v TEXT)"));
748 EXPECT_TRUE(db().Execute("INSERT INTO x (v) VALUES ('turtle')"));
749 EXPECT_TRUE(db().Execute("INSERT INTO x (v) VALUES ('truck')"));
750 EXPECT_TRUE(db().Execute("INSERT INTO x (v) VALUES ('trailer')"));
751
752 // This table needs index and a unique index to work.
753 ASSERT_TRUE(db().Execute("CREATE TABLE y (name TEXT, v TEXT)"));
754 ASSERT_TRUE(db().Execute("CREATE UNIQUE INDEX y_name ON y(name)"));
755 ASSERT_TRUE(db().Execute("CREATE INDEX y_v ON y(v)"));
756 EXPECT_TRUE(db().Execute("INSERT INTO y VALUES ('jim', 'telephone')"));
757 EXPECT_TRUE(db().Execute("INSERT INTO y VALUES ('bob', 'truck')"));
758 EXPECT_TRUE(db().Execute("INSERT INTO y VALUES ('dean', 'trailer')"));
759
760 // View which is the intersection of [x.v] and [y.v].
761 ASSERT_TRUE(db().Execute(
762 "CREATE VIEW v AS SELECT x.v FROM x, y WHERE x.v = y.v"));
763
764 // When an element is deleted from [x], trigger a delete on [y]. Between the
765 // BEGIN and END, [old] stands for the deleted rows from [x].
766 ASSERT_TRUE(db().Execute("CREATE TRIGGER t AFTER DELETE ON x "
767 "BEGIN DELETE FROM y WHERE y.v = old.v; END"));
768
769 // Save aside a copy of the original schema, verifying that it has the created
770 // items plus the sqlite_sequence table.
771 const std::string orig_schema(GetSchema(&db()));
772 ASSERT_EQ(6, std::count(orig_schema.begin(), orig_schema.end(), '\n'));
773
774 const char kXSql[] = "SELECT * FROM x ORDER BY 1";
775 const char kYSql[] = "SELECT * FROM y ORDER BY 1";
776 const char kVSql[] = "SELECT * FROM v ORDER BY 1";
777 EXPECT_EQ("1|turtle\n2|truck\n3|trailer",
778 ExecuteWithResults(&db(), kXSql, "|", "\n"));
779 EXPECT_EQ("bob|truck\ndean|trailer\njim|telephone",
780 ExecuteWithResults(&db(), kYSql, "|", "\n"));
781 EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db(), kVSql, "|", "\n"));
782
783 // Database handle is valid before recovery, poisoned after.
784 const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master";
785 EXPECT_TRUE(db().IsSQLValid(kTrivialSql));
786 sql::Recovery::RecoverDatabase(&db(), db_path());
787 EXPECT_FALSE(db().IsSQLValid(kTrivialSql));
788
789 // Since the database was not corrupt, the entire schema and all
790 // data should be recovered.
791 ASSERT_TRUE(Reopen());
792 ASSERT_EQ(orig_schema, GetSchema(&db()));
793 EXPECT_EQ("1|turtle\n2|truck\n3|trailer",
794 ExecuteWithResults(&db(), kXSql, "|", "\n"));
795 EXPECT_EQ("bob|truck\ndean|trailer\njim|telephone",
796 ExecuteWithResults(&db(), kYSql, "|", "\n"));
797 EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db(), kVSql, "|", "\n"));
798
799 // Test that the trigger works.
800 ASSERT_TRUE(db().Execute("DELETE FROM x WHERE v = 'truck'"));
801 EXPECT_EQ("1|turtle\n3|trailer",
802 ExecuteWithResults(&db(), kXSql, "|", "\n"));
803 EXPECT_EQ("dean|trailer\njim|telephone",
804 ExecuteWithResults(&db(), kYSql, "|", "\n"));
805 EXPECT_EQ("trailer", ExecuteWithResults(&db(), kVSql, "|", "\n"));
806}
807
shess00d65d42017-03-02 21:12:19808// When RecoverDatabase() encounters SQLITE_NOTADB, the database is deleted.
809TEST_F(SQLRecoveryTest, RecoverDatabaseDelete) {
810 // Create a valid database, then write junk over the header. This should lead
811 // to SQLITE_NOTADB, which will cause ATTACH to fail.
812 ASSERT_TRUE(db().Execute("CREATE TABLE x (t TEXT)"));
813 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')"));
814 db().Close();
815 WriteJunkToDatabase(SQLTestBase::TYPE_OVERWRITE);
816
817 {
818 sql::test::ScopedErrorExpecter expecter;
819 expecter.ExpectError(SQLITE_NOTADB);
820
821 // Reopen() here because it will see SQLITE_NOTADB.
822 ASSERT_TRUE(Reopen());
823
824 // This should "recover" the database by making it valid, but empty.
825 sql::Recovery::RecoverDatabase(&db(), db_path());
826
827 ASSERT_TRUE(expecter.SawExpectedErrors());
828 }
829
830 // Recovery poisoned the handle, must re-open.
831 db().Close();
832 ASSERT_TRUE(Reopen());
833
834 EXPECT_EQ("", GetSchema(&db()));
835}
836
shess5207e1452017-04-12 23:55:33837// Allow callers to validate the database between recovery and commit.
838TEST_F(SQLRecoveryTest, BeginRecoverDatabase) {
839 // Create a table with a broken index.
840 ASSERT_TRUE(db().Execute("CREATE TABLE t (id INTEGER PRIMARY KEY, c TEXT)"));
841 ASSERT_TRUE(db().Execute("CREATE UNIQUE INDEX t_id ON t (id)"));
842 ASSERT_TRUE(db().Execute("INSERT INTO t VALUES (1, 'hello world')"));
843 ASSERT_TRUE(db().Execute("INSERT INTO t VALUES (2, 'testing')"));
844 ASSERT_TRUE(db().Execute("INSERT INTO t VALUES (3, 'nope')"));
845
846 // Inject corruption into the index.
847 db().Close();
848 const char kDeleteSql[] = "DELETE FROM t WHERE id = 3";
849 ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "t_id", kDeleteSql));
850 ASSERT_TRUE(Reopen());
851
852 // id as read from index.
853 const char kSelectIndexIdSql[] = "SELECT id FROM t INDEXED BY t_id";
854 EXPECT_EQ("1,2,3", ExecuteWithResults(&db(), kSelectIndexIdSql, "|", ","));
855
856 // id as read from table.
857 const char kSelectTableIdSql[] = "SELECT id FROM t NOT INDEXED";
858 EXPECT_EQ("1,2", ExecuteWithResults(&db(), kSelectTableIdSql, "|", ","));
859
860 // Run recovery code, then rollback. Database remains the same.
861 {
862 std::unique_ptr<sql::Recovery> recovery =
863 sql::Recovery::BeginRecoverDatabase(&db(), db_path());
864 ASSERT_TRUE(recovery);
865 sql::Recovery::Rollback(std::move(recovery));
866 }
867 db().Close();
868 ASSERT_TRUE(Reopen());
869 EXPECT_EQ("1,2,3", ExecuteWithResults(&db(), kSelectIndexIdSql, "|", ","));
870 EXPECT_EQ("1,2", ExecuteWithResults(&db(), kSelectTableIdSql, "|", ","));
871
872 // Run recovery code, then commit. The failing row is dropped.
873 {
874 std::unique_ptr<sql::Recovery> recovery =
875 sql::Recovery::BeginRecoverDatabase(&db(), db_path());
876 ASSERT_TRUE(recovery);
877 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
878 }
879 db().Close();
880 ASSERT_TRUE(Reopen());
881 EXPECT_EQ("1,2", ExecuteWithResults(&db(), kSelectIndexIdSql, "|", ","));
882 EXPECT_EQ("1,2", ExecuteWithResults(&db(), kSelectTableIdSql, "|", ","));
883}
884
shess63188112016-08-27 10:26:23885// Test histograms recorded when the invalid database cannot be attached.
886TEST_F(SQLRecoveryTest, AttachFailure) {
887 // Create a valid database, then write junk over the header. This should lead
888 // to SQLITE_NOTADB, which will cause ATTACH to fail.
889 ASSERT_TRUE(db().Execute("CREATE TABLE x (t TEXT)"));
890 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')"));
891 db().Close();
892 WriteJunkToDatabase(SQLTestBase::TYPE_OVERWRITE);
893
894 const char kEventHistogramName[] = "Sqlite.RecoveryEvents";
895 const int kEventEnum = 5; // RECOVERY_FAILED_ATTACH
896 const char kErrorHistogramName[] = "Sqlite.RecoveryAttachError";
897 base::HistogramTester tester;
898
899 {
900 sql::test::ScopedErrorExpecter expecter;
901 expecter.ExpectError(SQLITE_NOTADB);
902
903 // Reopen() here because it will see SQLITE_NOTADB.
904 ASSERT_TRUE(Reopen());
905
906 // Begin() should fail.
907 std::unique_ptr<sql::Recovery>
908 recovery = sql::Recovery::Begin(&db(), db_path());
909 ASSERT_FALSE(recovery.get());
910
911 ASSERT_TRUE(expecter.SawExpectedErrors());
912 }
913
914 // Verify that the failure was in the right place with the expected code.
915 tester.ExpectBucketCount(kEventHistogramName, kEventEnum, 1);
916 tester.ExpectBucketCount(kErrorHistogramName, SQLITE_NOTADB, 1);
917}
918
shess0b8d5932016-10-27 19:54:12919// Helper for SQLRecoveryTest.PageSize. Creates a fresh db based on db_prefix,
920// with the given initial page size, and verifies it against the expected size.
921// Then changes to the final page size and recovers, verifying that the
922// recovered database ends up with the expected final page size.
923void TestPageSize(const base::FilePath& db_prefix,
924 int initial_page_size,
925 const std::string& expected_initial_page_size,
926 int final_page_size,
927 const std::string& expected_final_page_size) {
928 const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
929 const char kInsertSql1[] = "INSERT INTO x VALUES ('This is a test')";
930 const char kInsertSql2[] = "INSERT INTO x VALUES ('That was a test')";
931 const char kSelectSql[] = "SELECT * FROM x ORDER BY t";
932
933 const base::FilePath db_path = db_prefix.InsertBeforeExtensionASCII(
934 base::IntToString(initial_page_size));
935 sql::Connection::Delete(db_path);
936 sql::Connection db;
937 db.set_page_size(initial_page_size);
938 ASSERT_TRUE(db.Open(db_path));
939 ASSERT_TRUE(db.Execute(kCreateSql));
940 ASSERT_TRUE(db.Execute(kInsertSql1));
941 ASSERT_TRUE(db.Execute(kInsertSql2));
942 ASSERT_EQ(expected_initial_page_size,
943 ExecuteWithResult(&db, "PRAGMA page_size"));
944
945 // Recovery will use the page size set in the connection object, which may not
946 // match the file's page size.
947 db.set_page_size(final_page_size);
948 sql::Recovery::RecoverDatabase(&db, db_path);
949
950 // Recovery poisoned the handle, must re-open.
951 db.Close();
952
953 // Make sure the page size is read from the file.
954 db.set_page_size(0);
955 ASSERT_TRUE(db.Open(db_path));
956 ASSERT_EQ(expected_final_page_size,
957 ExecuteWithResult(&db, "PRAGMA page_size"));
958 EXPECT_EQ("That was a test\nThis is a test",
959 ExecuteWithResults(&db, kSelectSql, "|", "\n"));
960}
961
962// Verify that sql::Recovery maintains the page size, and the virtual table
963// works with page sizes other than SQLite's default. Also verify the case
964// where the default page size has changed.
965TEST_F(SQLRecoveryTest, PageSize) {
966 const std::string default_page_size =
967 ExecuteWithResult(&db(), "PRAGMA page_size");
968
969 // The database should have the default page size after recovery.
970 EXPECT_NO_FATAL_FAILURE(
971 TestPageSize(db_path(), 0, default_page_size, 0, default_page_size));
972
973 // Sync user 32k pages.
974 EXPECT_NO_FATAL_FAILURE(
975 TestPageSize(db_path(), 32768, "32768", 32768, "32768"));
976
977 // Many clients use 4k pages. This is the SQLite default after 3.12.0.
978 EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path(), 4096, "4096", 4096, "4096"));
979
980 // 1k is the default page size before 3.12.0.
981 EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path(), 1024, "1024", 1024, "1024"));
982
983 // Databases with no page size specified should recover with the new default
984 // page size. 2k has never been the default page size.
985 ASSERT_NE("2048", default_page_size);
986 EXPECT_NO_FATAL_FAILURE(
987 TestPageSize(db_path(), 2048, "2048", 0, default_page_size));
988}
989
[email protected]8d409412013-07-19 18:25:30990} // namespace