blob: afd386574f3619370fb876083ac2448e510116dd [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"
Devlin Cronin147687f2018-06-05 18:03:5619#include "base/test/metrics/histogram_tester.h"
Victor Costancfbfa602018-08-01 23:24:4620#include "sql/database.h"
[email protected]8d409412013-07-19 18:25:3021#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::ExecuteWithResult;
Shubham Aggarwal7b60fe6e2020-10-15 06:00:2833using sql::test::ExecuteWithResults;
[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.
Victor Costancfbfa602018-08-01 23:24:4639std::string GetSchema(sql::Database* db) {
Victor Costan1d868352018-06-26 19:06:4840 static const char kSql[] =
[email protected]8d409412013-07-19 18:25:3041 "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) {
Victor Costan1d868352018-06-26 19:06:4850 static const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
51 static const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')";
52 static const char kAltInsertSql[] =
53 "INSERT INTO x VALUES ('That was a test')";
[email protected]8d409412013-07-19 18:25:3054 ASSERT_TRUE(db().Execute(kCreateSql));
55 ASSERT_TRUE(db().Execute(kInsertSql));
56 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
57
58 // If the Recovery handle goes out of scope without being
59 // Recovered(), the database is razed.
60 {
mostynbd82cd9952016-04-11 20:05:3461 std::unique_ptr<sql::Recovery> recovery =
62 sql::Recovery::Begin(&db(), db_path());
[email protected]8d409412013-07-19 18:25:3063 ASSERT_TRUE(recovery.get());
64 }
65 EXPECT_FALSE(db().is_open());
66 ASSERT_TRUE(Reopen());
67 EXPECT_TRUE(db().is_open());
68 ASSERT_EQ("", GetSchema(&db()));
69
70 // Recreate the database.
71 ASSERT_TRUE(db().Execute(kCreateSql));
72 ASSERT_TRUE(db().Execute(kInsertSql));
73 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
74
75 // Unrecoverable() also razes.
76 {
mostynbd82cd9952016-04-11 20:05:3477 std::unique_ptr<sql::Recovery> recovery =
78 sql::Recovery::Begin(&db(), db_path());
[email protected]8d409412013-07-19 18:25:3079 ASSERT_TRUE(recovery.get());
dchenge48600452015-12-28 02:24:5080 sql::Recovery::Unrecoverable(std::move(recovery));
[email protected]8d409412013-07-19 18:25:3081
82 // TODO(shess): Test that calls to recover.db() start failing.
83 }
84 EXPECT_FALSE(db().is_open());
85 ASSERT_TRUE(Reopen());
86 EXPECT_TRUE(db().is_open());
87 ASSERT_EQ("", GetSchema(&db()));
88
shess874ea1bd2016-02-02 05:15:0689 // Attempting to recover a previously-recovered handle fails early.
90 {
mostynbd82cd9952016-04-11 20:05:3491 std::unique_ptr<sql::Recovery> recovery =
92 sql::Recovery::Begin(&db(), db_path());
shess874ea1bd2016-02-02 05:15:0693 ASSERT_TRUE(recovery.get());
94 recovery.reset();
95
96 recovery = sql::Recovery::Begin(&db(), db_path());
97 ASSERT_FALSE(recovery.get());
98 }
99 ASSERT_TRUE(Reopen());
100
[email protected]8d409412013-07-19 18:25:30101 // Recreate the database.
102 ASSERT_TRUE(db().Execute(kCreateSql));
103 ASSERT_TRUE(db().Execute(kInsertSql));
104 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
105
shess6ac39542016-02-04 20:56:22106 // Unrecovered table to distinguish from recovered database.
107 ASSERT_TRUE(db().Execute("CREATE TABLE y (c INTEGER)"));
108 ASSERT_NE("CREATE TABLE x (t TEXT)", GetSchema(&db()));
109
[email protected]8d409412013-07-19 18:25:30110 // Recovered() replaces the original with the "recovered" version.
111 {
mostynbd82cd9952016-04-11 20:05:34112 std::unique_ptr<sql::Recovery> recovery =
113 sql::Recovery::Begin(&db(), db_path());
[email protected]8d409412013-07-19 18:25:30114 ASSERT_TRUE(recovery.get());
115
116 // Create the new version of the table.
117 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
118
119 // Insert different data to distinguish from original database.
[email protected]8d409412013-07-19 18:25:30120 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql));
121
122 // Successfully recovered.
dchenge48600452015-12-28 02:24:50123 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]8d409412013-07-19 18:25:30124 }
125 EXPECT_FALSE(db().is_open());
126 ASSERT_TRUE(Reopen());
127 EXPECT_TRUE(db().is_open());
128 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
129
130 const char* kXSql = "SELECT * FROM x ORDER BY 1";
shess1f955b182016-10-25 22:59:09131 ASSERT_EQ("That was a test", ExecuteWithResult(&db(), kXSql));
shess6ac39542016-02-04 20:56:22132
133 // Reset the database contents.
134 ASSERT_TRUE(db().Execute("DELETE FROM x"));
135 ASSERT_TRUE(db().Execute(kInsertSql));
136
137 // Rollback() discards recovery progress and leaves the database as it was.
138 {
mostynbd82cd9952016-04-11 20:05:34139 std::unique_ptr<sql::Recovery> recovery =
140 sql::Recovery::Begin(&db(), db_path());
shess6ac39542016-02-04 20:56:22141 ASSERT_TRUE(recovery.get());
142
143 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
144 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql));
145
146 sql::Recovery::Rollback(std::move(recovery));
147 }
148 EXPECT_FALSE(db().is_open());
149 ASSERT_TRUE(Reopen());
150 EXPECT_TRUE(db().is_open());
151 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
152
shess1f955b182016-10-25 22:59:09153 ASSERT_EQ("This is a test", ExecuteWithResult(&db(), kXSql));
[email protected]8d409412013-07-19 18:25:30154}
155
shess6ac39542016-02-04 20:56:22156// Test operation of the virtual table used by sql::Recovery.
[email protected]dd325f052013-08-06 02:37:40157TEST_F(SQLRecoveryTest, VirtualTable) {
Victor Costan1d868352018-06-26 19:06:48158 static const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
[email protected]dd325f052013-08-06 02:37:40159 ASSERT_TRUE(db().Execute(kCreateSql));
160 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')"));
161 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test')"));
162
163 // Successfully recover the database.
164 {
mostynbd82cd9952016-04-11 20:05:34165 std::unique_ptr<sql::Recovery> recovery =
166 sql::Recovery::Begin(&db(), db_path());
[email protected]dd325f052013-08-06 02:37:40167
168 // Tables to recover original DB, now at [corrupt].
Victor Costan1d868352018-06-26 19:06:48169 static const char kRecoveryCreateSql[] =
[email protected]dd325f052013-08-06 02:37:40170 "CREATE VIRTUAL TABLE temp.recover_x using recover("
171 " corrupt.x,"
172 " t TEXT STRICT"
173 ")";
174 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql));
175
176 // Re-create the original schema.
177 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
178
179 // Copy the data from the recovery tables to the new database.
Victor Costan1d868352018-06-26 19:06:48180 static const char kRecoveryCopySql[] =
[email protected]dd325f052013-08-06 02:37:40181 "INSERT INTO x SELECT t FROM recover_x";
182 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql));
183
184 // Successfully recovered.
dchenge48600452015-12-28 02:24:50185 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]dd325f052013-08-06 02:37:40186 }
187
188 // Since the database was not corrupt, the entire schema and all
189 // data should be recovered.
190 ASSERT_TRUE(Reopen());
191 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db()));
192
Victor Costan1d868352018-06-26 19:06:48193 static const char* kXSql = "SELECT * FROM x ORDER BY 1";
[email protected]dd325f052013-08-06 02:37:40194 ASSERT_EQ("That was a test\nThis is a test",
195 ExecuteWithResults(&db(), kXSql, "|", "\n"));
196}
197
Victor Costancfbfa602018-08-01 23:24:46198void RecoveryCallback(sql::Database* db,
199 const base::FilePath& db_path,
200 const char* create_table,
201 const char* create_index,
202 int* record_error,
203 int error,
204 sql::Statement* stmt) {
[email protected]dd325f052013-08-06 02:37:40205 *record_error = error;
206
207 // Clear the error callback to prevent reentrancy.
208 db->reset_error_callback();
209
mostynbd82cd9952016-04-11 20:05:34210 std::unique_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path);
[email protected]dd325f052013-08-06 02:37:40211 ASSERT_TRUE(recovery.get());
212
shess6ac39542016-02-04 20:56:22213 ASSERT_TRUE(recovery->db()->Execute(create_table));
214 ASSERT_TRUE(recovery->db()->Execute(create_index));
[email protected]dd325f052013-08-06 02:37:40215
shess6ac39542016-02-04 20:56:22216 size_t rows = 0;
217 ASSERT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]dd325f052013-08-06 02:37:40218
dchenge48600452015-12-28 02:24:50219 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]dd325f052013-08-06 02:37:40220}
221
222// Build a database, corrupt it by making an index reference to
223// deleted row, then recover when a query selects that row.
224TEST_F(SQLRecoveryTest, RecoverCorruptIndex) {
Victor Costan1d868352018-06-26 19:06:48225 static const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)";
226 static const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)";
[email protected]dd325f052013-08-06 02:37:40227 ASSERT_TRUE(db().Execute(kCreateTable));
228 ASSERT_TRUE(db().Execute(kCreateIndex));
229
230 // Insert a bit of data.
231 {
232 ASSERT_TRUE(db().BeginTransaction());
233
Victor Costan1d868352018-06-26 19:06:48234 static const char kInsertSql[] = "INSERT INTO x (id, v) VALUES (?, ?)";
[email protected]dd325f052013-08-06 02:37:40235 sql::Statement s(db().GetUniqueStatement(kInsertSql));
236 for (int i = 0; i < 10; ++i) {
237 s.Reset(true);
238 s.BindInt(0, i);
239 s.BindInt(1, i);
240 EXPECT_FALSE(s.Step());
241 EXPECT_TRUE(s.Succeeded());
242 }
243
244 ASSERT_TRUE(db().CommitTransaction());
245 }
[email protected]dd325f052013-08-06 02:37:40246 db().Close();
247
[email protected]ae4f1622013-12-08 06:49:12248 // Delete a row from the table, while leaving the index entry which
249 // references it.
Victor Costan1d868352018-06-26 19:06:48250 static const char kDeleteSql[] = "DELETE FROM x WHERE id = 0";
[email protected]ae4f1622013-12-08 06:49:12251 ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "x_id", kDeleteSql));
[email protected]dd325f052013-08-06 02:37:40252
253 ASSERT_TRUE(Reopen());
254
255 int error = SQLITE_OK;
tzikd16d2192018-03-07 08:58:36256 db().set_error_callback(base::BindRepeating(
257 &RecoveryCallback, &db(), db_path(), kCreateTable, kCreateIndex, &error));
[email protected]dd325f052013-08-06 02:37:40258
259 // This works before the callback is called.
Victor Costan1d868352018-06-26 19:06:48260 static const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master";
[email protected]dd325f052013-08-06 02:37:40261 EXPECT_TRUE(db().IsSQLValid(kTrivialSql));
262
263 // TODO(shess): Could this be delete? Anything which fails should work.
Victor Costan1d868352018-06-26 19:06:48264 static const char kSelectSql[] = "SELECT v FROM x WHERE id = 0";
[email protected]dd325f052013-08-06 02:37:40265 ASSERT_FALSE(db().Execute(kSelectSql));
266 EXPECT_EQ(SQLITE_CORRUPT, error);
267
268 // Database handle has been poisoned.
269 EXPECT_FALSE(db().IsSQLValid(kTrivialSql));
270
271 ASSERT_TRUE(Reopen());
272
273 // The recovered table should reflect the deletion.
Victor Costan1d868352018-06-26 19:06:48274 static const char kSelectAllSql[] = "SELECT v FROM x ORDER BY id";
[email protected]dd325f052013-08-06 02:37:40275 EXPECT_EQ("1,2,3,4,5,6,7,8,9",
276 ExecuteWithResults(&db(), kSelectAllSql, "|", ","));
277
278 // The failing statement should now succeed, with no results.
279 EXPECT_EQ("", ExecuteWithResults(&db(), kSelectSql, "|", ","));
280}
281
282// Build a database, corrupt it by making a table contain a row not
283// referenced by the index, then recover the database.
284TEST_F(SQLRecoveryTest, RecoverCorruptTable) {
Victor Costan1d868352018-06-26 19:06:48285 static const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)";
286 static const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)";
[email protected]dd325f052013-08-06 02:37:40287 ASSERT_TRUE(db().Execute(kCreateTable));
288 ASSERT_TRUE(db().Execute(kCreateIndex));
289
290 // Insert a bit of data.
291 {
292 ASSERT_TRUE(db().BeginTransaction());
293
Victor Costan1d868352018-06-26 19:06:48294 static const char kInsertSql[] = "INSERT INTO x (id, v) VALUES (?, ?)";
[email protected]dd325f052013-08-06 02:37:40295 sql::Statement s(db().GetUniqueStatement(kInsertSql));
296 for (int i = 0; i < 10; ++i) {
297 s.Reset(true);
298 s.BindInt(0, i);
299 s.BindInt(1, i);
300 EXPECT_FALSE(s.Step());
301 EXPECT_TRUE(s.Succeeded());
302 }
303
304 ASSERT_TRUE(db().CommitTransaction());
305 }
[email protected]dd325f052013-08-06 02:37:40306 db().Close();
307
[email protected]ae4f1622013-12-08 06:49:12308 // Delete a row from the index while leaving a table entry.
Victor Costan1d868352018-06-26 19:06:48309 static const char kDeleteSql[] = "DELETE FROM x WHERE id = 0";
[email protected]ae4f1622013-12-08 06:49:12310 ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "x", kDeleteSql));
[email protected]dd325f052013-08-06 02:37:40311
[email protected]dd325f052013-08-06 02:37:40312 ASSERT_TRUE(Reopen());
313
shess6ac39542016-02-04 20:56:22314 int error = SQLITE_OK;
tzikd16d2192018-03-07 08:58:36315 db().set_error_callback(base::BindRepeating(
316 &RecoveryCallback, &db(), db_path(), kCreateTable, kCreateIndex, &error));
shess6ac39542016-02-04 20:56:22317
[email protected]dd325f052013-08-06 02:37:40318 // Index shows one less than originally inserted.
Victor Costan1d868352018-06-26 19:06:48319 static const char kCountSql[] = "SELECT COUNT (*) FROM x";
shess1f955b182016-10-25 22:59:09320 EXPECT_EQ("9", ExecuteWithResult(&db(), kCountSql));
[email protected]dd325f052013-08-06 02:37:40321
Scott Hessdcf120482015-02-10 21:33:29322 // A full table scan shows all of the original data. Using column [v] to
323 // force use of the table rather than the index.
Victor Costan1d868352018-06-26 19:06:48324 static const char kDistinctSql[] = "SELECT DISTINCT COUNT (v) FROM x";
shess1f955b182016-10-25 22:59:09325 EXPECT_EQ("10", ExecuteWithResult(&db(), kDistinctSql));
[email protected]dd325f052013-08-06 02:37:40326
327 // Insert id 0 again. Since it is not in the index, the insert
328 // succeeds, but results in a duplicate value in the table.
Victor Costan1d868352018-06-26 19:06:48329 static const char kInsertSql[] = "INSERT INTO x (id, v) VALUES (0, 100)";
[email protected]dd325f052013-08-06 02:37:40330 ASSERT_TRUE(db().Execute(kInsertSql));
331
332 // Duplication is visible.
shess1f955b182016-10-25 22:59:09333 EXPECT_EQ("10", ExecuteWithResult(&db(), kCountSql));
334 EXPECT_EQ("11", ExecuteWithResult(&db(), kDistinctSql));
[email protected]dd325f052013-08-06 02:37:40335
336 // This works before the callback is called.
Victor Costan1d868352018-06-26 19:06:48337 static const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master";
[email protected]dd325f052013-08-06 02:37:40338 EXPECT_TRUE(db().IsSQLValid(kTrivialSql));
339
shess6ac39542016-02-04 20:56:22340 // TODO(shess): Figure out a statement which causes SQLite to notice the
341 // corruption. SELECT doesn't see errors because missing index values aren't
342 // visible. UPDATE or DELETE against v=0 don't see errors, even though the
343 // index item is missing. I suspect SQLite only deletes the key in these
344 // cases, but doesn't verify that one or more keys were deleted.
345 ASSERT_FALSE(db().Execute("INSERT INTO x (id, v) VALUES (0, 101)"));
346 EXPECT_EQ(SQLITE_CONSTRAINT_UNIQUE, error);
[email protected]dd325f052013-08-06 02:37:40347
348 // Database handle has been poisoned.
349 EXPECT_FALSE(db().IsSQLValid(kTrivialSql));
350
351 ASSERT_TRUE(Reopen());
352
353 // The recovered table has consistency between the index and the table.
shess1f955b182016-10-25 22:59:09354 EXPECT_EQ("10", ExecuteWithResult(&db(), kCountSql));
355 EXPECT_EQ("10", ExecuteWithResult(&db(), kDistinctSql));
[email protected]dd325f052013-08-06 02:37:40356
shess806f4992016-02-04 21:12:09357 // Only one of the values is retained.
Victor Costan1d868352018-06-26 19:06:48358 static const char kSelectSql[] = "SELECT v FROM x WHERE id = 0";
shess1f955b182016-10-25 22:59:09359 const std::string results = ExecuteWithResult(&db(), kSelectSql);
shess806f4992016-02-04 21:12:09360 EXPECT_TRUE(results=="100" || results=="0") << "Actual results: " << results;
[email protected]dd325f052013-08-06 02:37:40361}
[email protected]a8848a72013-11-18 04:18:47362
363TEST_F(SQLRecoveryTest, Meta) {
364 const int kVersion = 3;
365 const int kCompatibleVersion = 2;
366
367 {
368 sql::MetaTable meta;
369 EXPECT_TRUE(meta.Init(&db(), kVersion, kCompatibleVersion));
370 EXPECT_EQ(kVersion, meta.GetVersionNumber());
371 }
372
373 // Test expected case where everything works.
374 {
mostynbd82cd9952016-04-11 20:05:34375 std::unique_ptr<sql::Recovery> recovery =
376 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47377 EXPECT_TRUE(recovery->SetupMeta());
378 int version = 0;
379 EXPECT_TRUE(recovery->GetMetaVersionNumber(&version));
380 EXPECT_EQ(kVersion, version);
381
dchenge48600452015-12-28 02:24:50382 sql::Recovery::Rollback(std::move(recovery));
[email protected]a8848a72013-11-18 04:18:47383 }
384 ASSERT_TRUE(Reopen()); // Handle was poisoned.
385
386 // Test version row missing.
387 EXPECT_TRUE(db().Execute("DELETE FROM meta WHERE key = 'version'"));
388 {
mostynbd82cd9952016-04-11 20:05:34389 std::unique_ptr<sql::Recovery> recovery =
390 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47391 EXPECT_TRUE(recovery->SetupMeta());
392 int version = 0;
393 EXPECT_FALSE(recovery->GetMetaVersionNumber(&version));
394 EXPECT_EQ(0, version);
395
dchenge48600452015-12-28 02:24:50396 sql::Recovery::Rollback(std::move(recovery));
[email protected]a8848a72013-11-18 04:18:47397 }
398 ASSERT_TRUE(Reopen()); // Handle was poisoned.
399
400 // Test meta table missing.
401 EXPECT_TRUE(db().Execute("DROP TABLE meta"));
402 {
shess976814402016-06-21 06:56:25403 sql::test::ScopedErrorExpecter expecter;
404 expecter.ExpectError(SQLITE_CORRUPT); // From virtual table.
mostynbd82cd9952016-04-11 20:05:34405 std::unique_ptr<sql::Recovery> recovery =
406 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47407 EXPECT_FALSE(recovery->SetupMeta());
shess976814402016-06-21 06:56:25408 ASSERT_TRUE(expecter.SawExpectedErrors());
[email protected]a8848a72013-11-18 04:18:47409 }
410}
411
412// Baseline AutoRecoverTable() test.
413TEST_F(SQLRecoveryTest, AutoRecoverTable) {
414 // BIGINT and VARCHAR to test type affinity.
Victor Costan1d868352018-06-26 19:06:48415 static const char kCreateSql[] =
416 "CREATE TABLE x (id BIGINT, t TEXT, v VARCHAR)";
[email protected]a8848a72013-11-18 04:18:47417 ASSERT_TRUE(db().Execute(kCreateSql));
418 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (11, 'This is', 'a test')"));
419 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5, 'That was', 'a test')"));
420
421 // Save aside a copy of the original schema and data.
422 const std::string orig_schema(GetSchema(&db()));
Victor Costan1d868352018-06-26 19:06:48423 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
[email protected]a8848a72013-11-18 04:18:47424 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n"));
425
426 // Create a lame-duck table which will not be propagated by recovery to
427 // detect that the recovery code actually ran.
428 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
429 ASSERT_NE(orig_schema, GetSchema(&db()));
430
431 {
mostynbd82cd9952016-04-11 20:05:34432 std::unique_ptr<sql::Recovery> recovery =
433 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47434 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
435
436 // Save a copy of the temp db's schema before recovering the table.
Victor Costan1d868352018-06-26 19:06:48437 static const char kTempSchemaSql[] =
438 "SELECT name, sql FROM sqlite_temp_master";
[email protected]a8848a72013-11-18 04:18:47439 const std::string temp_schema(
440 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n"));
441
442 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44443 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]a8848a72013-11-18 04:18:47444 EXPECT_EQ(2u, rows);
445
446 // Test that any additional temp tables were cleaned up.
447 EXPECT_EQ(temp_schema,
448 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n"));
449
dchenge48600452015-12-28 02:24:50450 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]a8848a72013-11-18 04:18:47451 }
452
453 // Since the database was not corrupt, the entire schema and all
454 // data should be recovered.
455 ASSERT_TRUE(Reopen());
456 ASSERT_EQ(orig_schema, GetSchema(&db()));
457 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
458
459 // Recovery fails if the target table doesn't exist.
460 {
mostynbd82cd9952016-04-11 20:05:34461 std::unique_ptr<sql::Recovery> recovery =
462 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47463 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
464
465 // TODO(shess): Should this failure implicitly lead to Raze()?
466 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44467 EXPECT_FALSE(recovery->AutoRecoverTable("y", &rows));
[email protected]a8848a72013-11-18 04:18:47468
dchenge48600452015-12-28 02:24:50469 sql::Recovery::Unrecoverable(std::move(recovery));
[email protected]a8848a72013-11-18 04:18:47470 }
471}
472
473// Test that default values correctly replace nulls. The recovery
474// virtual table reads directly from the database, so DEFAULT is not
475// interpretted at that level.
476TEST_F(SQLRecoveryTest, AutoRecoverTableWithDefault) {
477 ASSERT_TRUE(db().Execute("CREATE TABLE x (id INTEGER)"));
478 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5)"));
479 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (15)"));
480
481 // ALTER effectively leaves the new columns NULL in the first two
482 // rows. The row with 17 will get the default injected at insert
483 // time, while the row with 42 will get the actual value provided.
484 // Embedded "'" to make sure default-handling continues to be quoted
485 // correctly.
486 ASSERT_TRUE(db().Execute("ALTER TABLE x ADD COLUMN t TEXT DEFAULT 'a''a'"));
487 ASSERT_TRUE(db().Execute("ALTER TABLE x ADD COLUMN b BLOB DEFAULT x'AA55'"));
488 ASSERT_TRUE(db().Execute("ALTER TABLE x ADD COLUMN i INT DEFAULT 93"));
489 ASSERT_TRUE(db().Execute("INSERT INTO x (id) VALUES (17)"));
490 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (42, 'b', x'1234', 12)"));
491
492 // Save aside a copy of the original schema and data.
493 const std::string orig_schema(GetSchema(&db()));
Victor Costan1d868352018-06-26 19:06:48494 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
[email protected]a8848a72013-11-18 04:18:47495 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n"));
496
497 // Create a lame-duck table which will not be propagated by recovery to
498 // detect that the recovery code actually ran.
499 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
500 ASSERT_NE(orig_schema, GetSchema(&db()));
501
502 // Mechanically adjust the stored schema and data to allow detecting
503 // where the default value is coming from. The target table is just
504 // like the original with the default for [t] changed, to signal
505 // defaults coming from the recovery system. The two %5 rows should
506 // get the target-table default for [t], while the others should get
507 // the source-table default.
508 std::string final_schema(orig_schema);
509 std::string final_data(orig_data);
510 size_t pos;
511 while ((pos = final_schema.find("'a''a'")) != std::string::npos) {
512 final_schema.replace(pos, 6, "'c''c'");
513 }
514 while ((pos = final_data.find("5|a'a")) != std::string::npos) {
515 final_data.replace(pos, 5, "5|c'c");
516 }
517
518 {
mostynbd82cd9952016-04-11 20:05:34519 std::unique_ptr<sql::Recovery> recovery =
520 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47521 // Different default to detect which table provides the default.
522 ASSERT_TRUE(recovery->db()->Execute(final_schema.c_str()));
523
524 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44525 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]a8848a72013-11-18 04:18:47526 EXPECT_EQ(4u, rows);
527
dchenge48600452015-12-28 02:24:50528 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]a8848a72013-11-18 04:18:47529 }
530
531 // Since the database was not corrupt, the entire schema and all
532 // data should be recovered.
533 ASSERT_TRUE(Reopen());
534 ASSERT_EQ(final_schema, GetSchema(&db()));
535 ASSERT_EQ(final_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
536}
537
538// Test that rows with NULL in a NOT NULL column are filtered
539// correctly. In the wild, this would probably happen due to
540// corruption, but here it is simulated by recovering a table which
541// allowed nulls into a table which does not.
542TEST_F(SQLRecoveryTest, AutoRecoverTableNullFilter) {
Victor Costan1d868352018-06-26 19:06:48543 static const char kOrigSchema[] = "CREATE TABLE x (id INTEGER, t TEXT)";
544 static const char kFinalSchema[] =
545 "CREATE TABLE x (id INTEGER, t TEXT NOT NULL)";
[email protected]a8848a72013-11-18 04:18:47546
547 ASSERT_TRUE(db().Execute(kOrigSchema));
Victor Costanbd623112018-07-18 04:17:27548 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (5, NULL)"));
[email protected]a8848a72013-11-18 04:18:47549 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (15, 'this is a test')"));
550
551 // Create a lame-duck table which will not be propagated by recovery to
552 // detect that the recovery code actually ran.
553 ASSERT_EQ(kOrigSchema, GetSchema(&db()));
554 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
555 ASSERT_NE(kOrigSchema, GetSchema(&db()));
556
557 {
mostynbd82cd9952016-04-11 20:05:34558 std::unique_ptr<sql::Recovery> recovery =
559 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47560 ASSERT_TRUE(recovery->db()->Execute(kFinalSchema));
561
562 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44563 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]a8848a72013-11-18 04:18:47564 EXPECT_EQ(1u, rows);
565
dchenge48600452015-12-28 02:24:50566 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]a8848a72013-11-18 04:18:47567 }
568
569 // The schema should be the same, but only one row of data should
570 // have been recovered.
571 ASSERT_TRUE(Reopen());
572 ASSERT_EQ(kFinalSchema, GetSchema(&db()));
Victor Costan1d868352018-06-26 19:06:48573 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
[email protected]a8848a72013-11-18 04:18:47574 ASSERT_EQ("15|this is a test", ExecuteWithResults(&db(), kXSql, "|", "\n"));
575}
576
577// Test AutoRecoverTable with a ROWID alias.
578TEST_F(SQLRecoveryTest, AutoRecoverTableWithRowid) {
579 // The rowid alias is almost always the first column, intentionally
580 // put it later.
Victor Costan1d868352018-06-26 19:06:48581 static const char kCreateSql[] =
[email protected]a8848a72013-11-18 04:18:47582 "CREATE TABLE x (t TEXT, id INTEGER PRIMARY KEY NOT NULL)";
583 ASSERT_TRUE(db().Execute(kCreateSql));
Victor Costanbd623112018-07-18 04:17:27584 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test', NULL)"));
585 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('That was a test', NULL)"));
[email protected]a8848a72013-11-18 04:18:47586
587 // Save aside a copy of the original schema and data.
588 const std::string orig_schema(GetSchema(&db()));
Victor Costan1d868352018-06-26 19:06:48589 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
[email protected]a8848a72013-11-18 04:18:47590 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n"));
591
592 // Create a lame-duck table which will not be propagated by recovery to
593 // detect that the recovery code actually ran.
594 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
595 ASSERT_NE(orig_schema, GetSchema(&db()));
596
597 {
mostynbd82cd9952016-04-11 20:05:34598 std::unique_ptr<sql::Recovery> recovery =
599 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47600 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
601
602 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44603 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]a8848a72013-11-18 04:18:47604 EXPECT_EQ(2u, rows);
605
dchenge48600452015-12-28 02:24:50606 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]a8848a72013-11-18 04:18:47607 }
608
609 // Since the database was not corrupt, the entire schema and all
610 // data should be recovered.
611 ASSERT_TRUE(Reopen());
612 ASSERT_EQ(orig_schema, GetSchema(&db()));
613 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
614}
615
616// Test that a compound primary key doesn't fire the ROWID code.
617TEST_F(SQLRecoveryTest, AutoRecoverTableWithCompoundKey) {
Victor Costan1d868352018-06-26 19:06:48618 static const char kCreateSql[] =
[email protected]a8848a72013-11-18 04:18:47619 "CREATE TABLE x ("
620 "id INTEGER NOT NULL,"
621 "id2 TEXT NOT NULL,"
622 "t TEXT,"
623 "PRIMARY KEY (id, id2)"
624 ")";
625 ASSERT_TRUE(db().Execute(kCreateSql));
626
627 // NOTE(shess): Do not accidentally use [id] 1, 2, 3, as those will
628 // be the ROWID values.
629 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (1, 'a', 'This is a test')"));
630 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (1, 'b', 'That was a test')"));
631 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (2, 'a', 'Another test')"));
632
633 // Save aside a copy of the original schema and data.
634 const std::string orig_schema(GetSchema(&db()));
Victor Costan1d868352018-06-26 19:06:48635 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
[email protected]a8848a72013-11-18 04:18:47636 const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n"));
637
638 // Create a lame-duck table which will not be propagated by recovery to
639 // detect that the recovery code actually ran.
640 ASSERT_TRUE(db().Execute("CREATE TABLE y (c TEXT)"));
641 ASSERT_NE(orig_schema, GetSchema(&db()));
642
643 {
mostynbd82cd9952016-04-11 20:05:34644 std::unique_ptr<sql::Recovery> recovery =
645 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47646 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
647
648 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44649 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]a8848a72013-11-18 04:18:47650 EXPECT_EQ(3u, rows);
651
dchenge48600452015-12-28 02:24:50652 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]a8848a72013-11-18 04:18:47653 }
654
655 // Since the database was not corrupt, the entire schema and all
656 // data should be recovered.
657 ASSERT_TRUE(Reopen());
658 ASSERT_EQ(orig_schema, GetSchema(&db()));
659 ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
660}
661
shess6f68bd32016-02-04 19:29:44662// Test recovering from a table with fewer columns than the target.
663TEST_F(SQLRecoveryTest, AutoRecoverTableMissingColumns) {
Victor Costan1d868352018-06-26 19:06:48664 static const char kCreateSql[] =
665 "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)";
666 static const char kAlterSql[] =
667 "ALTER TABLE x ADD COLUMN t1 TEXT DEFAULT 't'";
[email protected]a8848a72013-11-18 04:18:47668 ASSERT_TRUE(db().Execute(kCreateSql));
669 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (1, 'This is')"));
670 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (2, 'That was')"));
671
shess6f68bd32016-02-04 19:29:44672 // Generate the expected info by faking a table to match what recovery will
673 // create.
[email protected]a8848a72013-11-18 04:18:47674 const std::string orig_schema(GetSchema(&db()));
Victor Costan1d868352018-06-26 19:06:48675 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
shess6f68bd32016-02-04 19:29:44676 std::string expected_schema;
677 std::string expected_data;
678 {
679 ASSERT_TRUE(db().BeginTransaction());
680 ASSERT_TRUE(db().Execute(kAlterSql));
[email protected]a8848a72013-11-18 04:18:47681
shess6f68bd32016-02-04 19:29:44682 expected_schema = GetSchema(&db());
683 expected_data = ExecuteWithResults(&db(), kXSql, "|", "\n");
[email protected]a8848a72013-11-18 04:18:47684
shess6f68bd32016-02-04 19:29:44685 db().RollbackTransaction();
686 }
687
688 // Following tests are pointless if the rollback didn't work.
689 ASSERT_EQ(orig_schema, GetSchema(&db()));
690
691 // Recover the previous version of the table into the altered version.
[email protected]a8848a72013-11-18 04:18:47692 {
mostynbd82cd9952016-04-11 20:05:34693 std::unique_ptr<sql::Recovery> recovery =
694 sql::Recovery::Begin(&db(), db_path());
[email protected]a8848a72013-11-18 04:18:47695 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
shess6f68bd32016-02-04 19:29:44696 ASSERT_TRUE(recovery->db()->Execute(kAlterSql));
[email protected]a8848a72013-11-18 04:18:47697 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44698 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]a8848a72013-11-18 04:18:47699 EXPECT_EQ(2u, rows);
dchenge48600452015-12-28 02:24:50700 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]a8848a72013-11-18 04:18:47701 }
702
703 // Since the database was not corrupt, the entire schema and all
704 // data should be recovered.
705 ASSERT_TRUE(Reopen());
shess6f68bd32016-02-04 19:29:44706 ASSERT_EQ(expected_schema, GetSchema(&db()));
707 ASSERT_EQ(expected_data, ExecuteWithResults(&db(), kXSql, "|", "\n"));
[email protected]a8848a72013-11-18 04:18:47708}
[email protected]cfb821612014-07-10 00:48:06709
710// Recover a golden file where an interior page has been manually modified so
711// that the number of cells is greater than will fit on a single page. This
712// case happened in <http://crbug.com/387868>.
713TEST_F(SQLRecoveryTest, Bug387868) {
714 base::FilePath golden_path;
Avi Drissmanea15ea02018-05-07 18:55:12715 ASSERT_TRUE(base::PathService::Get(sql::test::DIR_TEST_DATA, &golden_path));
[email protected]cfb821612014-07-10 00:48:06716 golden_path = golden_path.AppendASCII("recovery_387868");
717 db().Close();
718 ASSERT_TRUE(base::CopyFile(golden_path, db_path()));
719 ASSERT_TRUE(Reopen());
720
721 {
mostynbd82cd9952016-04-11 20:05:34722 std::unique_ptr<sql::Recovery> recovery =
723 sql::Recovery::Begin(&db(), db_path());
[email protected]cfb821612014-07-10 00:48:06724 ASSERT_TRUE(recovery.get());
725
726 // Create the new version of the table.
Victor Costan1d868352018-06-26 19:06:48727 static const char kCreateSql[] =
[email protected]cfb821612014-07-10 00:48:06728 "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)";
729 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
730
731 size_t rows = 0;
shess6f68bd32016-02-04 19:29:44732 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
[email protected]cfb821612014-07-10 00:48:06733 EXPECT_EQ(43u, rows);
734
735 // Successfully recovered.
dchenge48600452015-12-28 02:24:50736 EXPECT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
[email protected]cfb821612014-07-10 00:48:06737 }
738}
[email protected]dd325f052013-08-06 02:37:40739
shess2f3a814b2015-11-05 18:11:10740// Memory-mapped I/O interacts poorly with I/O errors. Make sure the recovery
741// database doesn't accidentally enable it.
742TEST_F(SQLRecoveryTest, NoMmap) {
mostynbd82cd9952016-04-11 20:05:34743 std::unique_ptr<sql::Recovery> recovery =
744 sql::Recovery::Begin(&db(), db_path());
shess2f3a814b2015-11-05 18:11:10745 ASSERT_TRUE(recovery.get());
746
747 // In the current implementation, the PRAGMA successfully runs with no result
748 // rows. Running with a single result of |0| is also acceptable.
749 sql::Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size"));
750 EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0));
751}
752
shessa402e752016-07-02 00:25:11753TEST_F(SQLRecoveryTest, RecoverDatabase) {
754 // As a side effect, AUTOINCREMENT creates the sqlite_sequence table for
755 // RecoverDatabase() to handle.
756 ASSERT_TRUE(db().Execute(
757 "CREATE TABLE x (id INTEGER PRIMARY KEY AUTOINCREMENT, v TEXT)"));
758 EXPECT_TRUE(db().Execute("INSERT INTO x (v) VALUES ('turtle')"));
759 EXPECT_TRUE(db().Execute("INSERT INTO x (v) VALUES ('truck')"));
760 EXPECT_TRUE(db().Execute("INSERT INTO x (v) VALUES ('trailer')"));
761
762 // This table needs index and a unique index to work.
763 ASSERT_TRUE(db().Execute("CREATE TABLE y (name TEXT, v TEXT)"));
764 ASSERT_TRUE(db().Execute("CREATE UNIQUE INDEX y_name ON y(name)"));
765 ASSERT_TRUE(db().Execute("CREATE INDEX y_v ON y(v)"));
766 EXPECT_TRUE(db().Execute("INSERT INTO y VALUES ('jim', 'telephone')"));
767 EXPECT_TRUE(db().Execute("INSERT INTO y VALUES ('bob', 'truck')"));
768 EXPECT_TRUE(db().Execute("INSERT INTO y VALUES ('dean', 'trailer')"));
769
770 // View which is the intersection of [x.v] and [y.v].
771 ASSERT_TRUE(db().Execute(
772 "CREATE VIEW v AS SELECT x.v FROM x, y WHERE x.v = y.v"));
773
774 // When an element is deleted from [x], trigger a delete on [y]. Between the
775 // BEGIN and END, [old] stands for the deleted rows from [x].
776 ASSERT_TRUE(db().Execute("CREATE TRIGGER t AFTER DELETE ON x "
777 "BEGIN DELETE FROM y WHERE y.v = old.v; END"));
778
779 // Save aside a copy of the original schema, verifying that it has the created
780 // items plus the sqlite_sequence table.
781 const std::string orig_schema(GetSchema(&db()));
782 ASSERT_EQ(6, std::count(orig_schema.begin(), orig_schema.end(), '\n'));
783
Victor Costan1d868352018-06-26 19:06:48784 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
785 static const char kYSql[] = "SELECT * FROM y ORDER BY 1";
786 static const char kVSql[] = "SELECT * FROM v ORDER BY 1";
shessa402e752016-07-02 00:25:11787 EXPECT_EQ("1|turtle\n2|truck\n3|trailer",
788 ExecuteWithResults(&db(), kXSql, "|", "\n"));
789 EXPECT_EQ("bob|truck\ndean|trailer\njim|telephone",
790 ExecuteWithResults(&db(), kYSql, "|", "\n"));
791 EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db(), kVSql, "|", "\n"));
792
793 // Database handle is valid before recovery, poisoned after.
Victor Costan1d868352018-06-26 19:06:48794 static const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master";
shessa402e752016-07-02 00:25:11795 EXPECT_TRUE(db().IsSQLValid(kTrivialSql));
796 sql::Recovery::RecoverDatabase(&db(), db_path());
797 EXPECT_FALSE(db().IsSQLValid(kTrivialSql));
798
799 // Since the database was not corrupt, the entire schema and all
800 // data should be recovered.
801 ASSERT_TRUE(Reopen());
802 ASSERT_EQ(orig_schema, GetSchema(&db()));
803 EXPECT_EQ("1|turtle\n2|truck\n3|trailer",
804 ExecuteWithResults(&db(), kXSql, "|", "\n"));
805 EXPECT_EQ("bob|truck\ndean|trailer\njim|telephone",
806 ExecuteWithResults(&db(), kYSql, "|", "\n"));
807 EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db(), kVSql, "|", "\n"));
808
809 // Test that the trigger works.
810 ASSERT_TRUE(db().Execute("DELETE FROM x WHERE v = 'truck'"));
811 EXPECT_EQ("1|turtle\n3|trailer",
812 ExecuteWithResults(&db(), kXSql, "|", "\n"));
813 EXPECT_EQ("dean|trailer\njim|telephone",
814 ExecuteWithResults(&db(), kYSql, "|", "\n"));
815 EXPECT_EQ("trailer", ExecuteWithResults(&db(), kVSql, "|", "\n"));
816}
817
shess00d65d42017-03-02 21:12:19818// When RecoverDatabase() encounters SQLITE_NOTADB, the database is deleted.
819TEST_F(SQLRecoveryTest, RecoverDatabaseDelete) {
820 // Create a valid database, then write junk over the header. This should lead
821 // to SQLITE_NOTADB, which will cause ATTACH to fail.
822 ASSERT_TRUE(db().Execute("CREATE TABLE x (t TEXT)"));
823 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')"));
824 db().Close();
825 WriteJunkToDatabase(SQLTestBase::TYPE_OVERWRITE);
826
827 {
828 sql::test::ScopedErrorExpecter expecter;
829 expecter.ExpectError(SQLITE_NOTADB);
830
831 // Reopen() here because it will see SQLITE_NOTADB.
832 ASSERT_TRUE(Reopen());
833
834 // This should "recover" the database by making it valid, but empty.
835 sql::Recovery::RecoverDatabase(&db(), db_path());
836
837 ASSERT_TRUE(expecter.SawExpectedErrors());
838 }
839
840 // Recovery poisoned the handle, must re-open.
841 db().Close();
842 ASSERT_TRUE(Reopen());
843
844 EXPECT_EQ("", GetSchema(&db()));
845}
846
shess5207e1452017-04-12 23:55:33847// Allow callers to validate the database between recovery and commit.
848TEST_F(SQLRecoveryTest, BeginRecoverDatabase) {
849 // Create a table with a broken index.
850 ASSERT_TRUE(db().Execute("CREATE TABLE t (id INTEGER PRIMARY KEY, c TEXT)"));
851 ASSERT_TRUE(db().Execute("CREATE UNIQUE INDEX t_id ON t (id)"));
852 ASSERT_TRUE(db().Execute("INSERT INTO t VALUES (1, 'hello world')"));
853 ASSERT_TRUE(db().Execute("INSERT INTO t VALUES (2, 'testing')"));
854 ASSERT_TRUE(db().Execute("INSERT INTO t VALUES (3, 'nope')"));
855
856 // Inject corruption into the index.
857 db().Close();
Victor Costan1d868352018-06-26 19:06:48858 static const char kDeleteSql[] = "DELETE FROM t WHERE id = 3";
shess5207e1452017-04-12 23:55:33859 ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "t_id", kDeleteSql));
860 ASSERT_TRUE(Reopen());
861
862 // id as read from index.
Victor Costan1d868352018-06-26 19:06:48863 static const char kSelectIndexIdSql[] = "SELECT id FROM t INDEXED BY t_id";
shess5207e1452017-04-12 23:55:33864 EXPECT_EQ("1,2,3", ExecuteWithResults(&db(), kSelectIndexIdSql, "|", ","));
865
866 // id as read from table.
Victor Costan1d868352018-06-26 19:06:48867 static const char kSelectTableIdSql[] = "SELECT id FROM t NOT INDEXED";
shess5207e1452017-04-12 23:55:33868 EXPECT_EQ("1,2", ExecuteWithResults(&db(), kSelectTableIdSql, "|", ","));
869
870 // Run recovery code, then rollback. Database remains the same.
871 {
872 std::unique_ptr<sql::Recovery> recovery =
873 sql::Recovery::BeginRecoverDatabase(&db(), db_path());
874 ASSERT_TRUE(recovery);
875 sql::Recovery::Rollback(std::move(recovery));
876 }
877 db().Close();
878 ASSERT_TRUE(Reopen());
879 EXPECT_EQ("1,2,3", ExecuteWithResults(&db(), kSelectIndexIdSql, "|", ","));
880 EXPECT_EQ("1,2", ExecuteWithResults(&db(), kSelectTableIdSql, "|", ","));
881
882 // Run recovery code, then commit. The failing row is dropped.
883 {
884 std::unique_ptr<sql::Recovery> recovery =
885 sql::Recovery::BeginRecoverDatabase(&db(), db_path());
886 ASSERT_TRUE(recovery);
887 ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery)));
888 }
889 db().Close();
890 ASSERT_TRUE(Reopen());
891 EXPECT_EQ("1,2", ExecuteWithResults(&db(), kSelectIndexIdSql, "|", ","));
892 EXPECT_EQ("1,2", ExecuteWithResults(&db(), kSelectTableIdSql, "|", ","));
893}
894
shess63188112016-08-27 10:26:23895// Test histograms recorded when the invalid database cannot be attached.
896TEST_F(SQLRecoveryTest, AttachFailure) {
897 // Create a valid database, then write junk over the header. This should lead
898 // to SQLITE_NOTADB, which will cause ATTACH to fail.
899 ASSERT_TRUE(db().Execute("CREATE TABLE x (t TEXT)"));
900 ASSERT_TRUE(db().Execute("INSERT INTO x VALUES ('This is a test')"));
901 db().Close();
902 WriteJunkToDatabase(SQLTestBase::TYPE_OVERWRITE);
903
Victor Costan1d868352018-06-26 19:06:48904 static const char kEventHistogramName[] = "Sqlite.RecoveryEvents";
shess63188112016-08-27 10:26:23905 const int kEventEnum = 5; // RECOVERY_FAILED_ATTACH
Victor Costan1d868352018-06-26 19:06:48906 static const char kErrorHistogramName[] = "Sqlite.RecoveryAttachError";
shess63188112016-08-27 10:26:23907 base::HistogramTester tester;
908
909 {
910 sql::test::ScopedErrorExpecter expecter;
911 expecter.ExpectError(SQLITE_NOTADB);
912
913 // Reopen() here because it will see SQLITE_NOTADB.
914 ASSERT_TRUE(Reopen());
915
916 // Begin() should fail.
917 std::unique_ptr<sql::Recovery>
918 recovery = sql::Recovery::Begin(&db(), db_path());
919 ASSERT_FALSE(recovery.get());
920
921 ASSERT_TRUE(expecter.SawExpectedErrors());
922 }
923
924 // Verify that the failure was in the right place with the expected code.
925 tester.ExpectBucketCount(kEventHistogramName, kEventEnum, 1);
926 tester.ExpectBucketCount(kErrorHistogramName, SQLITE_NOTADB, 1);
927}
928
shess0b8d5932016-10-27 19:54:12929// Helper for SQLRecoveryTest.PageSize. Creates a fresh db based on db_prefix,
930// with the given initial page size, and verifies it against the expected size.
931// Then changes to the final page size and recovers, verifying that the
932// recovered database ends up with the expected final page size.
933void TestPageSize(const base::FilePath& db_prefix,
934 int initial_page_size,
935 const std::string& expected_initial_page_size,
936 int final_page_size,
937 const std::string& expected_final_page_size) {
Victor Costan1d868352018-06-26 19:06:48938 static const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
939 static const char kInsertSql1[] = "INSERT INTO x VALUES ('This is a test')";
940 static const char kInsertSql2[] = "INSERT INTO x VALUES ('That was a test')";
941 static const char kSelectSql[] = "SELECT * FROM x ORDER BY t";
shess0b8d5932016-10-27 19:54:12942
943 const base::FilePath db_path = db_prefix.InsertBeforeExtensionASCII(
Raul Tambre6c708e32019-02-08 22:35:14944 base::NumberToString(initial_page_size));
Victor Costancfbfa602018-08-01 23:24:46945 sql::Database::Delete(db_path);
Shubham Aggarwal7b60fe6e2020-10-15 06:00:28946 sql::Database db({.page_size = initial_page_size});
shess0b8d5932016-10-27 19:54:12947 ASSERT_TRUE(db.Open(db_path));
948 ASSERT_TRUE(db.Execute(kCreateSql));
949 ASSERT_TRUE(db.Execute(kInsertSql1));
950 ASSERT_TRUE(db.Execute(kInsertSql2));
951 ASSERT_EQ(expected_initial_page_size,
952 ExecuteWithResult(&db, "PRAGMA page_size"));
shess0b8d5932016-10-27 19:54:12953 db.Close();
954
Shubham Aggarwal7b60fe6e2020-10-15 06:00:28955 // Re-open the database while setting a new |options.page_size| in the object.
956 sql::Database recover_db({.page_size = final_page_size});
957 ASSERT_TRUE(recover_db.Open(db_path));
958 // Recovery will use the page size set in the database object, which may not
959 // match the file's page size.
960 sql::Recovery::RecoverDatabase(&recover_db, db_path);
961
962 // Recovery poisoned the handle, must re-open.
963 recover_db.Close();
964
shess0b8d5932016-10-27 19:54:12965 // Make sure the page size is read from the file.
Shubham Aggarwal7b60fe6e2020-10-15 06:00:28966 sql::Database recovered_db(
967 {.page_size = sql::DatabaseOptions::kDefaultPageSize});
968 ASSERT_TRUE(recovered_db.Open(db_path));
shess0b8d5932016-10-27 19:54:12969 ASSERT_EQ(expected_final_page_size,
Shubham Aggarwal7b60fe6e2020-10-15 06:00:28970 ExecuteWithResult(&recovered_db, "PRAGMA page_size"));
shess0b8d5932016-10-27 19:54:12971 EXPECT_EQ("That was a test\nThis is a test",
Shubham Aggarwal7b60fe6e2020-10-15 06:00:28972 ExecuteWithResults(&recovered_db, kSelectSql, "|", "\n"));
shess0b8d5932016-10-27 19:54:12973}
974
975// Verify that sql::Recovery maintains the page size, and the virtual table
976// works with page sizes other than SQLite's default. Also verify the case
977// where the default page size has changed.
978TEST_F(SQLRecoveryTest, PageSize) {
979 const std::string default_page_size =
980 ExecuteWithResult(&db(), "PRAGMA page_size");
981
Victor Costan7f6abbbe2018-07-29 02:57:27982 // Check the default page size first.
983 EXPECT_NO_FATAL_FAILURE(TestPageSize(
Shubham Aggarwal7b60fe6e2020-10-15 06:00:28984 db_path(), sql::DatabaseOptions::kDefaultPageSize, default_page_size,
985 sql::DatabaseOptions::kDefaultPageSize, default_page_size));
shess0b8d5932016-10-27 19:54:12986
Victor Costan7f6abbbe2018-07-29 02:57:27987 // Sync uses 32k pages.
shess0b8d5932016-10-27 19:54:12988 EXPECT_NO_FATAL_FAILURE(
989 TestPageSize(db_path(), 32768, "32768", 32768, "32768"));
990
991 // Many clients use 4k pages. This is the SQLite default after 3.12.0.
992 EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path(), 4096, "4096", 4096, "4096"));
993
994 // 1k is the default page size before 3.12.0.
995 EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path(), 1024, "1024", 1024, "1024"));
996
997 // Databases with no page size specified should recover with the new default
998 // page size. 2k has never been the default page size.
999 ASSERT_NE("2048", default_page_size);
Victor Costan7f6abbbe2018-07-29 02:57:271000 EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path(), 2048, "2048",
Shubham Aggarwal7b60fe6e2020-10-15 06:00:281001 sql::DatabaseOptions::kDefaultPageSize,
Victor Costan7f6abbbe2018-07-29 02:57:271002 default_page_size));
shess0b8d5932016-10-27 19:54:121003}
1004
[email protected]8d409412013-07-19 18:25:301005} // namespace