Movatterモバイル変換


[0]ホーム

URL:


Google Git
Sign in
chromium /chromium /src /refs/heads/main /. /sql /recovery_unittest.cc
blob: eb34482f38cfb0ef610a7892d14dfc30b4df31c0 [file] [log] [blame]
Avi Drissman69b874f2022-09-15 19:11:14[diff] [blame]1// Copyright 2013 The Chromium Authors
shess@chromium.org8d409412013-07-19 18:25:30[diff] [blame]2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Arthur Sonzogni68107b02024-08-20 15:38:30[diff] [blame]5#ifdef UNSAFE_BUFFERS_BUILD
6// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
7#pragma allow_unsafe_buffers
8#endif
9
dchenge48600452015-12-28 02:24:50[diff] [blame]10#include"sql/recovery.h"
avi0b519202015-12-21 07:25:19[diff] [blame]11
dchenge48600452015-12-28 02:24:50[diff] [blame]12#include<stddef.h>
mostynbd82cd9952016-04-11 20:05:34[diff] [blame]13
Peter Kasting3f01b692025-01-27 19:50:47[diff] [blame]14#include<algorithm>
Takuto Ikuta2eb61342024-05-10 09:05:35[diff] [blame]15#include<cstdint>
shess@chromium.orgae4f1622013-12-08 06:49:12[diff] [blame]16#include<string>
Takuto Ikuta2eb61342024-05-10 09:05:35[diff] [blame]17#include<tuple>
dchenge48600452015-12-28 02:24:50[diff] [blame]18#include<utility>
Takuto Ikuta2eb61342024-05-10 09:05:35[diff] [blame]19#include<vector>
shess@chromium.orgae4f1622013-12-08 06:49:12[diff] [blame]20
Peter Boström59a246e82025-03-17 03:55:33[diff] [blame]21#include"base/dcheck_is_on.h"
Takuto Ikuta2eb61342024-05-10 09:05:35[diff] [blame]22#include"base/files/file.h"
shess@chromium.orgae4f1622013-12-08 06:49:12[diff] [blame]23#include"base/files/file_path.h"
thestig22dfc4012014-09-05 08:29:44[diff] [blame]24#include"base/files/file_util.h"
shess@chromium.org8d409412013-07-19 18:25:30[diff] [blame]25#include"base/files/scoped_temp_dir.h"
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]26#include"base/functional/callback_forward.h"
Austin Sullivan2fbe496e2023-05-18 19:48:52[diff] [blame]27#include"base/functional/callback_helpers.h"
shess@chromium.orgcfb821612014-07-10 00:48:06[diff] [blame]28#include"base/path_service.h"
Austin Sullivan7b543402023-10-07 00:55:09[diff] [blame]29#include"base/strings/strcat.h"
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]30#include"base/strings/string_number_conversions.h"
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]31#include"base/test/bind.h"
Austin Sullivan2fbe496e2023-05-18 19:48:52[diff] [blame]32#include"base/test/gtest_util.h"
Austin Sullivanc99e9302023-05-19 02:41:45[diff] [blame]33#include"base/test/metrics/histogram_tester.h"
Takuto Ikuta2eb61342024-05-10 09:05:35[diff] [blame]34#include"build/buildflag.h"
Victor Costancfbfa602018-08-01 23:24:46[diff] [blame]35#include"sql/database.h"
shess@chromium.org8d409412013-07-19 18:25:30[diff] [blame]36#include"sql/meta_table.h"
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]37#include"sql/sqlite_result_code.h"
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]38#include"sql/sqlite_result_code_values.h"
shess@chromium.org8d409412013-07-19 18:25:30[diff] [blame]39#include"sql/statement.h"
shess976814402016-06-21 06:56:25[diff] [blame]40#include"sql/test/scoped_error_expecter.h"
shess@chromium.orgae4f1622013-12-08 06:49:12[diff] [blame]41#include"sql/test/test_helpers.h"
shess@chromium.org8d409412013-07-19 18:25:30[diff] [blame]42#include"testing/gtest/include/gtest/gtest.h"
43#include"third_party/sqlite/sqlite3.h"
44
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]45namespace sql{
46
shess@chromium.org8d409412013-07-19 18:25:30[diff] [blame]47namespace{
48
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]49using test::ExecuteWithResult;
50using test::ExecuteWithResults;
shess@chromium.org8d409412013-07-19 18:25:30[diff] [blame]51
Austin Sullivanc99e9302023-05-19 02:41:45[diff] [blame]52constexprchar kRecoveryResultHistogramName[]="Sql.Recovery.Result";
Austin Sullivan54930842023-06-06 21:09:01[diff] [blame]53constexprchar kRecoveryResultCodeHistogramName[]="Sql.Recovery.ResultCode";
Austin Sullivanc99e9302023-05-19 02:41:45[diff] [blame]54
shess@chromium.org8d409412013-07-19 18:25:30[diff] [blame]55// Dump consistent human-readable representation of the database
56// schema. For tables or indices, this will contain the sql command
57// to create the table or index. For certain automatic SQLite
58// structures with no sql, the name is used.
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]59std::stringGetSchema(Database* db){
Victor Costan1d868352018-06-26 19:06:48[diff] [blame]60staticconstchar kSql[]=
John Delaney86dbec62021-08-24 15:05:21[diff] [blame]61"SELECT COALESCE(sql, name) FROM sqlite_schema ORDER BY 1";
shess@chromium.org8d409412013-07-19 18:25:30[diff] [blame]62returnExecuteWithResults(db, kSql,"|","\n");
63}
64
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]65// Parameterized to test with and without WAL mode enabled.
66classSqlRecoveryTest:public testing::Test,
67public testing::WithParamInterface<bool>{
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]68public:
Anthony Vallée-Dubois71d703132024-12-05 00:17:43[diff] [blame]69SqlRecoveryTest()
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]70: db_(DatabaseOptions().set_wal_mode(ShouldEnableWal()), test::kTestTag){
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]71}
72
73boolShouldEnableWal(){returnGetParam();}
74
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]75voidSetUp() override{
76 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
77 db_path_= temp_dir_.GetPath().AppendASCII("recovery_test.sqlite");
78 ASSERT_TRUE(db_.Open(db_path_));
79}
80
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]81voidTearDown() override{
82if(db_.is_open()){
83 db_.Close();
84}
85// Ensure the database, along with any recovery files, are cleaned up.
86 ASSERT_TRUE(base::DeleteFile(db_path_));
87 ASSERT_TRUE(base::DeleteFile(db_path_.AddExtensionASCII(".backup")));
88 ASSERT_TRUE(temp_dir_.Delete());
89}
90
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]91boolReopen(){
92 db_.Close();
93return db_.Open(db_path_);
94}
95
96boolOverwriteDatabaseHeader(){
97 base::File file(db_path_,
98 base::File::FLAG_CREATE_ALWAYS| base::File::FLAG_WRITE);
99staticconstexprchar kText[]="Now is the winter of our discontent.";
100constexprint kTextBytes=sizeof(kText)-1;
101return file.Write(0, kText, kTextBytes)== kTextBytes;
102}
103
104protected:
105 base::ScopedTempDir temp_dir_;
106 base::FilePath db_path_;
107Database db_;
Austin Sullivanc99e9302023-05-19 02:41:45[diff] [blame]108 base::HistogramTester histogram_tester_;
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]109};
110
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]111#if BUILDFLAG(IS_FUCHSIA)
112// WAL + recovery is not supported on Fuchsia, so only test without WAL mode.
113INSTANTIATE_TEST_SUITE_P(All,SqlRecoveryTest, testing::Values(false));
114#else
115INSTANTIATE_TEST_SUITE_P(All,SqlRecoveryTest, testing::Bool());
116#endif
Austin Sullivan18fdb6c92023-05-18 20:55:49[diff] [blame]117
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]118TEST_P(SqlRecoveryTest,ShouldAttemptRecovery){
Austin Sullivan18fdb6c92023-05-18 20:55:49[diff] [blame]119// Attempt to recover from corruption.
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]120 ASSERT_TRUE(Recovery::ShouldAttemptRecovery(&db_, SQLITE_CORRUPT));
Austin Sullivan18fdb6c92023-05-18 20:55:49[diff] [blame]121
122// Do not attempt to recover from transient errors.
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]123 EXPECT_FALSE(Recovery::ShouldAttemptRecovery(&db_, SQLITE_BUSY));
Austin Sullivan18fdb6c92023-05-18 20:55:49[diff] [blame]124
125// Do not attempt to recover null databases.
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]126 EXPECT_FALSE(Recovery::ShouldAttemptRecovery(nullptr, SQLITE_CORRUPT));
Austin Sullivan18fdb6c92023-05-18 20:55:49[diff] [blame]127
128// Do not attempt to recover closed databases.
Anthony Vallée-Duboise3c94912024-12-12 16:47:47[diff] [blame]129Database invalid_db(test::kTestTag);
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]130 EXPECT_FALSE(Recovery::ShouldAttemptRecovery(&invalid_db, SQLITE_CORRUPT));
Austin Sullivan18fdb6c92023-05-18 20:55:49[diff] [blame]131
132// Do not attempt to recover in-memory databases.
133 ASSERT_TRUE(invalid_db.OpenInMemory());
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]134 EXPECT_FALSE(Recovery::ShouldAttemptRecovery(&invalid_db, SQLITE_CORRUPT));
Austin Sullivan18fdb6c92023-05-18 20:55:49[diff] [blame]135
136// Return true for databases which have an error callback set, even though
137// the error callback should be reset before recovery is attempted.
138 db_.set_error_callback(base::DoNothing());
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]139 EXPECT_TRUE(Recovery::ShouldAttemptRecovery(&db_, SQLITE_CORRUPT));
Austin Sullivan18fdb6c92023-05-18 20:55:49[diff] [blame]140}
141
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]142TEST_P(SqlRecoveryTest,RecoverCorruptIndex){
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]143staticconstchar kCreateTable[]=
144"CREATE TABLE rows(indexed INTEGER NOT NULL, unindexed INTEGER NOT NULL)";
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]145 ASSERT_TRUE(db_.Execute(kCreateTable));
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]146
147staticconstchar kCreateIndex[]=
148"CREATE UNIQUE INDEX rows_index ON rows(indexed)";
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]149 ASSERT_TRUE(db_.Execute(kCreateIndex));
shess@chromium.orgdd325f052013-08-06 02:37:40[diff] [blame]150
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]151// Populate the table with powers of two. These numbers make it easy to see if
152// SUM() missed a row.
153 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(1, 1)"));
154 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(2, 2)"));
155 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(4, 4)"));
156 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(8, 8)"));
shess@chromium.orgdd325f052013-08-06 02:37:40[diff] [blame]157
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]158 db_.Close();
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]159 ASSERT_TRUE(test::CorruptIndexRootPage(db_path_,"rows_index"));
shess@chromium.orgdd325f052013-08-06 02:37:40[diff] [blame]160 ASSERT_TRUE(Reopen());
161
162int error= SQLITE_OK;
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]163 db_.set_error_callback(
164 base::BindLambdaForTesting([&](int sqlite_error,Statement* statement){
165 error= sqlite_error;
shess@chromium.orgdd325f052013-08-06 02:37:40[diff] [blame]166
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]167// Recovery::Begin() does not support a pre-existing error callback.
168 db_.reset_error_callback();
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]169
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]170 EXPECT_EQ(
171Recovery::RecoverDatabase(&db_,Recovery::Strategy::kRecoverOrRaze),
172SqliteResultCode::kOk);
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]173 histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName,
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]174Recovery::Result::kSuccess,
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]175/*expected_bucket_count=*/1);
176 histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
177SqliteLoggedResultCode::kNoError,
178/*expected_bucket_count=*/1);
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]179}));
shess@chromium.orgdd325f052013-08-06 02:37:40[diff] [blame]180
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]181// SUM(unindexed) heavily nudges SQLite to use the table instead of the index.
182staticconstchar kUnindexedCountSql[]="SELECT SUM(unindexed) FROM rows";
183 EXPECT_EQ("15",ExecuteWithResult(&db_, kUnindexedCountSql))
184<<"Table scan should not fail due to corrupt index";
185 EXPECT_EQ(SQLITE_OK, error)
186<<"Successful statement execution should not invoke the error callback";
shess@chromium.orgdd325f052013-08-06 02:37:40[diff] [blame]187
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]188staticconstchar kIndexedCountSql[]=
189"SELECT SUM(indexed) FROM rows INDEXED BY rows_index";
190 EXPECT_EQ("",ExecuteWithResult(&db_, kIndexedCountSql))
191<<"Index scan on corrupt index should fail";
192 EXPECT_EQ(SQLITE_CORRUPT, error)
193<<"Error callback should be called during scan on corrupt index";
shess@chromium.orgdd325f052013-08-06 02:37:40[diff] [blame]194
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]195 EXPECT_EQ("",ExecuteWithResult(&db_, kUnindexedCountSql))
196<<"Table scan should not succeed anymore on a poisoned database";
shess@chromium.orgdd325f052013-08-06 02:37:40[diff] [blame]197
198 ASSERT_TRUE(Reopen());
199
200// The recovered table has consistency between the index and the table.
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]201 EXPECT_EQ("15",ExecuteWithResult(&db_, kUnindexedCountSql))
202<<"Table should survive database recovery";
203 EXPECT_EQ("15",ExecuteWithResult(&db_, kIndexedCountSql))
204<<"Index should be reconstructed during database recovery";
205}
shess@chromium.orgdd325f052013-08-06 02:37:40[diff] [blame]206
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]207TEST_P(SqlRecoveryTest,RecoverCorruptTable){
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]208// The `filler` column is used to cause a record to overflow multiple pages.
209staticconstchar kCreateTable[]=
210// clang-format off
211"CREATE TABLE rows(indexed INTEGER NOT NULL, unindexed INTEGER NOT NULL,"
212"filler BLOB NOT NULL)";
213// clang-format on
214 ASSERT_TRUE(db_.Execute(kCreateTable));
215
216staticconstchar kCreateIndex[]=
217"CREATE UNIQUE INDEX rows_index ON rows(indexed)";
218 ASSERT_TRUE(db_.Execute(kCreateIndex));
219
220// Populate the table with powers of two. These numbers make it easy to see if
221// SUM() missed a row.
222 ASSERT_TRUE(db_.Execute(
223"INSERT INTO rows(indexed, unindexed, filler) VALUES(1, 1, x'31')"));
224 ASSERT_TRUE(db_.Execute(
225"INSERT INTO rows(indexed, unindexed, filler) VALUES(2, 2, x'32')"));
226 ASSERT_TRUE(db_.Execute(
227"INSERT INTO rows(indexed, unindexed, filler) VALUES(4, 4, x'34')"));
228
229constexprint kDbPageSize=4096;
230{
231// Insert a record that will overflow the page.
232 std::vector<uint8_t> large_buffer;
233 ASSERT_EQ(db_.page_size(), kDbPageSize)
234<<"Page overflow relies on specific size";
235 large_buffer.resize(kDbPageSize*2);
Peter Kasting3f01b692025-01-27 19:50:47[diff] [blame]236 std::ranges::fill(large_buffer,'8');
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]237Statement insert(db_.GetUniqueStatement(
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]238"INSERT INTO rows(indexed,unindexed,filler) VALUES(8,8,?)"));
239 insert.BindBlob(0, large_buffer);
240 ASSERT_TRUE(insert.Run());
241}
242
243 db_.Close();
244{
245// Zero out the last page of the database. This should be the overflow page
246// allocated for the last inserted row. So, deleting it should corrupt the
247// rows table.
248 base::File db_file(db_path_, base::File::FLAG_OPEN| base::File::FLAG_READ|
249 base::File::FLAG_WRITE);
250 ASSERT_TRUE(db_file.IsValid());
251int64_t db_size= db_file.GetLength();
252 ASSERT_GT(db_size, kDbPageSize)
253<<"The database should have multiple pages";
254 ASSERT_TRUE(db_file.SetLength(db_size- kDbPageSize));
255}
256
257{
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]258 test::ScopedErrorExpecter expecter;
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]259 expecter.ExpectError(SQLITE_CORRUPT);
Dan McArdle4feabeb52024-01-03 15:17:13[diff] [blame]260 ASSERT_FALSE(Reopen());
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]261 EXPECT_TRUE(expecter.SawExpectedErrors());
262// PRAGMAs executed inside Database::Open() will error out.
263}
264
265int error= SQLITE_OK;
266 db_.set_error_callback(
267 base::BindLambdaForTesting([&](int sqlite_error,Statement* statement){
268 error= sqlite_error;
269
270// Recovery::Begin() does not support a pre-existing error callback.
271 db_.reset_error_callback();
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]272
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]273 EXPECT_EQ(
274Recovery::RecoverDatabase(&db_,Recovery::Strategy::kRecoverOrRaze),
275SqliteResultCode::kOk);
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]276}));
277
278// SUM(unindexed) heavily nudges SQLite to use the table instead of the index.
279staticconstchar kUnindexedCountSql[]="SELECT SUM(unindexed) FROM rows";
280 EXPECT_FALSE(db_.Execute(kUnindexedCountSql))
281<<"Table scan on corrupt table should fail";
282 EXPECT_EQ(SQLITE_CORRUPT, error)
283<<"Error callback should be called during scan on corrupt index";
284
285 ASSERT_TRUE(Reopen());
286
287// All rows should be recovered. Only the BLOB in the last row was damaged.
288 EXPECT_EQ("15",ExecuteWithResult(&db_, kUnindexedCountSql))
289<<"Table should survive database recovery";
290staticconstchar kIndexedCountSql[]=
291"SELECT SUM(indexed) FROM rows INDEXED BY rows_index";
292 EXPECT_EQ("15",ExecuteWithResult(&db_, kIndexedCountSql))
293<<"Index should be reconstructed during database recovery";
shess@chromium.orgdd325f052013-08-06 02:37:40[diff] [blame]294}
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]295
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]296TEST_P(SqlRecoveryTest,Meta){
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]297constint kVersion=3;
298constint kCompatibleVersion=2;
299
300{
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]301MetaTable meta;
302 EXPECT_TRUE(meta.Init(&db_, kVersion, kCompatibleVersion));
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]303 EXPECT_EQ(kVersion, meta.GetVersionNumber());
304}
305
306// Test expected case where everything works.
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]307 EXPECT_EQ(Recovery::RecoverDatabase(
308&db_,Recovery::Strategy::kRecoverWithMetaVersionOrRaze),
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]309SqliteResultCode::kOk);
310 histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName,
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]311Recovery::Result::kSuccess,
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]312/*expected_bucket_count=*/1);
313 histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
314SqliteLoggedResultCode::kNoError,
315/*expected_bucket_count=*/1);
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]316
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]317 ASSERT_TRUE(Reopen());// Handle was poisoned.
318
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]319 ASSERT_TRUE(db_.DoesTableExist("meta"));
320
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]321// Test version row missing.
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]322 EXPECT_TRUE(db_.Execute("DELETE FROM meta WHERE key = 'version'"));
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]323
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]324 EXPECT_EQ(Recovery::RecoverDatabase(
325&db_,Recovery::Strategy::kRecoverWithMetaVersionOrRaze),
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]326SqliteResultCode::kError);
327 histogram_tester_.ExpectBucketCount(
328 kRecoveryResultHistogramName,
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]329Recovery::Result::kFailedMetaTableVersionWasInvalid,
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]330/*expected_count=*/1);
331 histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
332SqliteLoggedResultCode::kNoError,
333/*expected_bucket_count=*/2);
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]334 ASSERT_TRUE(Reopen());// Handle was poisoned.
335
336// Test meta table missing.
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]337 ASSERT_FALSE(db_.DoesTableExist("meta"));
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]338
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]339 EXPECT_EQ(Recovery::RecoverDatabase(
340&db_,Recovery::Strategy::kRecoverWithMetaVersionOrRaze),
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]341SqliteResultCode::kError);
342 histogram_tester_.ExpectBucketCount(
343 kRecoveryResultHistogramName,
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]344Recovery::Result::kFailedMetaTableDoesNotExist,
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]345/*expected_count=*/1);
346 histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
347SqliteLoggedResultCode::kNoError,
348/*expected_bucket_count=*/3);
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]349}
350
351// Baseline AutoRecoverTable() test.
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]352TEST_P(SqlRecoveryTest,AutoRecoverTable){
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]353// BIGINT and VARCHAR to test type affinity.
Victor Costan1d868352018-06-26 19:06:48[diff] [blame]354staticconstchar kCreateSql[]=
355"CREATE TABLE x (id BIGINT, t TEXT, v VARCHAR)";
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]356 ASSERT_TRUE(db_.Execute(kCreateSql));
357 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (11, 'This is', 'a test')"));
358 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (5, 'That was', 'a test')"));
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]359
360// Save aside a copy of the original schema and data.
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]361const std::string orig_schema(GetSchema(&db_));
Victor Costan1d868352018-06-26 19:06:48[diff] [blame]362staticconstchar kXSql[]="SELECT * FROM x ORDER BY 1";
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]363const std::string orig_data(ExecuteWithResults(&db_, kXSql,"|","\n"));
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]364
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]365 EXPECT_EQ(Recovery::RecoverDatabase(&db_,Recovery::Strategy::kRecoverOrRaze),
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]366SqliteResultCode::kOk);
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]367
368// Since the database was not corrupt, the entire schema and all
369// data should be recovered.
370 ASSERT_TRUE(Reopen());
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]371 ASSERT_EQ(orig_schema,GetSchema(&db_));
372 ASSERT_EQ(orig_data,ExecuteWithResults(&db_, kXSql,"|","\n"));
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]373
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]374// Recovery succeeds silently, since there's nothing to do.
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]375 EXPECT_EQ(Recovery::RecoverDatabase(&db_,Recovery::Strategy::kRecoverOrRaze),
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]376SqliteResultCode::kOk);
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]377}
378
379// Test that default values correctly replace nulls. The recovery
380// virtual table reads directly from the database, so DEFAULT is not
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]381// interpreted at that level.
382TEST_P(SqlRecoveryTest,AutoRecoverTableWithDefault){
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]383 ASSERT_TRUE(db_.Execute("CREATE TABLE x (id INTEGER)"));
384 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (5)"));
385 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (15)"));
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]386
387// ALTER effectively leaves the new columns NULL in the first two
388// rows. The row with 17 will get the default injected at insert
389// time, while the row with 42 will get the actual value provided.
390// Embedded "'" to make sure default-handling continues to be quoted
391// correctly.
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]392 ASSERT_TRUE(db_.Execute("ALTER TABLE x ADD COLUMN t TEXT DEFAULT 'a''a'"));
393 ASSERT_TRUE(db_.Execute("ALTER TABLE x ADD COLUMN b BLOB DEFAULT x'AA55'"));
394 ASSERT_TRUE(db_.Execute("ALTER TABLE x ADD COLUMN i INT DEFAULT 93"));
395 ASSERT_TRUE(db_.Execute("INSERT INTO x (id) VALUES (17)"));
396 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (42, 'b', x'1234', 12)"));
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]397
398// Save aside a copy of the original schema and data.
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]399const std::string orig_schema(GetSchema(&db_));
Victor Costan1d868352018-06-26 19:06:48[diff] [blame]400staticconstchar kXSql[]="SELECT * FROM x ORDER BY 1";
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]401const std::string orig_data(ExecuteWithResults(&db_, kXSql,"|","\n"));
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]402
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]403 std::string final_schema(orig_schema);
404 std::string final_data(orig_data);
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]405 EXPECT_EQ(Recovery::RecoverDatabase(&db_,Recovery::Strategy::kRecoverOrRaze),
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]406SqliteResultCode::kOk);
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]407
408// Since the database was not corrupt, the entire schema and all
409// data should be recovered.
410 ASSERT_TRUE(Reopen());
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]411 ASSERT_EQ(final_schema,GetSchema(&db_));
412 ASSERT_EQ(final_data,ExecuteWithResults(&db_, kXSql,"|","\n"));
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]413}
414
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]415// Test AutoRecoverTable with a ROWID alias.
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]416TEST_P(SqlRecoveryTest,AutoRecoverTableWithRowid){
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]417// The rowid alias is almost always the first column, intentionally
418// put it later.
Victor Costan1d868352018-06-26 19:06:48[diff] [blame]419staticconstchar kCreateSql[]=
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]420"CREATE TABLE x (t TEXT, id INTEGER PRIMARY KEY NOT NULL)";
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]421 ASSERT_TRUE(db_.Execute(kCreateSql));
422 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('This is a test', NULL)"));
423 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('That was a test', NULL)"));
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]424
425// Save aside a copy of the original schema and data.
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]426const std::string orig_schema(GetSchema(&db_));
Victor Costan1d868352018-06-26 19:06:48[diff] [blame]427staticconstchar kXSql[]="SELECT * FROM x ORDER BY 1";
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]428const std::string orig_data(ExecuteWithResults(&db_, kXSql,"|","\n"));
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]429
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]430 EXPECT_EQ(Recovery::RecoverDatabase(&db_,Recovery::Strategy::kRecoverOrRaze),
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]431SqliteResultCode::kOk);
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]432
433// Since the database was not corrupt, the entire schema and all
434// data should be recovered.
435 ASSERT_TRUE(Reopen());
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]436 ASSERT_EQ(orig_schema,GetSchema(&db_));
437 ASSERT_EQ(orig_data,ExecuteWithResults(&db_, kXSql,"|","\n"));
kinuko@chromium.orga8848a72013-11-18 04:18:47[diff] [blame]438}
439
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]440voidTestRecoverDatabase(Database& db,
441const base::FilePath& db_path,
442bool with_meta,
443 base::OnceClosure run_recovery){
444constint kVersion=3;
445constint kCompatibleVersion=2;
446
447if(with_meta){
448MetaTable meta;
449 EXPECT_TRUE(meta.Init(&db, kVersion, kCompatibleVersion));
450 EXPECT_EQ(kVersion, meta.GetVersionNumber());
451 EXPECT_EQ(kCompatibleVersion, meta.GetCompatibleVersionNumber());
452}
453
shessa402e752016-07-02 00:25:11[diff] [blame]454// As a side effect, AUTOINCREMENT creates the sqlite_sequence table for
455// RecoverDatabase() to handle.
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]456 ASSERT_TRUE(db.Execute(
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]457"CREATE TABLE table1(id INTEGER PRIMARY KEY AUTOINCREMENT, value TEXT)"));
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]458 EXPECT_TRUE(db.Execute("INSERT INTO table1(value) VALUES('turtle')"));
459 EXPECT_TRUE(db.Execute("INSERT INTO table1(value) VALUES('truck')"));
460 EXPECT_TRUE(db.Execute("INSERT INTO table1(value) VALUES('trailer')"));
shessa402e752016-07-02 00:25:11[diff] [blame]461
462// This table needs index and a unique index to work.
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]463 ASSERT_TRUE(db.Execute("CREATE TABLE table2(name TEXT, value TEXT)"));
464 ASSERT_TRUE(db.Execute("CREATE UNIQUE INDEX table2_name ON table2(name)"));
465 ASSERT_TRUE(db.Execute("CREATE INDEX table2_value ON table2(value)"));
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]466 EXPECT_TRUE(
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]467 db.Execute("INSERT INTO table2(name, value) VALUES('jim', 'telephone')"));
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]468 EXPECT_TRUE(
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]469 db.Execute("INSERT INTO table2(name, value) VALUES('bob', 'truck')"));
470 EXPECT_TRUE(
471 db.Execute("INSERT INTO table2(name, value) VALUES('dean', 'trailer')"));
shessa402e752016-07-02 00:25:11[diff] [blame]472
shessa402e752016-07-02 00:25:11[diff] [blame]473// Save aside a copy of the original schema, verifying that it has the created
474// items plus the sqlite_sequence table.
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]475const std::string original_schema=GetSchema(&db);
Peter Kasting3f01b692025-01-27 19:50:47[diff] [blame]476 ASSERT_EQ(with_meta?6:4, std::ranges::count(original_schema,'\n'))
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]477<< original_schema;
shessa402e752016-07-02 00:25:11[diff] [blame]478
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]479staticconstexprchar kTable1Sql[]="SELECT * FROM table1 ORDER BY 1";
480staticconstexprchar kTable2Sql[]="SELECT * FROM table2 ORDER BY 1";
shessa402e752016-07-02 00:25:11[diff] [blame]481 EXPECT_EQ("1|turtle\n2|truck\n3|trailer",
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]482ExecuteWithResults(&db, kTable1Sql,"|","\n"));
shessa402e752016-07-02 00:25:11[diff] [blame]483 EXPECT_EQ("bob|truck\ndean|trailer\njim|telephone",
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]484ExecuteWithResults(&db, kTable2Sql,"|","\n"));
shessa402e752016-07-02 00:25:11[diff] [blame]485
486// Database handle is valid before recovery, poisoned after.
John Delaney86dbec62021-08-24 15:05:21[diff] [blame]487staticconstexprchar kTrivialSql[]="SELECT COUNT(*) FROM sqlite_schema";
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]488 EXPECT_TRUE(db.IsSQLValid(kTrivialSql));
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]489
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]490 std::move(run_recovery).Run();
491
492 EXPECT_FALSE(db.is_open());
shessa402e752016-07-02 00:25:11[diff] [blame]493
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]494// Since the database was not corrupt, the entire schema and all data should
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]495// be recovered. Re-open the database.
496 db.Close();
497 ASSERT_TRUE(db.Open(db_path));
498 ASSERT_EQ(original_schema,GetSchema(&db));
shessa402e752016-07-02 00:25:11[diff] [blame]499 EXPECT_EQ("1|turtle\n2|truck\n3|trailer",
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]500ExecuteWithResults(&db, kTable1Sql,"|","\n"));
shessa402e752016-07-02 00:25:11[diff] [blame]501 EXPECT_EQ("bob|truck\ndean|trailer\njim|telephone",
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]502ExecuteWithResults(&db, kTable2Sql,"|","\n"));
503
504if(with_meta){
505MetaTable meta;
506 EXPECT_TRUE(meta.Init(&db, kVersion, kCompatibleVersion));
507 EXPECT_EQ(kVersion, meta.GetVersionNumber());
508 EXPECT_EQ(kCompatibleVersion, meta.GetCompatibleVersionNumber());
509}
510}
511
512TEST_P(SqlRecoveryTest,RecoverDatabase){
513auto run_recovery= base::BindLambdaForTesting([&](){
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]514 EXPECT_EQ(
515Recovery::RecoverDatabase(&db_,Recovery::Strategy::kRecoverOrRaze),
516SqliteResultCode::kOk);
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]517});
518
519TestRecoverDatabase(db_, db_path_,/*with_meta=*/false,
520 std::move(run_recovery));
521}
522
523TEST_P(SqlRecoveryTest,RecoverDatabaseMeta){
524auto run_recovery= base::BindLambdaForTesting([&](){
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]525 EXPECT_EQ(Recovery::RecoverDatabase(
526&db_,Recovery::Strategy::kRecoverWithMetaVersionOrRaze),
527SqliteResultCode::kOk);
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]528});
529
530TestRecoverDatabase(db_, db_path_,/*with_meta=*/true,
531 std::move(run_recovery));
532}
533
534TEST_P(SqlRecoveryTest,RecoverIfPossible){
535auto run_recovery= base::BindLambdaForTesting([&](){
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]536 EXPECT_TRUE(Recovery::RecoverIfPossible(
537&db_, SQLITE_CORRUPT,Recovery::Strategy::kRecoverOrRaze));
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]538});
539
540TestRecoverDatabase(db_, db_path_,/*with_meta=*/false,
541 std::move(run_recovery));
542}
543
544TEST_P(SqlRecoveryTest,RecoverIfPossibleMeta){
545auto run_recovery= base::BindLambdaForTesting([&](){
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]546 EXPECT_TRUE(Recovery::RecoverIfPossible(
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]547&db_, SQLITE_CORRUPT,
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]548Recovery::Strategy::kRecoverWithMetaVersionOrRaze));
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]549});
550
551TestRecoverDatabase(db_, db_path_,/*with_meta=*/true,
552 std::move(run_recovery));
553}
554
Dan McArdle4feabeb52024-01-03 15:17:13[diff] [blame]555TEST_P(SqlRecoveryTest,RecoverIfPossibleWithoutErrorCallback){
556auto run_recovery= base::BindLambdaForTesting([&](){
557// `RecoverIfPossible()` should not set an error callback.
558 EXPECT_FALSE(db_.has_error_callback());
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]559bool recovery_was_attempted=Recovery::RecoverIfPossible(
Dan McArdle4feabeb52024-01-03 15:17:13[diff] [blame]560&db_, SQLITE_CORRUPT,
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]561Recovery::Strategy::kRecoverWithMetaVersionOrRaze);
Dan McArdle4feabeb52024-01-03 15:17:13[diff] [blame]562 EXPECT_TRUE(recovery_was_attempted);
563 EXPECT_FALSE(db_.has_error_callback());
564});
565
566TestRecoverDatabase(db_, db_path_,/*with_meta=*/true,
567 std::move(run_recovery));
568}
569
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]570TEST_P(SqlRecoveryTest,RecoverIfPossibleWithErrorCallback){
571auto run_recovery= base::BindLambdaForTesting([&](){
572 db_.set_error_callback(base::DoNothing());
573// The error callback should be reset during `RecoverIfPossible()` if
574// recovery was attempted.
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]575bool recovery_was_attempted=Recovery::RecoverIfPossible(
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]576&db_, SQLITE_CORRUPT,
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]577Recovery::Strategy::kRecoverWithMetaVersionOrRaze);
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]578 EXPECT_TRUE(recovery_was_attempted);
579 EXPECT_NE(db_.has_error_callback(), recovery_was_attempted);
580});
581
582TestRecoverDatabase(db_, db_path_,/*with_meta=*/true,
583 std::move(run_recovery));
584}
585
586TEST_P(SqlRecoveryTest,RecoverIfPossibleWithClosedDatabase){
587auto run_recovery= base::BindLambdaForTesting([&](){
588// Recovery should not be attempted on a closed database.
589 db_.Close();
590
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]591 EXPECT_FALSE(Recovery::RecoverIfPossible(
592&db_, SQLITE_CORRUPT,Recovery::Strategy::kRecoverOrRaze));
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]593});
594
595TestRecoverDatabase(db_, db_path_,/*with_meta=*/false,
596 std::move(run_recovery));
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]597}
598
Austin Sullivan7b543402023-10-07 00:55:09[diff] [blame]599TEST_P(SqlRecoveryTest,RecoverIfPossibleWithPerDatabaseUma){
600auto run_recovery= base::BindLambdaForTesting([&](){
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]601 EXPECT_TRUE(Recovery::RecoverIfPossible(
602&db_, SQLITE_CORRUPT,Recovery::Strategy::kRecoverOrRaze));
Austin Sullivan7b543402023-10-07 00:55:09[diff] [blame]603});
604
605TestRecoverDatabase(db_, db_path_,/*with_meta=*/false,
606 std::move(run_recovery));
607
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]608// Log to the overall histograms.
609 histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName,
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]610Recovery::Result::kSuccess,
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]611/*expected_bucket_count=*/1);
612 histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
613SqliteLoggedResultCode::kNoError,
614/*expected_bucket_count=*/1);
615// And the histograms for this specific feature.
616 histogram_tester_.ExpectUniqueSample(
Anthony Vallée-Duboisdd2d9012024-12-18 23:25:28[diff] [blame]617 base::StrCat({kRecoveryResultHistogramName,".", test::kTestTag.value}),
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]618Recovery::Result::kSuccess,
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]619/*expected_bucket_count=*/1);
620 histogram_tester_.ExpectUniqueSample(
Anthony Vallée-Duboisdd2d9012024-12-18 23:25:28[diff] [blame]621 base::StrCat(
622{kRecoveryResultCodeHistogramName,".", test::kTestTag.value}),
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]623SqliteLoggedResultCode::kNoError,
624/*expected_bucket_count=*/1);
Austin Sullivan7b543402023-10-07 00:55:09[diff] [blame]625}
626
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]627TEST_P(SqlRecoveryTest,RecoverDatabaseWithView){
Victor Costanfe078f92021-07-19 20:02:59[diff] [blame]628 db_.Close();
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]629Database db(DatabaseOptions().set_enable_views_discouraged(true),
630 test::kTestTag);
Victor Costanfe078f92021-07-19 20:02:59[diff] [blame]631 ASSERT_TRUE(db.Open(db_path_));
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]632
Victor Costanfe078f92021-07-19 20:02:59[diff] [blame]633 ASSERT_TRUE(db.Execute(
634"CREATE TABLE table1(id INTEGER PRIMARY KEY AUTOINCREMENT, value TEXT)"));
635 EXPECT_TRUE(db.Execute("INSERT INTO table1(value) VALUES('turtle')"));
636 EXPECT_TRUE(db.Execute("INSERT INTO table1(value) VALUES('truck')"));
637 EXPECT_TRUE(db.Execute("INSERT INTO table1(value) VALUES('trailer')"));
638
639 ASSERT_TRUE(db.Execute("CREATE TABLE table2(name TEXT, value TEXT)"));
640 ASSERT_TRUE(db.Execute("CREATE UNIQUE INDEX table2_name ON table2(name)"));
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]641 EXPECT_TRUE(
Victor Costanfe078f92021-07-19 20:02:59[diff] [blame]642 db.Execute("INSERT INTO table2(name, value) VALUES('jim', 'telephone')"));
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]643 EXPECT_TRUE(
Victor Costanfe078f92021-07-19 20:02:59[diff] [blame]644 db.Execute("INSERT INTO table2(name, value) VALUES('bob', 'truck')"));
645 EXPECT_TRUE(
646 db.Execute("INSERT INTO table2(name, value) VALUES('dean', 'trailer')"));
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]647
648// View which is the intersection of [table1.value] and [table2.value].
Victor Costanfe078f92021-07-19 20:02:59[diff] [blame]649 ASSERT_TRUE(db.Execute(
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]650"CREATE VIEW view_table12 AS SELECT table1.value FROM table1, table2 "
651"WHERE table1.value = table2.value"));
652
653staticconstexprchar kViewSql[]="SELECT * FROM view_table12 ORDER BY 1";
Victor Costanfe078f92021-07-19 20:02:59[diff] [blame]654 EXPECT_EQ("trailer\ntruck",ExecuteWithResults(&db, kViewSql,"|","\n"));
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]655
656// Save aside a copy of the original schema, verifying that it has the created
657// items plus the sqlite_sequence table.
Victor Costanfe078f92021-07-19 20:02:59[diff] [blame]658const std::string original_schema=GetSchema(&db);
Peter Kasting3f01b692025-01-27 19:50:47[diff] [blame]659 ASSERT_EQ(4, std::ranges::count(original_schema,'\n'))<< original_schema;
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]660
661// Database handle is valid before recovery, poisoned after.
John Delaney86dbec62021-08-24 15:05:21[diff] [blame]662staticconstexprchar kTrivialSql[]="SELECT COUNT(*) FROM sqlite_schema";
Victor Costanfe078f92021-07-19 20:02:59[diff] [blame]663 EXPECT_TRUE(db.IsSQLValid(kTrivialSql));
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]664 EXPECT_EQ(Recovery::RecoverDatabase(&db,Recovery::Strategy::kRecoverOrRaze),
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]665SqliteResultCode::kOk);
Victor Costanfe078f92021-07-19 20:02:59[diff] [blame]666 EXPECT_FALSE(db.IsSQLValid(kTrivialSql));
Victor Costan95848c02021-07-17 00:06:29[diff] [blame]667
668// Since the database was not corrupt, the entire schema and all data should
669// be recovered.
Victor Costanfe078f92021-07-19 20:02:59[diff] [blame]670 db.Close();
671 ASSERT_TRUE(db.Open(db_path_));
672 EXPECT_EQ("trailer\ntruck",ExecuteWithResults(&db, kViewSql,"|","\n"));
shessa402e752016-07-02 00:25:11[diff] [blame]673}
674
shess00d65d42017-03-02 21:12:19[diff] [blame]675// When RecoverDatabase() encounters SQLITE_NOTADB, the database is deleted.
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]676TEST_P(SqlRecoveryTest,RecoverDatabaseDelete){
shess00d65d42017-03-02 21:12:19[diff] [blame]677// Create a valid database, then write junk over the header. This should lead
678// to SQLITE_NOTADB, which will cause ATTACH to fail.
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]679 ASSERT_TRUE(db_.Execute("CREATE TABLE x (t TEXT)"));
680 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('This is a test')"));
681 db_.Close();
682 ASSERT_TRUE(OverwriteDatabaseHeader());
shess00d65d42017-03-02 21:12:19[diff] [blame]683
684{
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]685 test::ScopedErrorExpecter expecter;
shess00d65d42017-03-02 21:12:19[diff] [blame]686 expecter.ExpectError(SQLITE_NOTADB);
687
688// Reopen() here because it will see SQLITE_NOTADB.
Dan McArdle4feabeb52024-01-03 15:17:13[diff] [blame]689 ASSERT_FALSE(Reopen());
shess00d65d42017-03-02 21:12:19[diff] [blame]690
691// This should "recover" the database by making it valid, but empty.
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]692 EXPECT_EQ(
693Recovery::RecoverDatabase(&db_,Recovery::Strategy::kRecoverOrRaze),
694SqliteResultCode::kNotADatabase);
695 histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName,
696Recovery::Result::kFailedRecoveryRun,
697/*expected_bucket_count=*/1);
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]698 histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
699SqliteLoggedResultCode::kNotADatabase,
700/*expected_bucket_count=*/1);
shess00d65d42017-03-02 21:12:19[diff] [blame]701 ASSERT_TRUE(expecter.SawExpectedErrors());
702}
703
704// Recovery poisoned the handle, must re-open.
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]705 db_.Close();
shess00d65d42017-03-02 21:12:19[diff] [blame]706 ASSERT_TRUE(Reopen());
707
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]708 EXPECT_EQ("",GetSchema(&db_));
shess00d65d42017-03-02 21:12:19[diff] [blame]709}
710
shess5207e1452017-04-12 23:55:33[diff] [blame]711// Allow callers to validate the database between recovery and commit.
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]712TEST_P(SqlRecoveryTest,BeginRecoverDatabase){
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]713staticconstchar kCreateTable[]=
714"CREATE TABLE rows(indexed INTEGER NOT NULL, unindexed INTEGER NOT NULL)";
715 ASSERT_TRUE(db_.Execute(kCreateTable));
shess5207e1452017-04-12 23:55:33[diff] [blame]716
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]717 ASSERT_TRUE(db_.Execute("CREATE UNIQUE INDEX rows_index ON rows(indexed)"));
718
719// Populate the table with powers of two. These numbers make it easy to see if
720// SUM() missed a row.
721 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(1, 1)"));
722 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(2, 2)"));
723 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(4, 4)"));
724 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(8, 8)"));
725
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]726 db_.Close();
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]727 ASSERT_TRUE(test::CorruptIndexRootPage(db_path_,"rows_index"));
shess5207e1452017-04-12 23:55:33[diff] [blame]728 ASSERT_TRUE(Reopen());
729
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]730staticconstchar kIndexedCountSql[]=
731"SELECT SUM(indexed) FROM rows INDEXED BY rows_index";
732{
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]733 test::ScopedErrorExpecter expecter;
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]734 expecter.ExpectError(SQLITE_CORRUPT);
735 EXPECT_EQ("",ExecuteWithResult(&db_, kIndexedCountSql))
736<<"Index should still be corrupted after recovery rollback";
737 EXPECT_TRUE(expecter.SawExpectedErrors())
738<<"Index should still be corrupted after recovery rollback";
739}
740
741// Run recovery code, then commit. The index is recovered.
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]742 EXPECT_EQ(Recovery::RecoverDatabase(&db_,Recovery::Strategy::kRecoverOrRaze),
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]743SqliteResultCode::kOk);
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]744 db_.Close();
shess5207e1452017-04-12 23:55:33[diff] [blame]745 ASSERT_TRUE(Reopen());
Victor Costan63b2c4c42022-02-26 03:43:44[diff] [blame]746
747 EXPECT_EQ("15",ExecuteWithResult(&db_, kIndexedCountSql))
748<<"Index should be reconstructed after database recovery";
shess5207e1452017-04-12 23:55:33[diff] [blame]749}
750
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]751TEST_P(SqlRecoveryTest,AttachFailure){
shess63188112016-08-27 10:26:23[diff] [blame]752// Create a valid database, then write junk over the header. This should lead
753// to SQLITE_NOTADB, which will cause ATTACH to fail.
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]754 ASSERT_TRUE(db_.Execute("CREATE TABLE x (t TEXT)"));
755 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('This is a test')"));
756 db_.Close();
757 ASSERT_TRUE(OverwriteDatabaseHeader());
shess63188112016-08-27 10:26:23[diff] [blame]758
shess63188112016-08-27 10:26:23[diff] [blame]759{
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]760 test::ScopedErrorExpecter expecter;
shess63188112016-08-27 10:26:23[diff] [blame]761 expecter.ExpectError(SQLITE_NOTADB);
762
763// Reopen() here because it will see SQLITE_NOTADB.
Dan McArdle4feabeb52024-01-03 15:17:13[diff] [blame]764 ASSERT_FALSE(Reopen());
shess63188112016-08-27 10:26:23[diff] [blame]765
766// Begin() should fail.
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]767 EXPECT_EQ(
768Recovery::RecoverDatabase(&db_,Recovery::Strategy::kRecoverOrRaze),
769SqliteResultCode::kNotADatabase);
770 histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName,
771Recovery::Result::kFailedRecoveryRun,
772/*expected_bucket_count=*/1);
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]773 histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
774SqliteLoggedResultCode::kNotADatabase,
775/*expected_bucket_count=*/1);
shess63188112016-08-27 10:26:23[diff] [blame]776 ASSERT_TRUE(expecter.SawExpectedErrors());
777}
shess63188112016-08-27 10:26:23[diff] [blame]778}
779
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]780// Helper for SqlRecoveryTest.PageSize. Creates a fresh db based on db_prefix,
shess0b8d5932016-10-27 19:54:12[diff] [blame]781// with the given initial page size, and verifies it against the expected size.
782// Then changes to the final page size and recovers, verifying that the
783// recovered database ends up with the expected final page size.
784voidTestPageSize(const base::FilePath& db_prefix,
785int initial_page_size,
786const std::string& expected_initial_page_size,
787int final_page_size,
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]788const std::string& expected_final_page_size){
Victor Costan1d868352018-06-26 19:06:48[diff] [blame]789staticconstchar kCreateSql[]="CREATE TABLE x (t TEXT)";
790staticconstchar kInsertSql1[]="INSERT INTO x VALUES ('This is a test')";
791staticconstchar kInsertSql2[]="INSERT INTO x VALUES ('That was a test')";
792staticconstchar kSelectSql[]="SELECT * FROM x ORDER BY t";
shess0b8d5932016-10-27 19:54:12[diff] [blame]793
794const base::FilePath db_path= db_prefix.InsertBeforeExtensionASCII(
Raul Tambre6c708e32019-02-08 22:35:14[diff] [blame]795 base::NumberToString(initial_page_size));
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]796Database::Delete(db_path);
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]797Database db{DatabaseOptions().set_page_size(initial_page_size),
798 test::kTestTag};
shess0b8d5932016-10-27 19:54:12[diff] [blame]799 ASSERT_TRUE(db.Open(db_path));
800 ASSERT_TRUE(db.Execute(kCreateSql));
801 ASSERT_TRUE(db.Execute(kInsertSql1));
802 ASSERT_TRUE(db.Execute(kInsertSql2));
803 ASSERT_EQ(expected_initial_page_size,
804ExecuteWithResult(&db,"PRAGMA page_size"));
shess0b8d5932016-10-27 19:54:12[diff] [blame]805 db.Close();
806
Shubham Aggarwal7b60fe6e2020-10-15 06:00:28[diff] [blame]807// Re-open the database while setting a new |options.page_size| in the object.
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]808Database recover_db(DatabaseOptions().set_page_size(final_page_size),
809 test::kTestTag);
Shubham Aggarwal7b60fe6e2020-10-15 06:00:28[diff] [blame]810 ASSERT_TRUE(recover_db.Open(db_path));
811// Recovery will use the page size set in the database object, which may not
812// match the file's page size.
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]813 EXPECT_EQ(Recovery::RecoverDatabase(&recover_db,
814Recovery::Strategy::kRecoverOrRaze),
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]815SqliteResultCode::kOk);
Shubham Aggarwal7b60fe6e2020-10-15 06:00:28[diff] [blame]816
817// Recovery poisoned the handle, must re-open.
818 recover_db.Close();
819
shess0b8d5932016-10-27 19:54:12[diff] [blame]820// Make sure the page size is read from the file.
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]821Database recovered_db(test::kTestTag);
Shubham Aggarwal7b60fe6e2020-10-15 06:00:28[diff] [blame]822 ASSERT_TRUE(recovered_db.Open(db_path));
shess0b8d5932016-10-27 19:54:12[diff] [blame]823 ASSERT_EQ(expected_final_page_size,
Shubham Aggarwal7b60fe6e2020-10-15 06:00:28[diff] [blame]824ExecuteWithResult(&recovered_db,"PRAGMA page_size"));
shess0b8d5932016-10-27 19:54:12[diff] [blame]825 EXPECT_EQ("That was a test\nThis is a test",
Shubham Aggarwal7b60fe6e2020-10-15 06:00:28[diff] [blame]826ExecuteWithResults(&recovered_db, kSelectSql,"|","\n"));
shess0b8d5932016-10-27 19:54:12[diff] [blame]827}
828
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]829// Verify that Recovery maintains the page size, and the virtual table
shess0b8d5932016-10-27 19:54:12[diff] [blame]830// works with page sizes other than SQLite's default. Also verify the case
831// where the default page size has changed.
Austin Sullivan0593ef92023-05-17 20:46:30[diff] [blame]832TEST_P(SqlRecoveryTest,PageSize){
shess0b8d5932016-10-27 19:54:12[diff] [blame]833const std::string default_page_size=
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]834ExecuteWithResult(&db_,"PRAGMA page_size");
shess0b8d5932016-10-27 19:54:12[diff] [blame]835
Victor Costan7f6abbbe2018-07-29 02:57:27[diff] [blame]836// Check the default page size first.
837 EXPECT_NO_FATAL_FAILURE(TestPageSize(
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]838 db_path_,DatabaseOptions::kDefaultPageSize, default_page_size,
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]839DatabaseOptions::kDefaultPageSize, default_page_size));
shess0b8d5932016-10-27 19:54:12[diff] [blame]840
Victor Costan7f6abbbe2018-07-29 02:57:27[diff] [blame]841// Sync uses 32k pages.
shess0b8d5932016-10-27 19:54:12[diff] [blame]842 EXPECT_NO_FATAL_FAILURE(
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]843TestPageSize(db_path_,32768,"32768",32768,"32768"));
shess0b8d5932016-10-27 19:54:12[diff] [blame]844
845// Many clients use 4k pages. This is the SQLite default after 3.12.0.
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]846 EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_,4096,"4096",4096,"4096"));
shess0b8d5932016-10-27 19:54:12[diff] [blame]847
848// 1k is the default page size before 3.12.0.
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]849 EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_,1024,"1024",1024,"1024"));
shess0b8d5932016-10-27 19:54:12[diff] [blame]850
shess0b8d5932016-10-27 19:54:12[diff] [blame]851 ASSERT_NE("2048", default_page_size);
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]852// Databases with no page size specified should recover to the page size of
853// the source database.
854 EXPECT_NO_FATAL_FAILURE(TestPageSize(
855 db_path_,2048,"2048",DatabaseOptions::kDefaultPageSize,"2048"));
shess0b8d5932016-10-27 19:54:12[diff] [blame]856}
857
Austin Sullivan2fbe496e2023-05-18 19:48:52[diff] [blame]858TEST_P(SqlRecoveryTest,CannotRecoverClosedDb){
859 db_.Close();
860
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]861 EXPECT_CHECK_DEATH(std::ignore=Recovery::RecoverDatabase(
862&db_,Recovery::Strategy::kRecoverOrRaze));
Austin Sullivan2fbe496e2023-05-18 19:48:52[diff] [blame]863}
864
865TEST_P(SqlRecoveryTest,CannotRecoverDbWithErrorCallback){
866 db_.set_error_callback(base::DoNothing());
867
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]868 EXPECT_CHECK_DEATH(std::ignore=Recovery::RecoverDatabase(
869&db_,Recovery::Strategy::kRecoverOrRaze));
Austin Sullivan2fbe496e2023-05-18 19:48:52[diff] [blame]870}
871
Alison Gale71bd8f152024-04-26 22:38:20[diff] [blame]872// TODO(crbug.com/40199997): Ideally this would be a
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]873// `SqlRecoveryTest`, but `Recovery::RecoverDatabase()` does not DCHECK
874// that it is passed a non-null database pointer and will instead likely result
875// in unexpected behavior or crashes.
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]876TEST_P(SqlRecoveryTest,CannotRecoverNullDb){
Peter Boström59a246e82025-03-17 03:55:33[diff] [blame]877// TODO(pbos): Consider consolidating these so that DCHECK builds crash in the
878// same spot. Probably either by upgrading DCHECKs to CHECKs, or if feasible
879// by setting up the test to make it past failing DCHECKs to the expected
880// CHECK.
881if(DCHECK_IS_ON()){
882 EXPECT_DCHECK_DEATH(std::ignore=Recovery::RecoverDatabase(
883nullptr,Recovery::Strategy::kRecoverOrRaze));
884}else{
885 EXPECT_CHECK_DEATH(std::ignore=Recovery::RecoverDatabase(
886nullptr,Recovery::Strategy::kRecoverOrRaze));
887}
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]888}
889
Alison Gale71bd8f152024-04-26 22:38:20[diff] [blame]890// TODO(crbug.com/40199997): Ideally this would be a
Austin Sullivan265c201b2023-05-31 02:04:26[diff] [blame]891// `SqlRecoveryTest`, but `Recovery::RecoverDatabase()` does not DCHECK
892// whether the database is in-memory and will instead likely result in
893// unexpected behavior or crashes.
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]894TEST_P(SqlRecoveryTest,CannotRecoverInMemoryDb){
Anthony Vallée-Duboise3c94912024-12-12 16:47:47[diff] [blame]895Database in_memory_db(test::kTestTag);
Austin Sullivan2fbe496e2023-05-18 19:48:52[diff] [blame]896 ASSERT_TRUE(in_memory_db.OpenInMemory());
897
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]898 EXPECT_CHECK_DEATH(std::ignore=Recovery::RecoverDatabase(
899&in_memory_db,Recovery::Strategy::kRecoverOrRaze));
Austin Sullivan2fbe496e2023-05-18 19:48:52[diff] [blame]900}
901
Austin Sullivanbfea855f2024-01-09 21:40:20[diff] [blame]902// This test mimics the case where a database that was using WAL mode crashed,
903// then next Chrome launch the database is not opened in WAL mode. This may
904// occur when e.g. WAL mode if configured via Finch and the user not in the
905// experiment group on the second launch of Chrome.
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]906TEST_P(SqlRecoveryTest, PRE_RecoverFormerlyWalDbAfterCrash){
Austin Sullivanbfea855f2024-01-09 21:40:20[diff] [blame]907 base::FilePath wal_db_path=
908 temp_dir_.GetPath().AppendASCII("recovery_wal_test.sqlite");
909
910// Open the DB in WAL mode to set journal_mode="wal".
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]911Database wal_db{DatabaseOptions().set_wal_mode(true), test::kTestTag};
Austin Sullivanbfea855f2024-01-09 21:40:20[diff] [blame]912 ASSERT_TRUE(wal_db.Open(wal_db_path));
913
914 EXPECT_TRUE(wal_db.UseWALMode());
915 EXPECT_EQ(ExecuteWithResult(&wal_db,"PRAGMA journal_mode"),"wal");
916
917// Crash the database somehow, foregoing the opportunity for any cleanup.
918 wal_db.set_error_callback(base::DoNothing());
919 EXPECT_DCHECK_DEATH(wal_db.set_error_callback(base::DoNothing()));
920}
921
Evan Stadeb3be6372024-02-27 21:11:09[diff] [blame]922TEST_P(SqlRecoveryTest,RecoverFormerlyWalDbAfterCrash){
Austin Sullivanbfea855f2024-01-09 21:40:20[diff] [blame]923 base::FilePath wal_db_path=
924 temp_dir_.GetPath().AppendASCII("recovery_wal_test.sqlite");
925
Anthony Vallée-Dubois9adf0bb2025-02-03 17:01:41[diff] [blame]926Database non_wal_db{DatabaseOptions().set_wal_mode(false), test::kTestTag};
Austin Sullivanbfea855f2024-01-09 21:40:20[diff] [blame]927 ASSERT_TRUE(non_wal_db.Open(wal_db_path));
928
929auto run_recovery= base::BindLambdaForTesting([&](){
Evan Stade4c7d6b32024-03-12 16:50:27[diff] [blame]930 EXPECT_EQ(
931Recovery::RecoverDatabase(
932&non_wal_db,Recovery::Strategy::kRecoverWithMetaVersionOrRaze),
933SqliteResultCode::kOk);
Austin Sullivanbfea855f2024-01-09 21:40:20[diff] [blame]934});
935
936TestRecoverDatabase(non_wal_db, wal_db_path,/*with_meta=*/true,
937 std::move(run_recovery));
938}
939
shess@chromium.org8d409412013-07-19 18:25:30[diff] [blame]940}// namespace
Victor Costan49a903a2021-05-07 22:21:00[diff] [blame]941
942}// namespace sql

[8]ページ先頭

©2009-2025 Movatter.jp