1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sql/recovery.h"
13 #include "base/bind.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/files/scoped_temp_dir.h"
17 #include "base/path_service.h"
18 #include "base/ranges/algorithm.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/test/bind.h"
21 #include "sql/database.h"
22 #include "sql/meta_table.h"
23 #include "sql/statement.h"
24 #include "sql/test/paths.h"
25 #include "sql/test/scoped_error_expecter.h"
26 #include "sql/test/test_helpers.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "third_party/sqlite/sqlite3.h"
34 using sql::test::ExecuteWithResult;
35 using sql::test::ExecuteWithResults;
37 // Dump consistent human-readable representation of the database
38 // schema. For tables or indices, this will contain the sql command
39 // to create the table or index. For certain automatic SQLite
40 // structures with no sql, the name is used.
41 std::string GetSchema(Database* db) {
42 static const char kSql[] =
43 "SELECT COALESCE(sql, name) FROM sqlite_schema ORDER BY 1";
44 return ExecuteWithResults(db, kSql, "|", "\n");
47 class SQLRecoveryTest : public testing::Test {
49 ~SQLRecoveryTest() override = default;
51 void SetUp() override {
52 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
53 db_path_ = temp_dir_.GetPath().AppendASCII("recovery_test.sqlite");
54 ASSERT_TRUE(db_.Open(db_path_));
59 return db_.Open(db_path_);
62 bool OverwriteDatabaseHeader() {
63 base::File file(db_path_,
64 base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
65 static constexpr char kText[] = "Now is the winter of our discontent.";
66 constexpr int kTextBytes = sizeof(kText) - 1;
67 return file.Write(0, kText, kTextBytes) == kTextBytes;
71 base::ScopedTempDir temp_dir_;
72 base::FilePath db_path_;
76 // Baseline Recovery test covering the different ways to dispose of the
77 // scoped pointer received from Recovery::Begin().
78 TEST_F(SQLRecoveryTest, RecoverBasic) {
79 static const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
80 static const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')";
81 static const char kAltInsertSql[] =
82 "INSERT INTO x VALUES ('That was a test')";
83 ASSERT_TRUE(db_.Execute(kCreateSql));
84 ASSERT_TRUE(db_.Execute(kInsertSql));
85 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_));
87 // If the Recovery handle goes out of scope without being
88 // Recovered(), the database is razed.
90 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
91 ASSERT_TRUE(recovery.get());
93 EXPECT_FALSE(db_.is_open());
94 ASSERT_TRUE(Reopen());
95 EXPECT_TRUE(db_.is_open());
96 ASSERT_EQ("", GetSchema(&db_));
98 // Recreate the database.
99 ASSERT_TRUE(db_.Execute(kCreateSql));
100 ASSERT_TRUE(db_.Execute(kInsertSql));
101 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_));
103 // Unrecoverable() also razes.
105 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
106 ASSERT_TRUE(recovery.get());
107 Recovery::Unrecoverable(std::move(recovery));
109 // TODO(shess): Test that calls to recover.db_ start failing.
111 EXPECT_FALSE(db_.is_open());
112 ASSERT_TRUE(Reopen());
113 EXPECT_TRUE(db_.is_open());
114 ASSERT_EQ("", GetSchema(&db_));
116 // Attempting to recover a previously-recovered handle fails early.
118 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
119 ASSERT_TRUE(recovery.get());
122 recovery = Recovery::Begin(&db_, db_path_);
123 ASSERT_FALSE(recovery.get());
125 ASSERT_TRUE(Reopen());
127 // Recreate the database.
128 ASSERT_TRUE(db_.Execute(kCreateSql));
129 ASSERT_TRUE(db_.Execute(kInsertSql));
130 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_));
132 // Unrecovered table to distinguish from recovered database.
133 ASSERT_TRUE(db_.Execute("CREATE TABLE y (c INTEGER)"));
134 ASSERT_NE("CREATE TABLE x (t TEXT)", GetSchema(&db_));
136 // Recovered() replaces the original with the "recovered" version.
138 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
139 ASSERT_TRUE(recovery.get());
141 // Create the new version of the table.
142 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
144 // Insert different data to distinguish from original database.
145 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql));
147 // Successfully recovered.
148 ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
150 EXPECT_FALSE(db_.is_open());
151 ASSERT_TRUE(Reopen());
152 EXPECT_TRUE(db_.is_open());
153 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_));
155 const char* kXSql = "SELECT * FROM x ORDER BY 1";
156 ASSERT_EQ("That was a test", ExecuteWithResult(&db_, kXSql));
158 // Reset the database contents.
159 ASSERT_TRUE(db_.Execute("DELETE FROM x"));
160 ASSERT_TRUE(db_.Execute(kInsertSql));
162 // Rollback() discards recovery progress and leaves the database as it was.
164 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
165 ASSERT_TRUE(recovery.get());
167 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
168 ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql));
170 Recovery::Rollback(std::move(recovery));
172 EXPECT_FALSE(db_.is_open());
173 ASSERT_TRUE(Reopen());
174 EXPECT_TRUE(db_.is_open());
175 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_));
177 ASSERT_EQ("This is a test", ExecuteWithResult(&db_, kXSql));
180 // Test operation of the virtual table used by Recovery.
181 TEST_F(SQLRecoveryTest, VirtualTable) {
182 static const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
183 ASSERT_TRUE(db_.Execute(kCreateSql));
184 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('This is a test')"));
185 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('That was a test')"));
187 // Successfully recover the database.
189 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
191 // Tables to recover original DB, now at [corrupt].
192 static const char kRecoveryCreateSql[] =
193 "CREATE VIRTUAL TABLE temp.recover_x using recover("
197 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql));
199 // Re-create the original schema.
200 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
202 // Copy the data from the recovery tables to the new database.
203 static const char kRecoveryCopySql[] =
204 "INSERT INTO x SELECT t FROM recover_x";
205 ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql));
207 // Successfully recovered.
208 ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
211 // Since the database was not corrupt, the entire schema and all
212 // data should be recovered.
213 ASSERT_TRUE(Reopen());
214 ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db_));
216 static const char* kXSql = "SELECT * FROM x ORDER BY 1";
217 ASSERT_EQ("That was a test\nThis is a test",
218 ExecuteWithResults(&db_, kXSql, "|", "\n"));
221 // Our corruption handling assumes that a corrupt index doesn't impact
222 // SQL statements that only operate on the associated table. This test verifies
224 TEST_F(SQLRecoveryTest, TableIndependentFromCorruptIndex) {
225 static const char kCreateTable[] =
226 "CREATE TABLE rows(indexed INTEGER NOT NULL, unindexed INTEGER NOT NULL)";
227 ASSERT_TRUE(db_.Execute(kCreateTable));
228 ASSERT_TRUE(db_.Execute("CREATE UNIQUE INDEX rows_index ON rows(indexed)"));
230 // Populate the table with powers of two. These numbers make it easy to see if
231 // SUM() missed a row.
232 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(1, 1)"));
233 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(2, 2)"));
234 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(4, 4)"));
235 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(8, 8)"));
237 // SQL statement that performs a table scan. SUM(unindexed) heavily nudges
238 // SQLite to use the table instead of the index.
239 static const char kUnindexedCountSql[] = "SELECT SUM(unindexed) FROM rows";
240 EXPECT_EQ("15", ExecuteWithResult(&db_, kUnindexedCountSql))
241 << "No SQL statement should fail before corruption";
243 // SQL statement that performs an index scan.
244 static const char kIndexedCountSql[] =
245 "SELECT SUM(indexed) FROM rows INDEXED BY rows_index";
246 EXPECT_EQ("15", ExecuteWithResult(&db_, kIndexedCountSql))
247 << "Table scan should not fail due to corrupt index";
250 ASSERT_TRUE(sql::test::CorruptIndexRootPage(db_path_, "rows_index"));
251 ASSERT_TRUE(Reopen());
254 sql::test::ScopedErrorExpecter expecter;
255 expecter.ExpectError(SQLITE_CORRUPT);
256 EXPECT_FALSE(db_.Execute(kIndexedCountSql))
257 << "Index scan on corrupt index should fail";
258 EXPECT_TRUE(expecter.SawExpectedErrors())
259 << "Index scan on corrupt index should fail";
262 EXPECT_EQ("15", ExecuteWithResult(&db_, kUnindexedCountSql))
263 << "Table scan should not fail due to corrupt index";
266 TEST_F(SQLRecoveryTest, RecoverCorruptIndex) {
267 static const char kCreateTable[] =
268 "CREATE TABLE rows(indexed INTEGER NOT NULL, unindexed INTEGER NOT NULL)";
269 ASSERT_TRUE(db_.Execute(kCreateTable));
271 static const char kCreateIndex[] =
272 "CREATE UNIQUE INDEX rows_index ON rows(indexed)";
273 ASSERT_TRUE(db_.Execute(kCreateIndex));
275 // Populate the table with powers of two. These numbers make it easy to see if
276 // SUM() missed a row.
277 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(1, 1)"));
278 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(2, 2)"));
279 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(4, 4)"));
280 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(8, 8)"));
283 ASSERT_TRUE(sql::test::CorruptIndexRootPage(db_path_, "rows_index"));
284 ASSERT_TRUE(Reopen());
286 int error = SQLITE_OK;
287 db_.set_error_callback(
288 base::BindLambdaForTesting([&](int sqlite_error, Statement* statement) {
289 error = sqlite_error;
291 // Recovery::Begin() does not support a pre-existing error callback.
292 db_.reset_error_callback();
293 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
294 ASSERT_TRUE(recovery.get());
296 ASSERT_TRUE(recovery->db()->Execute(kCreateTable));
297 ASSERT_TRUE(recovery->db()->Execute(kCreateIndex));
300 ASSERT_TRUE(recovery->AutoRecoverTable("rows", &rows));
301 ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
304 // SUM(unindexed) heavily nudges SQLite to use the table instead of the index.
305 static const char kUnindexedCountSql[] = "SELECT SUM(unindexed) FROM rows";
306 EXPECT_EQ("15", ExecuteWithResult(&db_, kUnindexedCountSql))
307 << "Table scan should not fail due to corrupt index";
308 EXPECT_EQ(SQLITE_OK, error)
309 << "Successful statement execution should not invoke the error callback";
311 static const char kIndexedCountSql[] =
312 "SELECT SUM(indexed) FROM rows INDEXED BY rows_index";
313 EXPECT_EQ("", ExecuteWithResult(&db_, kIndexedCountSql))
314 << "Index scan on corrupt index should fail";
315 EXPECT_EQ(SQLITE_CORRUPT, error)
316 << "Error callback should be called during scan on corrupt index";
318 EXPECT_EQ("", ExecuteWithResult(&db_, kUnindexedCountSql))
319 << "Table scan should not succeed anymore on a poisoned database";
321 ASSERT_TRUE(Reopen());
323 // The recovered table has consistency between the index and the table.
324 EXPECT_EQ("15", ExecuteWithResult(&db_, kUnindexedCountSql))
325 << "Table should survive database recovery";
326 EXPECT_EQ("15", ExecuteWithResult(&db_, kIndexedCountSql))
327 << "Index should be reconstructed during database recovery";
330 TEST_F(SQLRecoveryTest, RecoverCorruptTable) {
331 // The `filler` column is used to cause a record to overflow multiple pages.
332 static const char kCreateTable[] =
334 "CREATE TABLE rows(indexed INTEGER NOT NULL, unindexed INTEGER NOT NULL,"
335 "filler BLOB NOT NULL)";
337 ASSERT_TRUE(db_.Execute(kCreateTable));
339 static const char kCreateIndex[] =
340 "CREATE UNIQUE INDEX rows_index ON rows(indexed)";
341 ASSERT_TRUE(db_.Execute(kCreateIndex));
343 // Populate the table with powers of two. These numbers make it easy to see if
344 // SUM() missed a row.
345 ASSERT_TRUE(db_.Execute(
346 "INSERT INTO rows(indexed, unindexed, filler) VALUES(1, 1, x'31')"));
347 ASSERT_TRUE(db_.Execute(
348 "INSERT INTO rows(indexed, unindexed, filler) VALUES(2, 2, x'32')"));
349 ASSERT_TRUE(db_.Execute(
350 "INSERT INTO rows(indexed, unindexed, filler) VALUES(4, 4, x'34')"));
352 constexpr int kDbPageSize = 4096;
354 // Insert a record that will overflow the page.
355 std::vector<uint8_t> large_buffer;
356 ASSERT_EQ(db_.page_size(), kDbPageSize)
357 << "Page overflow relies on specific size";
358 large_buffer.resize(kDbPageSize * 2);
359 base::ranges::fill(large_buffer, '8');
360 sql::Statement insert(db_.GetUniqueStatement(
361 "INSERT INTO rows(indexed,unindexed,filler) VALUES(8,8,?)"));
362 insert.BindBlob(0, large_buffer);
363 ASSERT_TRUE(insert.Run());
368 // Zero out the last page of the database. This should be the overflow page
369 // allocated for the last inserted row. So, deleting it should corrupt the
371 base::File db_file(db_path_, base::File::FLAG_OPEN | base::File::FLAG_READ |
372 base::File::FLAG_WRITE);
373 ASSERT_TRUE(db_file.IsValid());
374 int64_t db_size = db_file.GetLength();
375 ASSERT_GT(db_size, kDbPageSize)
376 << "The database should have multiple pages";
377 ASSERT_TRUE(db_file.SetLength(db_size - kDbPageSize));
381 sql::test::ScopedErrorExpecter expecter;
382 expecter.ExpectError(SQLITE_CORRUPT);
383 ASSERT_TRUE(Reopen());
384 EXPECT_TRUE(expecter.SawExpectedErrors());
385 // PRAGMAs executed inside Database::Open() will error out.
388 int error = SQLITE_OK;
389 db_.set_error_callback(
390 base::BindLambdaForTesting([&](int sqlite_error, Statement* statement) {
391 error = sqlite_error;
393 // Recovery::Begin() does not support a pre-existing error callback.
394 db_.reset_error_callback();
395 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
396 ASSERT_TRUE(recovery.get());
398 ASSERT_TRUE(recovery->db()->Execute(kCreateTable));
399 ASSERT_TRUE(recovery->db()->Execute(kCreateIndex));
402 ASSERT_TRUE(recovery->AutoRecoverTable("rows", &rows));
403 ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
406 // SUM(unindexed) heavily nudges SQLite to use the table instead of the index.
407 static const char kUnindexedCountSql[] = "SELECT SUM(unindexed) FROM rows";
408 EXPECT_FALSE(db_.Execute(kUnindexedCountSql))
409 << "Table scan on corrupt table should fail";
410 EXPECT_EQ(SQLITE_CORRUPT, error)
411 << "Error callback should be called during scan on corrupt index";
413 ASSERT_TRUE(Reopen());
415 // All rows should be recovered. Only the BLOB in the last row was damaged.
416 EXPECT_EQ("15", ExecuteWithResult(&db_, kUnindexedCountSql))
417 << "Table should survive database recovery";
418 static const char kIndexedCountSql[] =
419 "SELECT SUM(indexed) FROM rows INDEXED BY rows_index";
420 EXPECT_EQ("15", ExecuteWithResult(&db_, kIndexedCountSql))
421 << "Index should be reconstructed during database recovery";
424 TEST_F(SQLRecoveryTest, Meta) {
425 const int kVersion = 3;
426 const int kCompatibleVersion = 2;
430 EXPECT_TRUE(meta.Init(&db_, kVersion, kCompatibleVersion));
431 EXPECT_EQ(kVersion, meta.GetVersionNumber());
434 // Test expected case where everything works.
436 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
437 EXPECT_TRUE(recovery->SetupMeta());
439 EXPECT_TRUE(recovery->GetMetaVersionNumber(&version));
440 EXPECT_EQ(kVersion, version);
442 Recovery::Rollback(std::move(recovery));
444 ASSERT_TRUE(Reopen()); // Handle was poisoned.
446 // Test version row missing.
447 EXPECT_TRUE(db_.Execute("DELETE FROM meta WHERE key = 'version'"));
449 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
450 EXPECT_TRUE(recovery->SetupMeta());
452 EXPECT_FALSE(recovery->GetMetaVersionNumber(&version));
453 EXPECT_EQ(0, version);
455 Recovery::Rollback(std::move(recovery));
457 ASSERT_TRUE(Reopen()); // Handle was poisoned.
459 // Test meta table missing.
460 EXPECT_TRUE(db_.Execute("DROP TABLE meta"));
462 sql::test::ScopedErrorExpecter expecter;
463 expecter.ExpectError(SQLITE_CORRUPT); // From virtual table.
464 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
465 EXPECT_FALSE(recovery->SetupMeta());
466 ASSERT_TRUE(expecter.SawExpectedErrors());
470 // Baseline AutoRecoverTable() test.
471 TEST_F(SQLRecoveryTest, AutoRecoverTable) {
472 // BIGINT and VARCHAR to test type affinity.
473 static const char kCreateSql[] =
474 "CREATE TABLE x (id BIGINT, t TEXT, v VARCHAR)";
475 ASSERT_TRUE(db_.Execute(kCreateSql));
476 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (11, 'This is', 'a test')"));
477 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (5, 'That was', 'a test')"));
479 // Save aside a copy of the original schema and data.
480 const std::string orig_schema(GetSchema(&db_));
481 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
482 const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n"));
484 // Create a lame-duck table which will not be propagated by recovery to
485 // detect that the recovery code actually ran.
486 ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)"));
487 ASSERT_NE(orig_schema, GetSchema(&db_));
490 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
491 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
493 // Save a copy of the temp db's schema before recovering the table.
494 static const char kTempSchemaSql[] =
495 "SELECT name, sql FROM sqlite_temp_schema";
496 const std::string temp_schema(
497 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n"));
500 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
503 // Test that any additional temp tables were cleaned up.
504 EXPECT_EQ(temp_schema,
505 ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n"));
507 ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
510 // Since the database was not corrupt, the entire schema and all
511 // data should be recovered.
512 ASSERT_TRUE(Reopen());
513 ASSERT_EQ(orig_schema, GetSchema(&db_));
514 ASSERT_EQ(orig_data, ExecuteWithResults(&db_, kXSql, "|", "\n"));
516 // Recovery fails if the target table doesn't exist.
518 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
519 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
521 // TODO(shess): Should this failure implicitly lead to Raze()?
523 EXPECT_FALSE(recovery->AutoRecoverTable("y", &rows));
525 Recovery::Unrecoverable(std::move(recovery));
529 // Test that default values correctly replace nulls. The recovery
530 // virtual table reads directly from the database, so DEFAULT is not
531 // interpretted at that level.
532 TEST_F(SQLRecoveryTest, AutoRecoverTableWithDefault) {
533 ASSERT_TRUE(db_.Execute("CREATE TABLE x (id INTEGER)"));
534 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (5)"));
535 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (15)"));
537 // ALTER effectively leaves the new columns NULL in the first two
538 // rows. The row with 17 will get the default injected at insert
539 // time, while the row with 42 will get the actual value provided.
540 // Embedded "'" to make sure default-handling continues to be quoted
542 ASSERT_TRUE(db_.Execute("ALTER TABLE x ADD COLUMN t TEXT DEFAULT 'a''a'"));
543 ASSERT_TRUE(db_.Execute("ALTER TABLE x ADD COLUMN b BLOB DEFAULT x'AA55'"));
544 ASSERT_TRUE(db_.Execute("ALTER TABLE x ADD COLUMN i INT DEFAULT 93"));
545 ASSERT_TRUE(db_.Execute("INSERT INTO x (id) VALUES (17)"));
546 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (42, 'b', x'1234', 12)"));
548 // Save aside a copy of the original schema and data.
549 const std::string orig_schema(GetSchema(&db_));
550 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
551 const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n"));
553 // Create a lame-duck table which will not be propagated by recovery to
554 // detect that the recovery code actually ran.
555 ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)"));
556 ASSERT_NE(orig_schema, GetSchema(&db_));
558 // Mechanically adjust the stored schema and data to allow detecting
559 // where the default value is coming from. The target table is just
560 // like the original with the default for [t] changed, to signal
561 // defaults coming from the recovery system. The two %5 rows should
562 // get the target-table default for [t], while the others should get
563 // the source-table default.
564 std::string final_schema(orig_schema);
565 std::string final_data(orig_data);
567 while ((pos = final_schema.find("'a''a'")) != std::string::npos) {
568 final_schema.replace(pos, 6, "'c''c'");
570 while ((pos = final_data.find("5|a'a")) != std::string::npos) {
571 final_data.replace(pos, 5, "5|c'c");
575 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
576 // Different default to detect which table provides the default.
577 ASSERT_TRUE(recovery->db()->Execute(final_schema.c_str()));
580 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
583 ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
586 // Since the database was not corrupt, the entire schema and all
587 // data should be recovered.
588 ASSERT_TRUE(Reopen());
589 ASSERT_EQ(final_schema, GetSchema(&db_));
590 ASSERT_EQ(final_data, ExecuteWithResults(&db_, kXSql, "|", "\n"));
593 // Test that rows with NULL in a NOT NULL column are filtered
594 // correctly. In the wild, this would probably happen due to
595 // corruption, but here it is simulated by recovering a table which
596 // allowed nulls into a table which does not.
597 TEST_F(SQLRecoveryTest, AutoRecoverTableNullFilter) {
598 static const char kOrigSchema[] = "CREATE TABLE x (id INTEGER, t TEXT)";
599 static const char kFinalSchema[] =
600 "CREATE TABLE x (id INTEGER, t TEXT NOT NULL)";
602 ASSERT_TRUE(db_.Execute(kOrigSchema));
603 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (5, NULL)"));
604 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (15, 'this is a test')"));
606 // Create a lame-duck table which will not be propagated by recovery to
607 // detect that the recovery code actually ran.
608 ASSERT_EQ(kOrigSchema, GetSchema(&db_));
609 ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)"));
610 ASSERT_NE(kOrigSchema, GetSchema(&db_));
613 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
614 ASSERT_TRUE(recovery->db()->Execute(kFinalSchema));
617 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
620 ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
623 // The schema should be the same, but only one row of data should
624 // have been recovered.
625 ASSERT_TRUE(Reopen());
626 ASSERT_EQ(kFinalSchema, GetSchema(&db_));
627 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
628 ASSERT_EQ("15|this is a test", ExecuteWithResults(&db_, kXSql, "|", "\n"));
631 // Test AutoRecoverTable with a ROWID alias.
632 TEST_F(SQLRecoveryTest, AutoRecoverTableWithRowid) {
633 // The rowid alias is almost always the first column, intentionally
635 static const char kCreateSql[] =
636 "CREATE TABLE x (t TEXT, id INTEGER PRIMARY KEY NOT NULL)";
637 ASSERT_TRUE(db_.Execute(kCreateSql));
638 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('This is a test', NULL)"));
639 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('That was a test', NULL)"));
641 // Save aside a copy of the original schema and data.
642 const std::string orig_schema(GetSchema(&db_));
643 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
644 const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n"));
646 // Create a lame-duck table which will not be propagated by recovery to
647 // detect that the recovery code actually ran.
648 ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)"));
649 ASSERT_NE(orig_schema, GetSchema(&db_));
652 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
653 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
656 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
659 ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
662 // Since the database was not corrupt, the entire schema and all
663 // data should be recovered.
664 ASSERT_TRUE(Reopen());
665 ASSERT_EQ(orig_schema, GetSchema(&db_));
666 ASSERT_EQ(orig_data, ExecuteWithResults(&db_, kXSql, "|", "\n"));
669 // Test that a compound primary key doesn't fire the ROWID code.
670 TEST_F(SQLRecoveryTest, AutoRecoverTableWithCompoundKey) {
671 static const char kCreateSql[] =
673 "id INTEGER NOT NULL,"
676 "PRIMARY KEY (id, id2)"
678 ASSERT_TRUE(db_.Execute(kCreateSql));
680 // NOTE(shess): Do not accidentally use [id] 1, 2, 3, as those will
681 // be the ROWID values.
682 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (1, 'a', 'This is a test')"));
683 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (1, 'b', 'That was a test')"));
684 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (2, 'a', 'Another test')"));
686 // Save aside a copy of the original schema and data.
687 const std::string orig_schema(GetSchema(&db_));
688 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
689 const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n"));
691 // Create a lame-duck table which will not be propagated by recovery to
692 // detect that the recovery code actually ran.
693 ASSERT_TRUE(db_.Execute("CREATE TABLE y (c TEXT)"));
694 ASSERT_NE(orig_schema, GetSchema(&db_));
697 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
698 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
701 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
704 ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
707 // Since the database was not corrupt, the entire schema and all
708 // data should be recovered.
709 ASSERT_TRUE(Reopen());
710 ASSERT_EQ(orig_schema, GetSchema(&db_));
711 ASSERT_EQ(orig_data, ExecuteWithResults(&db_, kXSql, "|", "\n"));
714 // Test recovering from a table with fewer columns than the target.
715 TEST_F(SQLRecoveryTest, AutoRecoverTableMissingColumns) {
716 static const char kCreateSql[] =
717 "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)";
718 static const char kAlterSql[] =
719 "ALTER TABLE x ADD COLUMN t1 TEXT DEFAULT 't'";
720 ASSERT_TRUE(db_.Execute(kCreateSql));
721 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (1, 'This is')"));
722 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES (2, 'That was')"));
724 // Generate the expected info by faking a table to match what recovery will
726 const std::string orig_schema(GetSchema(&db_));
727 static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
728 std::string expected_schema;
729 std::string expected_data;
731 ASSERT_TRUE(db_.BeginTransaction());
732 ASSERT_TRUE(db_.Execute(kAlterSql));
734 expected_schema = GetSchema(&db_);
735 expected_data = ExecuteWithResults(&db_, kXSql, "|", "\n");
737 db_.RollbackTransaction();
740 // Following tests are pointless if the rollback didn't work.
741 ASSERT_EQ(orig_schema, GetSchema(&db_));
743 // Recover the previous version of the table into the altered version.
745 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
746 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
747 ASSERT_TRUE(recovery->db()->Execute(kAlterSql));
749 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
751 ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
754 // Since the database was not corrupt, the entire schema and all
755 // data should be recovered.
756 ASSERT_TRUE(Reopen());
757 ASSERT_EQ(expected_schema, GetSchema(&db_));
758 ASSERT_EQ(expected_data, ExecuteWithResults(&db_, kXSql, "|", "\n"));
761 // Recover a golden file where an interior page has been manually modified so
762 // that the number of cells is greater than will fit on a single page. This
763 // case happened in <http://crbug.com/387868>.
764 TEST_F(SQLRecoveryTest, Bug387868) {
765 base::FilePath golden_path;
766 ASSERT_TRUE(base::PathService::Get(sql::test::DIR_TEST_DATA, &golden_path));
767 golden_path = golden_path.AppendASCII("recovery_387868");
769 ASSERT_TRUE(base::CopyFile(golden_path, db_path_));
770 ASSERT_TRUE(Reopen());
773 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
774 ASSERT_TRUE(recovery.get());
776 // Create the new version of the table.
777 static const char kCreateSql[] =
778 "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)";
779 ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
782 EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
783 EXPECT_EQ(43u, rows);
785 // Successfully recovered.
786 EXPECT_TRUE(Recovery::Recovered(std::move(recovery)));
790 // Memory-mapped I/O interacts poorly with I/O errors. Make sure the recovery
791 // database doesn't accidentally enable it.
792 TEST_F(SQLRecoveryTest, NoMmap) {
793 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
794 ASSERT_TRUE(recovery.get());
796 // In the current implementation, the PRAGMA successfully runs with no result
797 // rows. Running with a single result of |0| is also acceptable.
798 Statement s(recovery->db()->GetUniqueStatement("PRAGMA mmap_size"));
799 EXPECT_TRUE(!s.Step() || !s.ColumnInt64(0));
802 TEST_F(SQLRecoveryTest, RecoverDatabase) {
803 // As a side effect, AUTOINCREMENT creates the sqlite_sequence table for
804 // RecoverDatabase() to handle.
805 ASSERT_TRUE(db_.Execute(
806 "CREATE TABLE table1(id INTEGER PRIMARY KEY AUTOINCREMENT, value TEXT)"));
807 EXPECT_TRUE(db_.Execute("INSERT INTO table1(value) VALUES('turtle')"));
808 EXPECT_TRUE(db_.Execute("INSERT INTO table1(value) VALUES('truck')"));
809 EXPECT_TRUE(db_.Execute("INSERT INTO table1(value) VALUES('trailer')"));
811 // This table needs index and a unique index to work.
812 ASSERT_TRUE(db_.Execute("CREATE TABLE table2(name TEXT, value TEXT)"));
813 ASSERT_TRUE(db_.Execute("CREATE UNIQUE INDEX table2_name ON table2(name)"));
814 ASSERT_TRUE(db_.Execute("CREATE INDEX table2_value ON table2(value)"));
815 EXPECT_TRUE(db_.Execute(
816 "INSERT INTO table2(name, value) VALUES('jim', 'telephone')"));
818 db_.Execute("INSERT INTO table2(name, value) VALUES('bob', 'truck')"));
820 db_.Execute("INSERT INTO table2(name, value) VALUES('dean', 'trailer')"));
822 // Save aside a copy of the original schema, verifying that it has the created
823 // items plus the sqlite_sequence table.
824 const std::string original_schema = GetSchema(&db_);
825 ASSERT_EQ(4, std::count(original_schema.begin(), original_schema.end(), '\n'))
828 static constexpr char kTable1Sql[] = "SELECT * FROM table1 ORDER BY 1";
829 static constexpr char kTable2Sql[] = "SELECT * FROM table2 ORDER BY 1";
830 EXPECT_EQ("1|turtle\n2|truck\n3|trailer",
831 ExecuteWithResults(&db_, kTable1Sql, "|", "\n"));
832 EXPECT_EQ("bob|truck\ndean|trailer\njim|telephone",
833 ExecuteWithResults(&db_, kTable2Sql, "|", "\n"));
835 // Database handle is valid before recovery, poisoned after.
836 static constexpr char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_schema";
837 EXPECT_TRUE(db_.IsSQLValid(kTrivialSql));
838 Recovery::RecoverDatabase(&db_, db_path_);
839 EXPECT_FALSE(db_.IsSQLValid(kTrivialSql));
841 // Since the database was not corrupt, the entire schema and all data should
843 ASSERT_TRUE(Reopen());
844 ASSERT_EQ(original_schema, GetSchema(&db_));
845 EXPECT_EQ("1|turtle\n2|truck\n3|trailer",
846 ExecuteWithResults(&db_, kTable1Sql, "|", "\n"));
847 EXPECT_EQ("bob|truck\ndean|trailer\njim|telephone",
848 ExecuteWithResults(&db_, kTable2Sql, "|", "\n"));
851 TEST_F(SQLRecoveryTest, RecoverDatabaseWithView) {
853 sql::Database db({.enable_views_discouraged = true});
854 ASSERT_TRUE(db.Open(db_path_));
856 ASSERT_TRUE(db.Execute(
857 "CREATE TABLE table1(id INTEGER PRIMARY KEY AUTOINCREMENT, value TEXT)"));
858 EXPECT_TRUE(db.Execute("INSERT INTO table1(value) VALUES('turtle')"));
859 EXPECT_TRUE(db.Execute("INSERT INTO table1(value) VALUES('truck')"));
860 EXPECT_TRUE(db.Execute("INSERT INTO table1(value) VALUES('trailer')"));
862 ASSERT_TRUE(db.Execute("CREATE TABLE table2(name TEXT, value TEXT)"));
863 ASSERT_TRUE(db.Execute("CREATE UNIQUE INDEX table2_name ON table2(name)"));
865 db.Execute("INSERT INTO table2(name, value) VALUES('jim', 'telephone')"));
867 db.Execute("INSERT INTO table2(name, value) VALUES('bob', 'truck')"));
869 db.Execute("INSERT INTO table2(name, value) VALUES('dean', 'trailer')"));
871 // View which is the intersection of [table1.value] and [table2.value].
872 ASSERT_TRUE(db.Execute(
873 "CREATE VIEW view_table12 AS SELECT table1.value FROM table1, table2 "
874 "WHERE table1.value = table2.value"));
876 static constexpr char kViewSql[] = "SELECT * FROM view_table12 ORDER BY 1";
877 EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db, kViewSql, "|", "\n"));
879 // Save aside a copy of the original schema, verifying that it has the created
880 // items plus the sqlite_sequence table.
881 const std::string original_schema = GetSchema(&db);
882 ASSERT_EQ(4, std::count(original_schema.begin(), original_schema.end(), '\n'))
885 // Database handle is valid before recovery, poisoned after.
886 static constexpr char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_schema";
887 EXPECT_TRUE(db.IsSQLValid(kTrivialSql));
888 Recovery::RecoverDatabase(&db, db_path_);
889 EXPECT_FALSE(db.IsSQLValid(kTrivialSql));
891 // Since the database was not corrupt, the entire schema and all data should
894 ASSERT_TRUE(db.Open(db_path_));
895 EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db, kViewSql, "|", "\n"));
898 // When RecoverDatabase() encounters SQLITE_NOTADB, the database is deleted.
899 TEST_F(SQLRecoveryTest, RecoverDatabaseDelete) {
900 // Create a valid database, then write junk over the header. This should lead
901 // to SQLITE_NOTADB, which will cause ATTACH to fail.
902 ASSERT_TRUE(db_.Execute("CREATE TABLE x (t TEXT)"));
903 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('This is a test')"));
905 ASSERT_TRUE(OverwriteDatabaseHeader());
908 sql::test::ScopedErrorExpecter expecter;
909 expecter.ExpectError(SQLITE_NOTADB);
911 // Reopen() here because it will see SQLITE_NOTADB.
912 ASSERT_TRUE(Reopen());
914 // This should "recover" the database by making it valid, but empty.
915 Recovery::RecoverDatabase(&db_, db_path_);
917 ASSERT_TRUE(expecter.SawExpectedErrors());
920 // Recovery poisoned the handle, must re-open.
922 ASSERT_TRUE(Reopen());
924 EXPECT_EQ("", GetSchema(&db_));
927 // Allow callers to validate the database between recovery and commit.
928 TEST_F(SQLRecoveryTest, BeginRecoverDatabase) {
929 static const char kCreateTable[] =
930 "CREATE TABLE rows(indexed INTEGER NOT NULL, unindexed INTEGER NOT NULL)";
931 ASSERT_TRUE(db_.Execute(kCreateTable));
933 ASSERT_TRUE(db_.Execute("CREATE UNIQUE INDEX rows_index ON rows(indexed)"));
935 // Populate the table with powers of two. These numbers make it easy to see if
936 // SUM() missed a row.
937 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(1, 1)"));
938 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(2, 2)"));
939 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(4, 4)"));
940 ASSERT_TRUE(db_.Execute("INSERT INTO rows(indexed, unindexed) VALUES(8, 8)"));
943 ASSERT_TRUE(sql::test::CorruptIndexRootPage(db_path_, "rows_index"));
944 ASSERT_TRUE(Reopen());
946 // Run recovery code, then rollback. Database remains the same.
948 std::unique_ptr<Recovery> recovery =
949 Recovery::BeginRecoverDatabase(&db_, db_path_);
950 ASSERT_TRUE(recovery);
951 Recovery::Rollback(std::move(recovery));
954 ASSERT_TRUE(Reopen());
956 static const char kIndexedCountSql[] =
957 "SELECT SUM(indexed) FROM rows INDEXED BY rows_index";
959 sql::test::ScopedErrorExpecter expecter;
960 expecter.ExpectError(SQLITE_CORRUPT);
961 EXPECT_EQ("", ExecuteWithResult(&db_, kIndexedCountSql))
962 << "Index should still be corrupted after recovery rollback";
963 EXPECT_TRUE(expecter.SawExpectedErrors())
964 << "Index should still be corrupted after recovery rollback";
967 // Run recovery code, then commit. The index is recovered.
969 std::unique_ptr<Recovery> recovery =
970 Recovery::BeginRecoverDatabase(&db_, db_path_);
971 ASSERT_TRUE(recovery);
972 ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
975 ASSERT_TRUE(Reopen());
977 EXPECT_EQ("15", ExecuteWithResult(&db_, kIndexedCountSql))
978 << "Index should be reconstructed after database recovery";
981 TEST_F(SQLRecoveryTest, AttachFailure) {
982 // Create a valid database, then write junk over the header. This should lead
983 // to SQLITE_NOTADB, which will cause ATTACH to fail.
984 ASSERT_TRUE(db_.Execute("CREATE TABLE x (t TEXT)"));
985 ASSERT_TRUE(db_.Execute("INSERT INTO x VALUES ('This is a test')"));
987 ASSERT_TRUE(OverwriteDatabaseHeader());
990 sql::test::ScopedErrorExpecter expecter;
991 expecter.ExpectError(SQLITE_NOTADB);
993 // Reopen() here because it will see SQLITE_NOTADB.
994 ASSERT_TRUE(Reopen());
996 // Begin() should fail.
997 std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
998 EXPECT_FALSE(recovery.get());
1000 ASSERT_TRUE(expecter.SawExpectedErrors());
1004 // Helper for SQLRecoveryTest.PageSize. Creates a fresh db based on db_prefix,
1005 // with the given initial page size, and verifies it against the expected size.
1006 // Then changes to the final page size and recovers, verifying that the
1007 // recovered database ends up with the expected final page size.
1008 void TestPageSize(const base::FilePath& db_prefix,
1009 int initial_page_size,
1010 const std::string& expected_initial_page_size,
1011 int final_page_size,
1012 const std::string& expected_final_page_size) {
1013 static const char kCreateSql[] = "CREATE TABLE x (t TEXT)";
1014 static const char kInsertSql1[] = "INSERT INTO x VALUES ('This is a test')";
1015 static const char kInsertSql2[] = "INSERT INTO x VALUES ('That was a test')";
1016 static const char kSelectSql[] = "SELECT * FROM x ORDER BY t";
1018 const base::FilePath db_path = db_prefix.InsertBeforeExtensionASCII(
1019 base::NumberToString(initial_page_size));
1020 Database::Delete(db_path);
1021 Database db({.page_size = initial_page_size});
1022 ASSERT_TRUE(db.Open(db_path));
1023 ASSERT_TRUE(db.Execute(kCreateSql));
1024 ASSERT_TRUE(db.Execute(kInsertSql1));
1025 ASSERT_TRUE(db.Execute(kInsertSql2));
1026 ASSERT_EQ(expected_initial_page_size,
1027 ExecuteWithResult(&db, "PRAGMA page_size"));
1030 // Re-open the database while setting a new |options.page_size| in the object.
1031 Database recover_db({.page_size = final_page_size});
1032 ASSERT_TRUE(recover_db.Open(db_path));
1033 // Recovery will use the page size set in the database object, which may not
1034 // match the file's page size.
1035 Recovery::RecoverDatabase(&recover_db, db_path);
1037 // Recovery poisoned the handle, must re-open.
1040 // Make sure the page size is read from the file.
1041 Database recovered_db({.page_size = DatabaseOptions::kDefaultPageSize});
1042 ASSERT_TRUE(recovered_db.Open(db_path));
1043 ASSERT_EQ(expected_final_page_size,
1044 ExecuteWithResult(&recovered_db, "PRAGMA page_size"));
1045 EXPECT_EQ("That was a test\nThis is a test",
1046 ExecuteWithResults(&recovered_db, kSelectSql, "|", "\n"));
1049 // Verify that Recovery maintains the page size, and the virtual table
1050 // works with page sizes other than SQLite's default. Also verify the case
1051 // where the default page size has changed.
1052 TEST_F(SQLRecoveryTest, PageSize) {
1053 const std::string default_page_size =
1054 ExecuteWithResult(&db_, "PRAGMA page_size");
1056 // Check the default page size first.
1057 EXPECT_NO_FATAL_FAILURE(TestPageSize(
1058 db_path_, DatabaseOptions::kDefaultPageSize, default_page_size,
1059 DatabaseOptions::kDefaultPageSize, default_page_size));
1061 // Sync uses 32k pages.
1062 EXPECT_NO_FATAL_FAILURE(
1063 TestPageSize(db_path_, 32768, "32768", 32768, "32768"));
1065 // Many clients use 4k pages. This is the SQLite default after 3.12.0.
1066 EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 4096, "4096", 4096, "4096"));
1068 // 1k is the default page size before 3.12.0.
1069 EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 1024, "1024", 1024, "1024"));
1071 // Databases with no page size specified should recover with the new default
1072 // page size. 2k has never been the default page size.
1073 ASSERT_NE("2048", default_page_size);
1074 EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 2048, "2048",
1075 DatabaseOptions::kDefaultPageSize,
1076 default_page_size));