Commit 455989b0 authored by Victor Costan's avatar Victor Costan Committed by Commit Bot

sqlite: Rewrite tests for custom recovery code.

Our largest SQLite patch,
0001-Virtual-table-supporting-recovery-of-corrupted-datab.patch,
contains the implementation of a virtual table extension that contains
custom recovery logic. The automated tests for the extension use
SQLite's infrastructure base on Tcl, and don't run on CQ.

This CL rewrites the tests to use C++ and //sql, so they can be run on
CQ. A follow-up CL will replace the recovery code with a rewritten
version that lives in the Chromium tree.

This CL also adds a test covering the edge cases of the recovery code's
SQLite varint decoding logic, which has proven useful for debugging the
rewritten recovery code.

Bug: 945204
Change-Id: I50c6cbf6f94dc698915e6fb4925769051396cb4b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1603604Reviewed-by: default avatarChris Mumford <cmumford@google.com>
Commit-Queue: Victor Costan <pwnall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#659225}
parent ffc7906e
...@@ -94,6 +94,7 @@ test("sql_unittests") { ...@@ -94,6 +94,7 @@ test("sql_unittests") {
sources = [ sources = [
"database_unittest.cc", "database_unittest.cc",
"meta_table_unittest.cc", "meta_table_unittest.cc",
"recover_module_unittest.cc",
"recovery_unittest.cc", "recovery_unittest.cc",
"sql_memory_dump_provider_unittest.cc", "sql_memory_dump_provider_unittest.cc",
"sqlite_features_unittest.cc", "sqlite_features_unittest.cc",
......
...@@ -7,6 +7,10 @@ ...@@ -7,6 +7,10 @@
namespace sql { namespace sql {
namespace test {
struct ColumnInfo;
} // namespace test
// Restricts access to APIs internal to the //sql package. // Restricts access to APIs internal to the //sql package.
// //
// This implements Java's package-private via the passkey idiom. // This implements Java's package-private via the passkey idiom.
...@@ -18,6 +22,7 @@ class InternalApiToken { ...@@ -18,6 +22,7 @@ class InternalApiToken {
friend class DatabaseTestPeer; friend class DatabaseTestPeer;
friend class Recovery; friend class Recovery;
friend struct test::ColumnInfo;
}; };
} // namespace sql } // namespace sql
......
This diff is collapsed.
...@@ -239,7 +239,7 @@ bool Recovery::Init(const base::FilePath& db_path) { ...@@ -239,7 +239,7 @@ bool Recovery::Init(const base::FilePath& db_path) {
} }
// Enable the recover virtual table for this connection. // Enable the recover virtual table for this connection.
int rc = chrome_sqlite3_recoverVtableInit(recover_db_.db(InternalApiToken())); int rc = EnableRecoveryExtension(&recover_db_, InternalApiToken());
if (rc != SQLITE_OK) { if (rc != SQLITE_OK) {
RecordRecoveryEvent(RECOVERY_FAILED_VIRTUAL_TABLE_INIT); RecordRecoveryEvent(RECOVERY_FAILED_VIRTUAL_TABLE_INIT);
LOG(ERROR) << "Failed to initialize recover module: " LOG(ERROR) << "Failed to initialize recover module: "
...@@ -798,4 +798,9 @@ bool Recovery::ShouldRecover(int extended_error) { ...@@ -798,4 +798,9 @@ bool Recovery::ShouldRecover(int extended_error) {
} }
} }
// static
int Recovery::EnableRecoveryExtension(Database* db, InternalApiToken) {
return chrome_sqlite3_recoverVtableInit(db->db(InternalApiToken()));
}
} // namespace sql } // namespace sql
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "base/component_export.h" #include "base/component_export.h"
#include "base/macros.h" #include "base/macros.h"
#include "sql/database.h" #include "sql/database.h"
#include "sql/internal_api_token.h"
namespace base { namespace base {
class FilePath; class FilePath;
...@@ -175,6 +176,11 @@ class COMPONENT_EXPORT(SQL) Recovery { ...@@ -175,6 +176,11 @@ class COMPONENT_EXPORT(SQL) Recovery {
// the database. // the database.
static bool ShouldRecover(int extended_error); static bool ShouldRecover(int extended_error);
// Enables the "recover" SQLite extension for a database connection.
//
// Returns a SQLite error code.
static int EnableRecoveryExtension(Database* db, InternalApiToken);
private: private:
explicit Recovery(Database* database); explicit Recovery(Database* database);
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "sql/database.h" #include "sql/database.h"
#include "sql/internal_api_token.h" #include "sql/internal_api_token.h"
#include "sql/recovery.h"
#include "third_party/sqlite/sqlite3.h"
namespace sql { namespace sql {
...@@ -24,4 +26,9 @@ bool DatabaseTestPeer::DetachDatabase(Database* db, ...@@ -24,4 +26,9 @@ bool DatabaseTestPeer::DetachDatabase(Database* db,
return db->DetachDatabase(attachment_point, InternalApiToken()); return db->DetachDatabase(attachment_point, InternalApiToken());
} }
// static
bool DatabaseTestPeer::EnableRecoveryExtension(Database* db) {
return Recovery::EnableRecoveryExtension(db, InternalApiToken()) == SQLITE_OK;
}
} // namespace sql } // namespace sql
...@@ -19,6 +19,8 @@ class DatabaseTestPeer { ...@@ -19,6 +19,8 @@ class DatabaseTestPeer {
const base::FilePath& other_db_path, const base::FilePath& other_db_path,
const char* attachment_point); const char* attachment_point);
static bool DetachDatabase(Database* db, const char* attachment_point); static bool DetachDatabase(Database* db, const char* attachment_point);
static bool EnableRecoveryExtension(Database* db);
}; };
} // namespace sql } // namespace sql
......
...@@ -12,10 +12,12 @@ ...@@ -12,10 +12,12 @@
#include "base/files/file_util.h" #include "base/files/file_util.h"
#include "base/files/scoped_file.h" #include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "sql/database.h" #include "sql/database.h"
#include "sql/statement.h" #include "sql/statement.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "third_party/sqlite/sqlite3.h"
namespace { namespace {
...@@ -143,7 +145,7 @@ bool CorruptTableOrIndex(const base::FilePath& db_path, ...@@ -143,7 +145,7 @@ bool CorruptTableOrIndex(const base::FilePath& db_path,
if (!db.Open(db_path)) if (!db.Open(db_path))
return false; return false;
int page_size = 0; int page_size = db.page_size();
if (!GetPageSize(&db, &page_size)) if (!GetPageSize(&db, &page_size))
return false; return false;
...@@ -297,5 +299,37 @@ std::string ExecuteWithResults(sql::Database* db, ...@@ -297,5 +299,37 @@ std::string ExecuteWithResults(sql::Database* db,
return ret; return ret;
} }
int GetPageCount(sql::Database* db) {
sql::Statement statement(db->GetUniqueStatement("PRAGMA page_count"));
CHECK(statement.Step());
return statement.ColumnInt(0);
}
// static
ColumnInfo ColumnInfo::Create(sql::Database* db,
const std::string& db_name,
const std::string& table_name,
const std::string& column_name) {
sqlite3* const sqlite3_db = db->db(InternalApiToken());
const char* data_type;
const char* collation_sequence;
int not_null;
int primary_key;
int auto_increment;
int status = sqlite3_table_column_metadata(
sqlite3_db, db_name.c_str(), table_name.c_str(), column_name.c_str(),
&data_type, &collation_sequence, &not_null, &primary_key,
&auto_increment);
CHECK_EQ(status, SQLITE_OK) << "SQLite error: " << sqlite3_errmsg(sqlite3_db);
// This happens when virtual tables report no type information.
if (data_type == nullptr)
data_type = "(nullptr)";
return {std::string(data_type), std::string(collation_sequence),
not_null != 0, primary_key != 0, auto_increment != 0};
}
} // namespace test } // namespace test
} // namespace sql } // namespace sql
...@@ -119,6 +119,39 @@ std::string ExecuteWithResults(sql::Database* db, ...@@ -119,6 +119,39 @@ std::string ExecuteWithResults(sql::Database* db,
const char* column_sep, const char* column_sep,
const char* row_sep); const char* row_sep);
// Returns the database size, in pages. Crashes on SQLite errors.
int GetPageCount(sql::Database* db);
// Column information returned by GetColumnInfo.
//
// C++ wrapper around the out-params of sqlite3_table_column_metadata().
struct ColumnInfo {
// Retrieves schema information for a column in a table.
//
// Crashes on SQLite errors.
//
// |db_name| should be "main" for the connection's main (opened) database, and
// "temp" for the connection's temporary (in-memory) database.
//
// This is a static method rather than a function so it can be listed in the
// InternalApiToken access control list.
static ColumnInfo Create(sql::Database* db,
const std::string& db_name,
const std::string& table_name,
const std::string& column_name) WARN_UNUSED_RESULT;
// The native data type. Example: "INTEGER".
std::string data_type;
// Default collation sequence for sorting. Example: "BINARY".
std::string collation_sequence;
// True if the column has a "NOT NULL" constraint.
bool has_non_null_constraint;
// True if the column is included in the table's PRIMARY KEY.
bool is_in_primary_key;
// True if the column is AUTOINCREMENT.
bool is_auto_incremented;
};
} // namespace test } // namespace test
} // namespace sql } // namespace sql
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment