[M108 Migration][HBBTV] Implement ewk_context_register_jsplugin_mime_types API
[platform/framework/web/chromium-efl.git] / sql / recovery_unittest.cc
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.
4
5 #include "sql/recovery.h"
6
7 #include <stddef.h>
8
9 #include <memory>
10 #include <string>
11 #include <utility>
12
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"
29
30 namespace sql {
31
32 namespace {
33
34 using sql::test::ExecuteWithResult;
35 using sql::test::ExecuteWithResults;
36
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");
45 }
46
47 class SQLRecoveryTest : public testing::Test {
48  public:
49   ~SQLRecoveryTest() override = default;
50
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_));
55   }
56
57   bool Reopen() {
58     db_.Close();
59     return db_.Open(db_path_);
60   }
61
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;
68   }
69
70  protected:
71   base::ScopedTempDir temp_dir_;
72   base::FilePath db_path_;
73   Database db_;
74 };
75
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_));
86
87   // If the Recovery handle goes out of scope without being
88   // Recovered(), the database is razed.
89   {
90     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
91     ASSERT_TRUE(recovery.get());
92   }
93   EXPECT_FALSE(db_.is_open());
94   ASSERT_TRUE(Reopen());
95   EXPECT_TRUE(db_.is_open());
96   ASSERT_EQ("", GetSchema(&db_));
97
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_));
102
103   // Unrecoverable() also razes.
104   {
105     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
106     ASSERT_TRUE(recovery.get());
107     Recovery::Unrecoverable(std::move(recovery));
108
109     // TODO(shess): Test that calls to recover.db_ start failing.
110   }
111   EXPECT_FALSE(db_.is_open());
112   ASSERT_TRUE(Reopen());
113   EXPECT_TRUE(db_.is_open());
114   ASSERT_EQ("", GetSchema(&db_));
115
116   // Attempting to recover a previously-recovered handle fails early.
117   {
118     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
119     ASSERT_TRUE(recovery.get());
120     recovery.reset();
121
122     recovery = Recovery::Begin(&db_, db_path_);
123     ASSERT_FALSE(recovery.get());
124   }
125   ASSERT_TRUE(Reopen());
126
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_));
131
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_));
135
136   // Recovered() replaces the original with the "recovered" version.
137   {
138     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
139     ASSERT_TRUE(recovery.get());
140
141     // Create the new version of the table.
142     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
143
144     // Insert different data to distinguish from original database.
145     ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql));
146
147     // Successfully recovered.
148     ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
149   }
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_));
154
155   const char* kXSql = "SELECT * FROM x ORDER BY 1";
156   ASSERT_EQ("That was a test", ExecuteWithResult(&db_, kXSql));
157
158   // Reset the database contents.
159   ASSERT_TRUE(db_.Execute("DELETE FROM x"));
160   ASSERT_TRUE(db_.Execute(kInsertSql));
161
162   // Rollback() discards recovery progress and leaves the database as it was.
163   {
164     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
165     ASSERT_TRUE(recovery.get());
166
167     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
168     ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql));
169
170     Recovery::Rollback(std::move(recovery));
171   }
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_));
176
177   ASSERT_EQ("This is a test", ExecuteWithResult(&db_, kXSql));
178 }
179
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')"));
186
187   // Successfully recover the database.
188   {
189     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
190
191     // Tables to recover original DB, now at [corrupt].
192     static const char kRecoveryCreateSql[] =
193         "CREATE VIRTUAL TABLE temp.recover_x using recover("
194         "  corrupt.x,"
195         "  t TEXT STRICT"
196         ")";
197     ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql));
198
199     // Re-create the original schema.
200     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
201
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));
206
207     // Successfully recovered.
208     ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
209   }
210
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_));
215
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"));
219 }
220
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
223 // the assumption.
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)"));
229
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)"));
236
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";
242
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";
248
249   db_.Close();
250   ASSERT_TRUE(sql::test::CorruptIndexRootPage(db_path_, "rows_index"));
251   ASSERT_TRUE(Reopen());
252
253   {
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";
260   }
261
262   EXPECT_EQ("15", ExecuteWithResult(&db_, kUnindexedCountSql))
263       << "Table scan should not fail due to corrupt index";
264 }
265
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));
270
271   static const char kCreateIndex[] =
272       "CREATE UNIQUE INDEX rows_index ON rows(indexed)";
273   ASSERT_TRUE(db_.Execute(kCreateIndex));
274
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)"));
281
282   db_.Close();
283   ASSERT_TRUE(sql::test::CorruptIndexRootPage(db_path_, "rows_index"));
284   ASSERT_TRUE(Reopen());
285
286   int error = SQLITE_OK;
287   db_.set_error_callback(
288       base::BindLambdaForTesting([&](int sqlite_error, Statement* statement) {
289         error = sqlite_error;
290
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());
295
296         ASSERT_TRUE(recovery->db()->Execute(kCreateTable));
297         ASSERT_TRUE(recovery->db()->Execute(kCreateIndex));
298
299         size_t rows = 0;
300         ASSERT_TRUE(recovery->AutoRecoverTable("rows", &rows));
301         ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
302       }));
303
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";
310
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";
317
318   EXPECT_EQ("", ExecuteWithResult(&db_, kUnindexedCountSql))
319       << "Table scan should not succeed anymore on a poisoned database";
320
321   ASSERT_TRUE(Reopen());
322
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";
328 }
329
330 TEST_F(SQLRecoveryTest, RecoverCorruptTable) {
331   // The `filler` column is used to cause a record to overflow multiple pages.
332   static const char kCreateTable[] =
333       // clang-format off
334       "CREATE TABLE rows(indexed INTEGER NOT NULL, unindexed INTEGER NOT NULL,"
335       "filler BLOB NOT NULL)";
336   // clang-format on
337   ASSERT_TRUE(db_.Execute(kCreateTable));
338
339   static const char kCreateIndex[] =
340       "CREATE UNIQUE INDEX rows_index ON rows(indexed)";
341   ASSERT_TRUE(db_.Execute(kCreateIndex));
342
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')"));
351
352   constexpr int kDbPageSize = 4096;
353   {
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());
364   }
365
366   db_.Close();
367   {
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
370     // rows table.
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));
378   }
379
380   {
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.
386   }
387
388   int error = SQLITE_OK;
389   db_.set_error_callback(
390       base::BindLambdaForTesting([&](int sqlite_error, Statement* statement) {
391         error = sqlite_error;
392
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());
397
398         ASSERT_TRUE(recovery->db()->Execute(kCreateTable));
399         ASSERT_TRUE(recovery->db()->Execute(kCreateIndex));
400
401         size_t rows = 0;
402         ASSERT_TRUE(recovery->AutoRecoverTable("rows", &rows));
403         ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
404       }));
405
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";
412
413   ASSERT_TRUE(Reopen());
414
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";
422 }
423
424 TEST_F(SQLRecoveryTest, Meta) {
425   const int kVersion = 3;
426   const int kCompatibleVersion = 2;
427
428   {
429     MetaTable meta;
430     EXPECT_TRUE(meta.Init(&db_, kVersion, kCompatibleVersion));
431     EXPECT_EQ(kVersion, meta.GetVersionNumber());
432   }
433
434   // Test expected case where everything works.
435   {
436     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
437     EXPECT_TRUE(recovery->SetupMeta());
438     int version = 0;
439     EXPECT_TRUE(recovery->GetMetaVersionNumber(&version));
440     EXPECT_EQ(kVersion, version);
441
442     Recovery::Rollback(std::move(recovery));
443   }
444   ASSERT_TRUE(Reopen());  // Handle was poisoned.
445
446   // Test version row missing.
447   EXPECT_TRUE(db_.Execute("DELETE FROM meta WHERE key = 'version'"));
448   {
449     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
450     EXPECT_TRUE(recovery->SetupMeta());
451     int version = 0;
452     EXPECT_FALSE(recovery->GetMetaVersionNumber(&version));
453     EXPECT_EQ(0, version);
454
455     Recovery::Rollback(std::move(recovery));
456   }
457   ASSERT_TRUE(Reopen());  // Handle was poisoned.
458
459   // Test meta table missing.
460   EXPECT_TRUE(db_.Execute("DROP TABLE meta"));
461   {
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());
467   }
468 }
469
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')"));
478
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"));
483
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_));
488
489   {
490     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
491     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
492
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"));
498
499     size_t rows = 0;
500     EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
501     EXPECT_EQ(2u, rows);
502
503     // Test that any additional temp tables were cleaned up.
504     EXPECT_EQ(temp_schema,
505               ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n"));
506
507     ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
508   }
509
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"));
515
516   // Recovery fails if the target table doesn't exist.
517   {
518     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
519     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
520
521     // TODO(shess): Should this failure implicitly lead to Raze()?
522     size_t rows = 0;
523     EXPECT_FALSE(recovery->AutoRecoverTable("y", &rows));
524
525     Recovery::Unrecoverable(std::move(recovery));
526   }
527 }
528
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)"));
536
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
541   // correctly.
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)"));
547
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"));
552
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_));
557
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);
566   size_t pos;
567   while ((pos = final_schema.find("'a''a'")) != std::string::npos) {
568     final_schema.replace(pos, 6, "'c''c'");
569   }
570   while ((pos = final_data.find("5|a'a")) != std::string::npos) {
571     final_data.replace(pos, 5, "5|c'c");
572   }
573
574   {
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()));
578
579     size_t rows = 0;
580     EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
581     EXPECT_EQ(4u, rows);
582
583     ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
584   }
585
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"));
591 }
592
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)";
601
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')"));
605
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_));
611
612   {
613     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
614     ASSERT_TRUE(recovery->db()->Execute(kFinalSchema));
615
616     size_t rows = 0;
617     EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
618     EXPECT_EQ(1u, rows);
619
620     ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
621   }
622
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"));
629 }
630
631 // Test AutoRecoverTable with a ROWID alias.
632 TEST_F(SQLRecoveryTest, AutoRecoverTableWithRowid) {
633   // The rowid alias is almost always the first column, intentionally
634   // put it later.
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)"));
640
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"));
645
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_));
650
651   {
652     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
653     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
654
655     size_t rows = 0;
656     EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
657     EXPECT_EQ(2u, rows);
658
659     ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
660   }
661
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"));
667 }
668
669 // Test that a compound primary key doesn't fire the ROWID code.
670 TEST_F(SQLRecoveryTest, AutoRecoverTableWithCompoundKey) {
671   static const char kCreateSql[] =
672       "CREATE TABLE x ("
673       "id INTEGER NOT NULL,"
674       "id2 TEXT NOT NULL,"
675       "t TEXT,"
676       "PRIMARY KEY (id, id2)"
677       ")";
678   ASSERT_TRUE(db_.Execute(kCreateSql));
679
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')"));
685
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"));
690
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_));
695
696   {
697     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
698     ASSERT_TRUE(recovery->db()->Execute(kCreateSql));
699
700     size_t rows = 0;
701     EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
702     EXPECT_EQ(3u, rows);
703
704     ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
705   }
706
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"));
712 }
713
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')"));
723
724   // Generate the expected info by faking a table to match what recovery will
725   // create.
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;
730   {
731     ASSERT_TRUE(db_.BeginTransaction());
732     ASSERT_TRUE(db_.Execute(kAlterSql));
733
734     expected_schema = GetSchema(&db_);
735     expected_data = ExecuteWithResults(&db_, kXSql, "|", "\n");
736
737     db_.RollbackTransaction();
738   }
739
740   // Following tests are pointless if the rollback didn't work.
741   ASSERT_EQ(orig_schema, GetSchema(&db_));
742
743   // Recover the previous version of the table into the altered version.
744   {
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));
748     size_t rows = 0;
749     EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
750     EXPECT_EQ(2u, rows);
751     ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
752   }
753
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"));
759 }
760
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");
768   db_.Close();
769   ASSERT_TRUE(base::CopyFile(golden_path, db_path_));
770   ASSERT_TRUE(Reopen());
771
772   {
773     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
774     ASSERT_TRUE(recovery.get());
775
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));
780
781     size_t rows = 0;
782     EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows));
783     EXPECT_EQ(43u, rows);
784
785     // Successfully recovered.
786     EXPECT_TRUE(Recovery::Recovered(std::move(recovery)));
787   }
788 }
789
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());
795
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));
800 }
801
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')"));
810
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')"));
817   EXPECT_TRUE(
818       db_.Execute("INSERT INTO table2(name, value) VALUES('bob', 'truck')"));
819   EXPECT_TRUE(
820       db_.Execute("INSERT INTO table2(name, value) VALUES('dean', 'trailer')"));
821
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'))
826       << original_schema;
827
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"));
834
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));
840
841   // Since the database was not corrupt, the entire schema and all data should
842   // be recovered.
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"));
849 }
850
851 TEST_F(SQLRecoveryTest, RecoverDatabaseWithView) {
852   db_.Close();
853   sql::Database db({.enable_views_discouraged = true});
854   ASSERT_TRUE(db.Open(db_path_));
855
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')"));
861
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)"));
864   EXPECT_TRUE(
865       db.Execute("INSERT INTO table2(name, value) VALUES('jim', 'telephone')"));
866   EXPECT_TRUE(
867       db.Execute("INSERT INTO table2(name, value) VALUES('bob', 'truck')"));
868   EXPECT_TRUE(
869       db.Execute("INSERT INTO table2(name, value) VALUES('dean', 'trailer')"));
870
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"));
875
876   static constexpr char kViewSql[] = "SELECT * FROM view_table12 ORDER BY 1";
877   EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db, kViewSql, "|", "\n"));
878
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'))
883       << original_schema;
884
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));
890
891   // Since the database was not corrupt, the entire schema and all data should
892   // be recovered.
893   db.Close();
894   ASSERT_TRUE(db.Open(db_path_));
895   EXPECT_EQ("trailer\ntruck", ExecuteWithResults(&db, kViewSql, "|", "\n"));
896 }
897
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')"));
904   db_.Close();
905   ASSERT_TRUE(OverwriteDatabaseHeader());
906
907   {
908     sql::test::ScopedErrorExpecter expecter;
909     expecter.ExpectError(SQLITE_NOTADB);
910
911     // Reopen() here because it will see SQLITE_NOTADB.
912     ASSERT_TRUE(Reopen());
913
914     // This should "recover" the database by making it valid, but empty.
915     Recovery::RecoverDatabase(&db_, db_path_);
916
917     ASSERT_TRUE(expecter.SawExpectedErrors());
918   }
919
920   // Recovery poisoned the handle, must re-open.
921   db_.Close();
922   ASSERT_TRUE(Reopen());
923
924   EXPECT_EQ("", GetSchema(&db_));
925 }
926
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));
932
933   ASSERT_TRUE(db_.Execute("CREATE UNIQUE INDEX rows_index ON rows(indexed)"));
934
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)"));
941
942   db_.Close();
943   ASSERT_TRUE(sql::test::CorruptIndexRootPage(db_path_, "rows_index"));
944   ASSERT_TRUE(Reopen());
945
946   // Run recovery code, then rollback.  Database remains the same.
947   {
948     std::unique_ptr<Recovery> recovery =
949         Recovery::BeginRecoverDatabase(&db_, db_path_);
950     ASSERT_TRUE(recovery);
951     Recovery::Rollback(std::move(recovery));
952   }
953   db_.Close();
954   ASSERT_TRUE(Reopen());
955
956   static const char kIndexedCountSql[] =
957       "SELECT SUM(indexed) FROM rows INDEXED BY rows_index";
958   {
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";
965   }
966
967   // Run recovery code, then commit.  The index is recovered.
968   {
969     std::unique_ptr<Recovery> recovery =
970         Recovery::BeginRecoverDatabase(&db_, db_path_);
971     ASSERT_TRUE(recovery);
972     ASSERT_TRUE(Recovery::Recovered(std::move(recovery)));
973   }
974   db_.Close();
975   ASSERT_TRUE(Reopen());
976
977   EXPECT_EQ("15", ExecuteWithResult(&db_, kIndexedCountSql))
978       << "Index should be reconstructed after database recovery";
979 }
980
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')"));
986   db_.Close();
987   ASSERT_TRUE(OverwriteDatabaseHeader());
988
989   {
990     sql::test::ScopedErrorExpecter expecter;
991     expecter.ExpectError(SQLITE_NOTADB);
992
993     // Reopen() here because it will see SQLITE_NOTADB.
994     ASSERT_TRUE(Reopen());
995
996     // Begin() should fail.
997     std::unique_ptr<Recovery> recovery = Recovery::Begin(&db_, db_path_);
998     EXPECT_FALSE(recovery.get());
999
1000     ASSERT_TRUE(expecter.SawExpectedErrors());
1001   }
1002 }
1003
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";
1017
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"));
1028   db.Close();
1029
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);
1036
1037   // Recovery poisoned the handle, must re-open.
1038   recover_db.Close();
1039
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"));
1047 }
1048
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");
1055
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));
1060
1061   // Sync uses 32k pages.
1062   EXPECT_NO_FATAL_FAILURE(
1063       TestPageSize(db_path_, 32768, "32768", 32768, "32768"));
1064
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"));
1067
1068   // 1k is the default page size before 3.12.0.
1069   EXPECT_NO_FATAL_FAILURE(TestPageSize(db_path_, 1024, "1024", 1024, "1024"));
1070
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));
1077 }
1078
1079 }  // namespace
1080
1081 }  // namespace sql