diff options
author | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-18 04:18:47 +0000 |
---|---|---|
committer | kinuko@chromium.org <kinuko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-18 04:18:47 +0000 |
commit | a8848a7685d5f673b30bc1f5336e6f9eba44fc41 (patch) | |
tree | dc1888152353c034340059cb229810ba3f4aab54 /sql/test | |
parent | 2a4ee00d34865b2486e84eaf001c0e18358c901d (diff) | |
download | chromium_src-a8848a7685d5f673b30bc1f5336e6f9eba44fc41.zip chromium_src-a8848a7685d5f673b30bc1f5336e6f9eba44fc41.tar.gz chromium_src-a8848a7685d5f673b30bc1f5336e6f9eba44fc41.tar.bz2 |
Revert 235595 "Revert 235492 "[sql] Recover Favicons v5 database..."
> Revert 235492 "[sql] Recover Favicons v5 databases, with more re..."
>
> Speculative revert to find the cause for Mac size regression
> (will revert this revert later)
>
> > [sql] Recover Favicons v5 databases, with more recovery automation.
> >
> > An entirely automated recovery system runs afoul of questions about
> > whether the corrupt database's schema can be trusted.
> > sql::Recovery::AutoRecoverTable() uses a schema created by the caller
> > to construct the recovery virtual table and then copies the data over.
> >
> > sql::Recovery::SetupMeta() and GetMetaVersionNumber() simplify
> > accessing meta-table info in the corrupt database.
> >
> > sql::test::IntegrityCheck() and CorruptSizeInHeader() helpers to
> > simplify common testing operations.
> >
> > Rewrite ThumbnailDatabase v6 and v7 recovery code and tests using
> > these changes, and add a v5 recovery path. Additionally handle
> > deprecated versions.
> >
> > BUG=240396,109482
> >
> > Review URL: https://codereview.chromium.org/50493012
>
> TBR=shess@chromium.org
>
> Review URL: https://codereview.chromium.org/74933002
TBR=kinuko@chromium.org
Review URL: https://codereview.chromium.org/74953002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@235604 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'sql/test')
-rw-r--r-- | sql/test/test_helpers.cc | 71 | ||||
-rw-r--r-- | sql/test/test_helpers.h | 17 |
2 files changed, 88 insertions, 0 deletions
diff --git a/sql/test/test_helpers.cc b/sql/test/test_helpers.cc index de3e8f8..5438bd6 100644 --- a/sql/test/test_helpers.cc +++ b/sql/test/test_helpers.cc @@ -21,11 +21,73 @@ size_t CountSQLItemsOfType(sql::Connection* db, const char* type) { return s.ColumnInt(0); } +// Helper for reading a number from the SQLite header. +// See net/base/big_endian.h. +unsigned ReadBigEndian(unsigned char* buf, size_t bytes) { + unsigned r = buf[0]; + for (size_t i = 1; i < bytes; i++) { + r <<= 8; + r |= buf[i]; + } + return r; +} + +// Helper for writing a number to the SQLite header. +void WriteBigEndian(unsigned val, unsigned char* buf, size_t bytes) { + for (size_t i = 0; i < bytes; i++) { + buf[bytes - i - 1] = (val & 0xFF); + val >>= 8; + } +} + } // namespace namespace sql { namespace test { +bool CorruptSizeInHeader(const base::FilePath& db_path) { + // See http://www.sqlite.org/fileformat.html#database_header + const size_t kHeaderSize = 100; + const size_t kPageSizeOffset = 16; + const size_t kFileChangeCountOffset = 24; + const size_t kPageCountOffset = 28; + const size_t kVersionValidForOffset = 92; // duplicate kFileChangeCountOffset + + unsigned char header[kHeaderSize]; + + file_util::ScopedFILE file(file_util::OpenFile(db_path, "rb+")); + if (!file.get()) + return false; + + if (0 != fseek(file.get(), 0, SEEK_SET)) + return false; + if (1u != fread(header, sizeof(header), 1, file.get())) + return false; + + int64 db_size = 0; + if (!file_util::GetFileSize(db_path, &db_size)) + return false; + + const unsigned page_size = ReadBigEndian(header + kPageSizeOffset, 2); + + // One larger than the expected size. + const unsigned page_count = (db_size + page_size) / page_size; + WriteBigEndian(page_count, header + kPageCountOffset, 4); + + // Update change count so outstanding readers know the info changed. + // Both spots must match for the page count to be considered valid. + unsigned change_count = ReadBigEndian(header + kFileChangeCountOffset, 4); + WriteBigEndian(change_count + 1, header + kFileChangeCountOffset, 4); + WriteBigEndian(change_count + 1, header + kVersionValidForOffset, 4); + + if (0 != fseek(file.get(), 0, SEEK_SET)) + return false; + if (1u != fwrite(header, sizeof(header), 1, file.get())) + return false; + + return true; +} + size_t CountSQLTables(sql::Connection* db) { return CountSQLItemsOfType(db, "table"); } @@ -91,5 +153,14 @@ bool CreateDatabaseFromSQL(const base::FilePath& db_path, return db.Execute(sql.c_str()); } +std::string IntegrityCheck(sql::Connection* db) { + sql::Statement statement(db->GetUniqueStatement("PRAGMA integrity_check")); + + // SQLite should always return a row of data. + EXPECT_TRUE(statement.Step()); + + return statement.ColumnString(0); +} + } // namespace test } // namespace sql diff --git a/sql/test/test_helpers.h b/sql/test/test_helpers.h index 330f59a..b9d5e9b 100644 --- a/sql/test/test_helpers.h +++ b/sql/test/test_helpers.h @@ -5,6 +5,8 @@ #ifndef SQL_TEST_TEST_HELPERS_H_ #define SQL_TEST_TEST_HELPERS_H_ +#include <string> + #include "base/basictypes.h" #include "base/compiler_specific.h" @@ -21,6 +23,16 @@ class Connection; namespace sql { namespace test { +// SQLite stores the database size in the header, and if the actual +// OS-derived size is smaller, the database is considered corrupt. +// [This case is actually a common form of corruption in the wild.] +// This helper sets the in-header size to one page larger than the +// actual file size. The resulting file will return SQLITE_CORRUPT +// for most operations unless PRAGMA writable_schema is turned ON. +// +// Returns false if any error occurs accessing the file. +bool CorruptSizeInHeader(const base::FilePath& db_path) WARN_UNUSED_RESULT; + // Return the number of tables in sqlite_master. size_t CountSQLTables(sql::Connection* db) WARN_UNUSED_RESULT; @@ -43,6 +55,11 @@ bool CountTableRows(sql::Connection* db, const char* table, size_t* count); bool CreateDatabaseFromSQL(const base::FilePath& db_path, const base::FilePath& sql_path) WARN_UNUSED_RESULT; +// Return the results of running "PRAGMA integrity_check" on |db|. +// TODO(shess): sql::Connection::IntegrityCheck() is basically the +// same, but not as convenient for testing. Maybe combine. +std::string IntegrityCheck(sql::Connection* db) WARN_UNUSED_RESULT; + } // namespace test } // namespace sql |