0

SQL: recovery cleanup - rename BuiltInRecovery and delete dead code.

Bug: 40061775
Change-Id: I62aea84fccbeb735029a54bff4a652816c4e41b9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5328615
Reviewed-by: Austin Sullivan <asully@chromium.org>
Commit-Queue: Evan Stade <estade@chromium.org>
Reviewed-by: Ayu Ishii <ayui@chromium.org>
Reviewed-by: Arthur Sonzogni <arthursonzogni@chromium.org>
Reviewed-by: Cait Phillips <caitkp@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1271620}
This commit is contained in:
Evan Stade
2024-03-12 16:50:27 +00:00
committed by Chromium LUCI CQ
parent 39d5d2a9c9
commit 4c7d6b3b77
20 changed files with 169 additions and 228 deletions

@ -94,8 +94,8 @@ void DatabaseErrorCallback(sql::Database* db,
int extended_error,
sql::Statement* stmt) {
// Attempt to recover a corrupt database, if it is eligible to be recovered.
if (sql::BuiltInRecovery::RecoverIfPossible(
db, extended_error, sql::BuiltInRecovery::Strategy::kRecoverOrRaze)) {
if (sql::Recovery::RecoverIfPossible(
db, extended_error, sql::Recovery::Strategy::kRecoverOrRaze)) {
// Recovery was attempted. The database handle has been poisoned and the
// error callback has been reset.

@ -195,9 +195,9 @@ void DatabaseErrorCallback(sql::Database* db,
// see how to reach that.
// Attempt to recover a corrupt database, if it is eligible to be recovered.
if (sql::BuiltInRecovery::RecoverIfPossible(
if (sql::Recovery::RecoverIfPossible(
db, extended_error,
sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze)) {
sql::Recovery::Strategy::kRecoverWithMetaVersionOrRaze)) {
// Recovery was attempted. The database handle has been poisoned and the
// error callback has been reset.

@ -113,9 +113,9 @@ void TopSitesDatabase::DatabaseErrorCallback(const base::FilePath& db_path,
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(db_);
if (sql::BuiltInRecovery::RecoverIfPossible(
if (sql::Recovery::RecoverIfPossible(
db_.get(), extended_error,
sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze)) {
sql::Recovery::Strategy::kRecoverWithMetaVersionOrRaze)) {
// Recovery was attempted. The database handle has been poisoned and the
// error callback has been reset.

@ -221,9 +221,8 @@ void MediaDeviceSaltDatabase::OnDatabaseError(int error,
sql::Statement* statement) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
sql::UmaHistogramSqliteResult("Media.MediaDevices.SaltDatabaseErrors", error);
std::ignore = sql::BuiltInRecovery::RecoverIfPossible(
&db_, error,
sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze);
std::ignore = sql::Recovery::RecoverIfPossible(
&db_, error, sql::Recovery::Strategy::kRecoverWithMetaVersionOrRaze);
}
} // namespace media_device_salt

@ -64,8 +64,8 @@ void DatabaseErrorCallback(sql::Database* db,
int extended_error,
sql::Statement* stmt) {
// Attempt to recover a corrupt database, if it is eligible to be recovered.
if (sql::BuiltInRecovery::RecoverIfPossible(
db, extended_error, sql::BuiltInRecovery::Strategy::kRecoverOrRaze)) {
if (sql::Recovery::RecoverIfPossible(
db, extended_error, sql::Recovery::Strategy::kRecoverOrRaze)) {
// Recovery was attempted. The database handle has been poisoned and the
// error callback has been reset.

@ -2619,9 +2619,9 @@ void AttributionStorageSql::DatabaseErrorCallback(int extended_error,
sql::Statement* stmt) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Attempt to recover a corrupt database, if it is eligible to be recovered.
if (sql::BuiltInRecovery::RecoverIfPossible(
if (sql::Recovery::RecoverIfPossible(
&db_, extended_error,
sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze)) {
sql::Recovery::Strategy::kRecoverWithMetaVersionOrRaze)) {
// Recovery was attempted. The database handle has been poisoned and the
// error callback has been reset.

@ -343,9 +343,9 @@ void BrowsingTopicsSiteDataStorage::DatabaseErrorCallback(
sql::Statement* stmt) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// Attempt to recover a corrupt database, if it is eligible to be recovered.
if (sql::BuiltInRecovery::RecoverIfPossible(
if (sql::Recovery::RecoverIfPossible(
db_.get(), extended_error,
sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze)) {
sql::Recovery::Strategy::kRecoverWithMetaVersionOrRaze)) {
// Recovery was attempted. The database handle has been poisoned and the
// error callback has been reset.

@ -734,9 +734,9 @@ void FirstPartySetsDatabase::DatabaseErrorCallback(int extended_error,
sql::Statement* stmt) {
CHECK(db_);
// Attempt to recover a corrupt database, if it is eligible to be recovered.
if (sql::BuiltInRecovery::RecoverIfPossible(
if (sql::Recovery::RecoverIfPossible(
db_.get(), extended_error,
sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze)) {
sql::Recovery::Strategy::kRecoverWithMetaVersionOrRaze)) {
// Recovery was attempted. The database handle has been poisoned and the
// error callback has been reset.

@ -331,7 +331,7 @@ bool Database::Open(const base::FilePath& path) {
DCHECK_NE(path_string, kSqliteOpenInMemoryPath)
<< "Path conflicts with SQLite magic identifier";
if (OpenInternal(path_string, OpenMode::kNone)) {
if (OpenInternal(path_string)) {
return true;
}
// OpenInternal() may have run the error callback before returning false. If
@ -339,7 +339,7 @@ bool Database::Open(const base::FilePath& path) {
// razed, so a second attempt may succeed.
if (poisoned_) {
Close();
return OpenInternal(path_string, OpenMode::kNone);
return OpenInternal(path_string);
}
// Otherwise, do not attempt to reopen.
return false;
@ -351,7 +351,7 @@ bool Database::OpenInMemory() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
in_memory_ = true;
return OpenInternal(kSqliteOpenInMemoryPath, OpenMode::kInMemory);
return OpenInternal(kSqliteOpenInMemoryPath);
}
void Database::DetachFromSequence() {
@ -359,13 +359,6 @@ void Database::DetachFromSequence() {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
bool Database::OpenTemporary(base::PassKey<Recovery>) {
TRACE_EVENT0("sql", "Database::OpenTemporary");
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return OpenInternal(std::string(), OpenMode::kTemporary);
}
void Database::CloseInternal(bool forced) {
TRACE_EVENT0("sql", "Database::CloseInternal");
@ -1796,22 +1789,10 @@ const char* Database::GetErrorMessage() const {
return sqlite3_errmsg(db_);
}
bool Database::OpenInternal(const std::string& db_file_path,
Database::OpenMode mode) {
bool Database::OpenInternal(const std::string& db_file_path) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
TRACE_EVENT1("sql", "Database::OpenInternal", "path", db_file_path);
DCHECK(mode != OpenMode::kTemporary || db_file_path.empty())
<< "Temporary databases should be open with an empty file path";
if (mode == OpenMode::kInMemory) {
DCHECK_EQ(db_file_path, kSqliteOpenInMemoryPath)
<< "In-memory databases should be open with the magic :memory: path";
} else {
DCHECK_NE(db_file_path, kSqliteOpenInMemoryPath)
<< "Database file path conflicts with SQLite magic identifier";
}
if (is_open()) {
DLOG(FATAL) << "sql::Database is already open.";
return false;
@ -1843,7 +1824,8 @@ bool Database::OpenInternal(const std::string& db_file_path,
std::string uri_file_path = db_file_path;
if (options_.exclusive_database_file_lock) {
#if BUILDFLAG(IS_WIN)
if (mode == OpenMode::kNone) {
const bool in_memory = db_file_path == kSqliteOpenInMemoryPath;
if (!in_memory) {
// Do not allow query injection.
if (base::Contains(db_file_path, '?')) {
return false;

@ -53,7 +53,6 @@ class ChromeSqlDiagnostics;
namespace sql {
class DatabaseMemoryDumpProvider;
class Recovery;
class Statement;
namespace test {
@ -110,8 +109,7 @@ struct COMPONENT_EXPORT(SQL) DatabaseOptions {
// If true, enables SQLite's Write-Ahead Logging (WAL).
//
// WAL integration is under development, and should not be used in shipping
// Chrome features yet. In particular, our custom database recovery code does
// not support the WAL log file.
// Chrome features yet.
//
// WAL mode is currently not fully supported on FuchsiaOS. It will only be
// turned on if the database is also using exclusive locking mode.
@ -393,14 +391,6 @@ class COMPONENT_EXPORT(SQL) Database {
// is closed.
[[nodiscard]] bool OpenInMemory();
// Alternative to Open() that creates a temporary on-disk database.
//
// Returns true in case of success, false in case of failure.
//
// The files associated with the temporary database will be deleted when the
// database is closed.
[[nodiscard]] bool OpenTemporary(base::PassKey<Recovery>);
// Returns true if the database has been successfully opened.
bool is_open() const;
@ -739,24 +729,12 @@ class COMPONENT_EXPORT(SQL) Database {
FRIEND_TEST_ALL_PREFIXES(SQLiteFeaturesTest, WALNoClose);
FRIEND_TEST_ALL_PREFIXES(SQLEmptyPathDatabaseTest, EmptyPathTest);
// Enables a special behavior for OpenInternal().
enum class OpenMode {
// No special behavior.
kNone,
// Open an in-memory database. Used by OpenInMemory().
kInMemory,
// Open a temporary database. Used by OpenTemporary().
kTemporary,
};
// Implements Open(), OpenInMemory(), and OpenTemporary().
// Implements Open(), OpenInMemory().
//
// `db_file_path` is a UTF-8 path to the file storing the database pages. The
// path must be empty if `mode` is kTemporary. The path must be the SQLite
// magic memory path string if `mode` is kMemory.
bool OpenInternal(const std::string& file_name, OpenMode mode);
// `db_file_path` is a UTF-8 path to the file storing the database pages. If
// `file_name` is the SQLite magic memory path :memory:, the database will be
// opened in-memory.
bool OpenInternal(const std::string& file_name);
// Configures the underlying sqlite3* object via sqlite3_db_config().
//
@ -955,9 +933,7 @@ class COMPONENT_EXPORT(SQL) Database {
// Returns a SQLite VFS interface pointer to the file storing database pages.
//
// Returns null if the database is not backed by a VFS file. This is always
// the case for in-memory databases. Temporary databases (only used by sq
// ::Recovery) start without a backing VFS file, and only get a file when they
// outgrow their page cache.
// the case for in-memory databases.
//
// This method must only be called while the database is successfully opened.
sqlite3_file* GetSqliteVfsFile();

@ -2273,8 +2273,8 @@ TEST_P(SQLDatabaseTest, OpenWithRecoveryHandlesCorruption) {
size_t error_count = 0;
auto callback = base::BindLambdaForTesting([&](int error, Statement* stmt) {
error_count++;
ASSERT_TRUE(BuiltInRecovery::RecoverIfPossible(
db_.get(), error, sql::BuiltInRecovery::Strategy::kRecoverOrRaze));
ASSERT_TRUE(Recovery::RecoverIfPossible(
db_.get(), error, sql::Recovery::Strategy::kRecoverOrRaze));
if (corrupt_after_recovery) {
// Corrupt the file again after temporarily recovering it.
ASSERT_TRUE(sql::test::CorruptSizeInHeader(db_path_));

@ -129,7 +129,7 @@ class TestCase {
}
}
sql::BuiltInRecovery::Strategy strategy() const { return strategy_; }
sql::Recovery::Strategy strategy() const { return strategy_; }
bool wal_mode() const { return wal_mode_; }
base::span<const Mutation> mutations() const { return mutations_; }
std::string_view sql_statement() const { return sql_statement_; }
@ -157,18 +157,17 @@ class TestCase {
private:
// Converts an arbitrary int to a valid enum value.
static sql::BuiltInRecovery::Strategy RecoveryStrategyFromInt(int input);
static sql::Recovery::Strategy RecoveryStrategyFromInt(int input);
// Converts arbitrary bytes in `s` to a human-readable ASCII string.
// Non-printable characters are hex-escaped.
static std::string DebugFormat(std::string_view s);
// Converts the value of `strategy`, which must be a valid enum value, to a
// human-readable string.
static constexpr const char* DebugFormat(
sql::BuiltInRecovery::Strategy strategy);
static constexpr const char* DebugFormat(sql::Recovery::Strategy strategy);
// Fields parsed from the fuzzer input:
const sql::BuiltInRecovery::Strategy strategy_ =
sql::BuiltInRecovery::Strategy::kRecoverOrRaze;
const sql::Recovery::Strategy strategy_ =
sql::Recovery::Strategy::kRecoverOrRaze;
const bool wal_mode_ = false;
std::vector<Mutation> mutations_;
const std::string sql_statement_;
@ -266,7 +265,7 @@ DEFINE_PROTO_FUZZER(const sql_fuzzers::RecoveryFuzzerTestCase& fuzzer_input) {
auto error_callback =
base::BindLambdaForTesting([&](int extended_error, sql::Statement*) {
if (!attempted_recovery) {
attempted_recovery = sql::BuiltInRecovery::RecoverIfPossible(
attempted_recovery = sql::Recovery::RecoverIfPossible(
&database, extended_error, test_case.strategy());
}
});
@ -291,22 +290,22 @@ DEFINE_PROTO_FUZZER(const sql_fuzzers::RecoveryFuzzerTestCase& fuzzer_input) {
namespace {
sql::BuiltInRecovery::Strategy TestCase::RecoveryStrategyFromInt(int input) {
sql::Recovery::Strategy TestCase::RecoveryStrategyFromInt(int input) {
static_assert(
std::is_same_v<std::underlying_type<sql::BuiltInRecovery::Strategy>::type,
std::is_same_v<std::underlying_type<sql::Recovery::Strategy>::type,
decltype(input)>,
"sql::BuiltInRecovery::Strategy's underlying type must match the input");
"sql::Recovery::Strategy's underlying type must match the input");
const auto strategy = static_cast<sql::BuiltInRecovery::Strategy>(input);
const auto strategy = static_cast<sql::Recovery::Strategy>(input);
// Ensure that we remember to update the fuzzer if more strategies are added.
switch (strategy) {
case sql::BuiltInRecovery::Strategy::kRecoverOrRaze:
case sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze:
case sql::Recovery::Strategy::kRecoverOrRaze:
case sql::Recovery::Strategy::kRecoverWithMetaVersionOrRaze:
return strategy;
}
// When `input` is out of range, return a default value.
return sql::BuiltInRecovery::Strategy::kRecoverOrRaze;
return sql::Recovery::Strategy::kRecoverOrRaze;
}
std::string TestCase::DebugFormat(std::string_view s) {
@ -326,12 +325,11 @@ std::string TestCase::DebugFormat(std::string_view s) {
return out;
}
constexpr const char* TestCase::DebugFormat(
sql::BuiltInRecovery::Strategy strategy) {
constexpr const char* TestCase::DebugFormat(sql::Recovery::Strategy strategy) {
switch (strategy) {
case sql::BuiltInRecovery::Strategy::kRecoverOrRaze:
case sql::Recovery::Strategy::kRecoverOrRaze:
return "kRecoverOrRaze";
case sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze:
case sql::Recovery::Strategy::kRecoverWithMetaVersionOrRaze:
return "kRecoverWithMetaVersionOrRaze";
}
}

@ -25,7 +25,7 @@ class InternalApiToken {
InternalApiToken() {}
InternalApiToken(const InternalApiToken&) = default;
friend class BuiltInRecovery;
friend class Recovery;
friend class DatabaseTestPeer;
friend class Recovery;
friend class Transaction;

@ -38,8 +38,7 @@ constexpr char kMainDatabaseName[] = "main";
} // namespace
// static
bool BuiltInRecovery::ShouldAttemptRecovery(Database* database,
int extended_error) {
bool Recovery::ShouldAttemptRecovery(Database* database, int extended_error) {
return database && database->is_open() &&
!database->DbPath(InternalApiToken()).empty() &&
#if BUILDFLAG(IS_FUCHSIA)
@ -50,16 +49,16 @@ bool BuiltInRecovery::ShouldAttemptRecovery(Database* database,
}
// static
SqliteResultCode BuiltInRecovery::RecoverDatabase(Database* database,
Strategy strategy) {
auto recovery = BuiltInRecovery(database, strategy);
SqliteResultCode Recovery::RecoverDatabase(Database* database,
Strategy strategy) {
auto recovery = Recovery(database, strategy);
return recovery.RecoverAndReplaceDatabase();
}
// static
bool BuiltInRecovery::RecoverIfPossible(Database* database,
int extended_error,
Strategy strategy) {
bool Recovery::RecoverIfPossible(Database* database,
int extended_error,
Strategy strategy) {
if (!ShouldAttemptRecovery(database, extended_error)) {
return false;
}
@ -69,7 +68,7 @@ bool BuiltInRecovery::RecoverIfPossible(Database* database,
// re-entry.
database->reset_error_callback();
auto result = BuiltInRecovery::RecoverDatabase(database, strategy);
auto result = Recovery::RecoverDatabase(database, strategy);
if (!IsSqliteSuccessCode(result)) {
DLOG(ERROR) << "Database recovery failed with result code: " << result;
}
@ -77,7 +76,7 @@ bool BuiltInRecovery::RecoverIfPossible(Database* database,
return true;
}
BuiltInRecovery::BuiltInRecovery(Database* database, Strategy strategy)
Recovery::Recovery(Database* database, Strategy strategy)
: strategy_(strategy),
db_(database),
recover_db_(sql::DatabaseOptions{
@ -107,7 +106,7 @@ BuiltInRecovery::BuiltInRecovery(Database* database, Strategy strategy)
db_->RollbackAllTransactions();
}
BuiltInRecovery::~BuiltInRecovery() {
Recovery::~Recovery() {
// Recovery result must be set before we reach this point.
CHECK_NE(result_, Result::kUnknown);
@ -143,15 +142,15 @@ BuiltInRecovery::~BuiltInRecovery() {
sql::Database::Delete(recovery_database_path_);
}
void BuiltInRecovery::SetRecoverySucceeded() {
void Recovery::SetRecoverySucceeded() {
// Recovery result must only be set once.
CHECK_EQ(result_, Result::kUnknown);
result_ = Result::kSuccess;
}
void BuiltInRecovery::SetRecoveryFailed(Result failure_result,
SqliteResultCode result_code) {
void Recovery::SetRecoveryFailed(Result failure_result,
SqliteResultCode result_code) {
// Recovery result must only be set once.
CHECK_EQ(result_, Result::kUnknown);
@ -175,7 +174,7 @@ void BuiltInRecovery::SetRecoveryFailed(Result failure_result,
sqlite_result_code_ = result_code;
}
SqliteResultCode BuiltInRecovery::RecoverAndReplaceDatabase() {
SqliteResultCode Recovery::RecoverAndReplaceDatabase() {
auto sqlite_result_code = AttemptToRecoverDatabaseToBackup();
if (sqlite_result_code != SqliteResultCode::kOk) {
return sqlite_result_code;
@ -202,7 +201,7 @@ SqliteResultCode BuiltInRecovery::RecoverAndReplaceDatabase() {
return ReplaceOriginalWithRecoveredDb();
}
SqliteResultCode BuiltInRecovery::AttemptToRecoverDatabaseToBackup() {
SqliteResultCode Recovery::AttemptToRecoverDatabaseToBackup() {
CHECK(db_->is_open());
CHECK(!recover_db_.is_open());
@ -274,7 +273,7 @@ SqliteResultCode BuiltInRecovery::AttemptToRecoverDatabaseToBackup() {
return sqlite_result_code;
}
SqliteResultCode BuiltInRecovery::ReplaceOriginalWithRecoveredDb() {
SqliteResultCode Recovery::ReplaceOriginalWithRecoveredDb() {
CHECK(db_->is_open());
CHECK(recover_db_.is_open());
@ -342,7 +341,7 @@ SqliteResultCode BuiltInRecovery::ReplaceOriginalWithRecoveredDb() {
return SqliteResultCode::kOk;
}
bool BuiltInRecovery::RecoveredDbHasValidMetaTable() {
bool Recovery::RecoveredDbHasValidMetaTable() {
CHECK(recover_db_.is_open());
if (!MetaTable::DoesTableExist(&recover_db_)) {

@ -32,7 +32,7 @@ namespace sql {
// recovery should not be attempted on WAL databases for now.
//
// Uses SQLite's recovery extension: https://www.sqlite.org/recovery.html
class COMPONENT_EXPORT(SQL) BuiltInRecovery {
class COMPONENT_EXPORT(SQL) Recovery {
public:
enum class Strategy {
// Razes the database if it could not be recovered.
@ -137,9 +137,9 @@ class COMPONENT_EXPORT(SQL) BuiltInRecovery {
// Recommended usage from within a database error callback:
//
// // Attempt to recover the database, if recovery is possible.
// if (sql::BuiltInRecovery::RecoverIfPossible(
// if (sql::Recovery::RecoverIfPossible(
// &db, extended_error,
// sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze)) {
// sql::Recovery::Strategy::kRecoverWithMetaVersionOrRaze)) {
// // Recovery was attempted. The database handle has been poisoned and the
// // error callback has been reset.
//
@ -150,12 +150,12 @@ class COMPONENT_EXPORT(SQL) BuiltInRecovery {
int extended_error,
Strategy strategy);
BuiltInRecovery(const BuiltInRecovery&) = delete;
BuiltInRecovery& operator=(const BuiltInRecovery&) = delete;
Recovery(const Recovery&) = delete;
Recovery& operator=(const Recovery&) = delete;
private:
BuiltInRecovery(Database* database, Strategy strategy);
~BuiltInRecovery();
Recovery(Database* database, Strategy strategy);
~Recovery();
// Entry point.
SqliteResultCode RecoverAndReplaceDatabase();

@ -47,28 +47,27 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
bool should_attempt_recovery = false;
database.set_error_callback(
base::BindLambdaForTesting([&](int extended_error, sql::Statement*) {
should_attempt_recovery = sql::BuiltInRecovery::ShouldAttemptRecovery(
&database, extended_error);
should_attempt_recovery =
sql::Recovery::ShouldAttemptRecovery(&database, extended_error);
}));
std::ignore = database.Open(env.data_file_path());
// Select a recovery strategy pseudorandomly.
auto strategy =
size % 2 == 0
? sql::BuiltInRecovery::Strategy::kRecoverOrRaze
: sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze;
auto strategy = size % 2 == 0
? sql::Recovery::Strategy::kRecoverOrRaze
: sql::Recovery::Strategy::kRecoverWithMetaVersionOrRaze;
// Ensure that we remember to update the fuzzer if more strategies are added.
switch (strategy) {
case sql::BuiltInRecovery::Strategy::kRecoverOrRaze:
case sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze:
case sql::Recovery::Strategy::kRecoverOrRaze:
case sql::Recovery::Strategy::kRecoverWithMetaVersionOrRaze:
break;
}
// Attempt recovery.
if (should_attempt_recovery) {
database.reset_error_callback();
std::ignore = sql::BuiltInRecovery::RecoverDatabase(&database, strategy);
std::ignore = sql::Recovery::RecoverDatabase(&database, strategy);
}
return 0;

@ -116,28 +116,26 @@ INSTANTIATE_TEST_SUITE_P(All, SqlRecoveryTest, testing::Bool());
TEST_P(SqlRecoveryTest, ShouldAttemptRecovery) {
// Attempt to recover from corruption.
ASSERT_TRUE(BuiltInRecovery::ShouldAttemptRecovery(&db_, SQLITE_CORRUPT));
ASSERT_TRUE(Recovery::ShouldAttemptRecovery(&db_, SQLITE_CORRUPT));
// Do not attempt to recover from transient errors.
EXPECT_FALSE(BuiltInRecovery::ShouldAttemptRecovery(&db_, SQLITE_BUSY));
EXPECT_FALSE(Recovery::ShouldAttemptRecovery(&db_, SQLITE_BUSY));
// Do not attempt to recover null databases.
EXPECT_FALSE(BuiltInRecovery::ShouldAttemptRecovery(nullptr, SQLITE_CORRUPT));
EXPECT_FALSE(Recovery::ShouldAttemptRecovery(nullptr, SQLITE_CORRUPT));
// Do not attempt to recover closed databases.
Database invalid_db;
EXPECT_FALSE(
BuiltInRecovery::ShouldAttemptRecovery(&invalid_db, SQLITE_CORRUPT));
EXPECT_FALSE(Recovery::ShouldAttemptRecovery(&invalid_db, SQLITE_CORRUPT));
// Do not attempt to recover in-memory databases.
ASSERT_TRUE(invalid_db.OpenInMemory());
EXPECT_FALSE(
BuiltInRecovery::ShouldAttemptRecovery(&invalid_db, SQLITE_CORRUPT));
EXPECT_FALSE(Recovery::ShouldAttemptRecovery(&invalid_db, SQLITE_CORRUPT));
// Return true for databases which have an error callback set, even though
// the error callback should be reset before recovery is attempted.
db_.set_error_callback(base::DoNothing());
EXPECT_TRUE(BuiltInRecovery::ShouldAttemptRecovery(&db_, SQLITE_CORRUPT));
EXPECT_TRUE(Recovery::ShouldAttemptRecovery(&db_, SQLITE_CORRUPT));
}
TEST_P(SqlRecoveryTest, RecoverCorruptIndex) {
@ -168,11 +166,11 @@ TEST_P(SqlRecoveryTest, RecoverCorruptIndex) {
// Recovery::Begin() does not support a pre-existing error callback.
db_.reset_error_callback();
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
EXPECT_EQ(
Recovery::RecoverDatabase(&db_, Recovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName,
BuiltInRecovery::Result::kSuccess,
Recovery::Result::kSuccess,
/*expected_bucket_count=*/1);
histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
SqliteLoggedResultCode::kNoError,
@ -271,9 +269,9 @@ TEST_P(SqlRecoveryTest, RecoverCorruptTable) {
// Recovery::Begin() does not support a pre-existing error callback.
db_.reset_error_callback();
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
EXPECT_EQ(
Recovery::RecoverDatabase(&db_, Recovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
}));
// SUM(unindexed) heavily nudges SQLite to use the table instead of the index.
@ -305,11 +303,11 @@ TEST_P(SqlRecoveryTest, Meta) {
}
// Test expected case where everything works.
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze),
EXPECT_EQ(Recovery::RecoverDatabase(
&db_, Recovery::Strategy::kRecoverWithMetaVersionOrRaze),
SqliteResultCode::kOk);
histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName,
BuiltInRecovery::Result::kSuccess,
Recovery::Result::kSuccess,
/*expected_bucket_count=*/1);
histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
SqliteLoggedResultCode::kNoError,
@ -322,12 +320,12 @@ TEST_P(SqlRecoveryTest, Meta) {
// Test version row missing.
EXPECT_TRUE(db_.Execute("DELETE FROM meta WHERE key = 'version'"));
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze),
EXPECT_EQ(Recovery::RecoverDatabase(
&db_, Recovery::Strategy::kRecoverWithMetaVersionOrRaze),
SqliteResultCode::kError);
histogram_tester_.ExpectBucketCount(
kRecoveryResultHistogramName,
BuiltInRecovery::Result::kFailedMetaTableVersionWasInvalid,
Recovery::Result::kFailedMetaTableVersionWasInvalid,
/*expected_count=*/1);
histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
SqliteLoggedResultCode::kNoError,
@ -337,12 +335,12 @@ TEST_P(SqlRecoveryTest, Meta) {
// Test meta table missing.
ASSERT_FALSE(db_.DoesTableExist("meta"));
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze),
EXPECT_EQ(Recovery::RecoverDatabase(
&db_, Recovery::Strategy::kRecoverWithMetaVersionOrRaze),
SqliteResultCode::kError);
histogram_tester_.ExpectBucketCount(
kRecoveryResultHistogramName,
BuiltInRecovery::Result::kFailedMetaTableDoesNotExist,
Recovery::Result::kFailedMetaTableDoesNotExist,
/*expected_count=*/1);
histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
SqliteLoggedResultCode::kNoError,
@ -363,8 +361,7 @@ TEST_P(SqlRecoveryTest, AutoRecoverTable) {
static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n"));
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverOrRaze),
EXPECT_EQ(Recovery::RecoverDatabase(&db_, Recovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
// Since the database was not corrupt, the entire schema and all
@ -374,8 +371,7 @@ TEST_P(SqlRecoveryTest, AutoRecoverTable) {
ASSERT_EQ(orig_data, ExecuteWithResults(&db_, kXSql, "|", "\n"));
// Recovery succeeds silently, since there's nothing to do.
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverOrRaze),
EXPECT_EQ(Recovery::RecoverDatabase(&db_, Recovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
}
@ -405,8 +401,7 @@ TEST_P(SqlRecoveryTest, AutoRecoverTableWithDefault) {
std::string final_schema(orig_schema);
std::string final_data(orig_data);
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverOrRaze),
EXPECT_EQ(Recovery::RecoverDatabase(&db_, Recovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
// Since the database was not corrupt, the entire schema and all
@ -431,8 +426,7 @@ TEST_P(SqlRecoveryTest, AutoRecoverTableWithRowid) {
static const char kXSql[] = "SELECT * FROM x ORDER BY 1";
const std::string orig_data(ExecuteWithResults(&db_, kXSql, "|", "\n"));
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverOrRaze),
EXPECT_EQ(Recovery::RecoverDatabase(&db_, Recovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
// Since the database was not corrupt, the entire schema and all
@ -516,9 +510,9 @@ void TestRecoverDatabase(Database& db,
TEST_P(SqlRecoveryTest, RecoverDatabase) {
auto run_recovery = base::BindLambdaForTesting([&]() {
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
EXPECT_EQ(
Recovery::RecoverDatabase(&db_, Recovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
});
TestRecoverDatabase(db_, db_path_, /*with_meta=*/false,
@ -527,10 +521,9 @@ TEST_P(SqlRecoveryTest, RecoverDatabase) {
TEST_P(SqlRecoveryTest, RecoverDatabaseMeta) {
auto run_recovery = base::BindLambdaForTesting([&]() {
EXPECT_EQ(
BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze),
SqliteResultCode::kOk);
EXPECT_EQ(Recovery::RecoverDatabase(
&db_, Recovery::Strategy::kRecoverWithMetaVersionOrRaze),
SqliteResultCode::kOk);
});
TestRecoverDatabase(db_, db_path_, /*with_meta=*/true,
@ -539,8 +532,8 @@ TEST_P(SqlRecoveryTest, RecoverDatabaseMeta) {
TEST_P(SqlRecoveryTest, RecoverIfPossible) {
auto run_recovery = base::BindLambdaForTesting([&]() {
EXPECT_TRUE(BuiltInRecovery::RecoverIfPossible(
&db_, SQLITE_CORRUPT, BuiltInRecovery::Strategy::kRecoverOrRaze));
EXPECT_TRUE(Recovery::RecoverIfPossible(
&db_, SQLITE_CORRUPT, Recovery::Strategy::kRecoverOrRaze));
});
TestRecoverDatabase(db_, db_path_, /*with_meta=*/false,
@ -549,9 +542,9 @@ TEST_P(SqlRecoveryTest, RecoverIfPossible) {
TEST_P(SqlRecoveryTest, RecoverIfPossibleMeta) {
auto run_recovery = base::BindLambdaForTesting([&]() {
EXPECT_TRUE(BuiltInRecovery::RecoverIfPossible(
EXPECT_TRUE(Recovery::RecoverIfPossible(
&db_, SQLITE_CORRUPT,
BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze));
Recovery::Strategy::kRecoverWithMetaVersionOrRaze));
});
TestRecoverDatabase(db_, db_path_, /*with_meta=*/true,
@ -562,9 +555,9 @@ TEST_P(SqlRecoveryTest, RecoverIfPossibleWithoutErrorCallback) {
auto run_recovery = base::BindLambdaForTesting([&]() {
// `RecoverIfPossible()` should not set an error callback.
EXPECT_FALSE(db_.has_error_callback());
bool recovery_was_attempted = BuiltInRecovery::RecoverIfPossible(
bool recovery_was_attempted = Recovery::RecoverIfPossible(
&db_, SQLITE_CORRUPT,
BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze);
Recovery::Strategy::kRecoverWithMetaVersionOrRaze);
EXPECT_TRUE(recovery_was_attempted);
EXPECT_FALSE(db_.has_error_callback());
});
@ -578,9 +571,9 @@ TEST_P(SqlRecoveryTest, RecoverIfPossibleWithErrorCallback) {
db_.set_error_callback(base::DoNothing());
// The error callback should be reset during `RecoverIfPossible()` if
// recovery was attempted.
bool recovery_was_attempted = BuiltInRecovery::RecoverIfPossible(
bool recovery_was_attempted = Recovery::RecoverIfPossible(
&db_, SQLITE_CORRUPT,
BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze);
Recovery::Strategy::kRecoverWithMetaVersionOrRaze);
EXPECT_TRUE(recovery_was_attempted);
EXPECT_NE(db_.has_error_callback(), recovery_was_attempted);
});
@ -594,8 +587,8 @@ TEST_P(SqlRecoveryTest, RecoverIfPossibleWithClosedDatabase) {
// Recovery should not be attempted on a closed database.
db_.Close();
EXPECT_FALSE(BuiltInRecovery::RecoverIfPossible(
&db_, SQLITE_CORRUPT, BuiltInRecovery::Strategy::kRecoverOrRaze));
EXPECT_FALSE(Recovery::RecoverIfPossible(
&db_, SQLITE_CORRUPT, Recovery::Strategy::kRecoverOrRaze));
});
TestRecoverDatabase(db_, db_path_, /*with_meta=*/false,
@ -604,8 +597,8 @@ TEST_P(SqlRecoveryTest, RecoverIfPossibleWithClosedDatabase) {
TEST_P(SqlRecoveryTest, RecoverIfPossibleWithPerDatabaseUma) {
auto run_recovery = base::BindLambdaForTesting([&]() {
EXPECT_TRUE(BuiltInRecovery::RecoverIfPossible(
&db_, SQLITE_CORRUPT, BuiltInRecovery::Strategy::kRecoverOrRaze));
EXPECT_TRUE(Recovery::RecoverIfPossible(
&db_, SQLITE_CORRUPT, Recovery::Strategy::kRecoverOrRaze));
});
TestRecoverDatabase(db_, db_path_, /*with_meta=*/false,
@ -613,7 +606,7 @@ TEST_P(SqlRecoveryTest, RecoverIfPossibleWithPerDatabaseUma) {
// Log to the overall histograms.
histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName,
BuiltInRecovery::Result::kSuccess,
Recovery::Result::kSuccess,
/*expected_bucket_count=*/1);
histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
SqliteLoggedResultCode::kNoError,
@ -621,7 +614,7 @@ TEST_P(SqlRecoveryTest, RecoverIfPossibleWithPerDatabaseUma) {
// And the histograms for this specific feature.
histogram_tester_.ExpectUniqueSample(
base::StrCat({kRecoveryResultHistogramName, ".MyFeatureDatabase"}),
BuiltInRecovery::Result::kSuccess,
Recovery::Result::kSuccess,
/*expected_bucket_count=*/1);
histogram_tester_.ExpectUniqueSample(
base::StrCat({kRecoveryResultCodeHistogramName, ".MyFeatureDatabase"}),
@ -665,8 +658,7 @@ TEST_P(SqlRecoveryTest, RecoverDatabaseWithView) {
// Database handle is valid before recovery, poisoned after.
static constexpr char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_schema";
EXPECT_TRUE(db.IsSQLValid(kTrivialSql));
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db, BuiltInRecovery::Strategy::kRecoverOrRaze),
EXPECT_EQ(Recovery::RecoverDatabase(&db, Recovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
EXPECT_FALSE(db.IsSQLValid(kTrivialSql));
@ -694,13 +686,12 @@ TEST_P(SqlRecoveryTest, RecoverDatabaseDelete) {
ASSERT_FALSE(Reopen());
// This should "recover" the database by making it valid, but empty.
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kNotADatabase);
histogram_tester_.ExpectUniqueSample(
kRecoveryResultHistogramName,
BuiltInRecovery::Result::kFailedRecoveryRun,
/*expected_bucket_count=*/1);
EXPECT_EQ(
Recovery::RecoverDatabase(&db_, Recovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kNotADatabase);
histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName,
Recovery::Result::kFailedRecoveryRun,
/*expected_bucket_count=*/1);
histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
SqliteLoggedResultCode::kNotADatabase,
/*expected_bucket_count=*/1);
@ -745,8 +736,7 @@ TEST_P(SqlRecoveryTest, BeginRecoverDatabase) {
}
// Run recovery code, then commit. The index is recovered.
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverOrRaze),
EXPECT_EQ(Recovery::RecoverDatabase(&db_, Recovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
db_.Close();
ASSERT_TRUE(Reopen());
@ -771,13 +761,12 @@ TEST_P(SqlRecoveryTest, AttachFailure) {
ASSERT_FALSE(Reopen());
// Begin() should fail.
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kNotADatabase);
histogram_tester_.ExpectUniqueSample(
kRecoveryResultHistogramName,
BuiltInRecovery::Result::kFailedRecoveryRun,
/*expected_bucket_count=*/1);
EXPECT_EQ(
Recovery::RecoverDatabase(&db_, Recovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kNotADatabase);
histogram_tester_.ExpectUniqueSample(kRecoveryResultHistogramName,
Recovery::Result::kFailedRecoveryRun,
/*expected_bucket_count=*/1);
histogram_tester_.ExpectUniqueSample(kRecoveryResultCodeHistogramName,
SqliteLoggedResultCode::kNotADatabase,
/*expected_bucket_count=*/1);
@ -816,8 +805,8 @@ void TestPageSize(const base::FilePath& db_prefix,
ASSERT_TRUE(recover_db.Open(db_path));
// Recovery will use the page size set in the database object, which may not
// match the file's page size.
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&recover_db, BuiltInRecovery::Strategy::kRecoverOrRaze),
EXPECT_EQ(Recovery::RecoverDatabase(&recover_db,
Recovery::Strategy::kRecoverOrRaze),
SqliteResultCode::kOk);
// Recovery poisoned the handle, must re-open.
@ -864,15 +853,15 @@ TEST_P(SqlRecoveryTest, PageSize) {
TEST_P(SqlRecoveryTest, CannotRecoverClosedDb) {
db_.Close();
EXPECT_CHECK_DEATH(std::ignore = BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverOrRaze));
EXPECT_CHECK_DEATH(std::ignore = Recovery::RecoverDatabase(
&db_, Recovery::Strategy::kRecoverOrRaze));
}
TEST_P(SqlRecoveryTest, CannotRecoverDbWithErrorCallback) {
db_.set_error_callback(base::DoNothing());
EXPECT_CHECK_DEATH(std::ignore = BuiltInRecovery::RecoverDatabase(
&db_, BuiltInRecovery::Strategy::kRecoverOrRaze));
EXPECT_CHECK_DEATH(std::ignore = Recovery::RecoverDatabase(
&db_, Recovery::Strategy::kRecoverOrRaze));
}
// TODO(https://crbug.com/1255316): Ideally this would be a
@ -880,8 +869,8 @@ TEST_P(SqlRecoveryTest, CannotRecoverDbWithErrorCallback) {
// that it is passed a non-null database pointer and will instead likely result
// in unexpected behavior or crashes.
TEST_P(SqlRecoveryTest, CannotRecoverNullDb) {
EXPECT_CHECK_DEATH(std::ignore = BuiltInRecovery::RecoverDatabase(
nullptr, BuiltInRecovery::Strategy::kRecoverOrRaze));
EXPECT_CHECK_DEATH(std::ignore = Recovery::RecoverDatabase(
nullptr, Recovery::Strategy::kRecoverOrRaze));
}
// TODO(https://crbug.com/1255316): Ideally this would be a
@ -892,9 +881,8 @@ TEST_P(SqlRecoveryTest, CannotRecoverInMemoryDb) {
Database in_memory_db;
ASSERT_TRUE(in_memory_db.OpenInMemory());
EXPECT_CHECK_DEATH(
std::ignore = BuiltInRecovery::RecoverDatabase(
&in_memory_db, BuiltInRecovery::Strategy::kRecoverOrRaze));
EXPECT_CHECK_DEATH(std::ignore = Recovery::RecoverDatabase(
&in_memory_db, Recovery::Strategy::kRecoverOrRaze));
}
// This test mimics the case where a database that was using WAL mode crashed,
@ -925,10 +913,10 @@ TEST_P(SqlRecoveryTest, RecoverFormerlyWalDbAfterCrash) {
ASSERT_TRUE(non_wal_db.Open(wal_db_path));
auto run_recovery = base::BindLambdaForTesting([&]() {
EXPECT_EQ(BuiltInRecovery::RecoverDatabase(
&non_wal_db,
BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze),
SqliteResultCode::kOk);
EXPECT_EQ(
Recovery::RecoverDatabase(
&non_wal_db, Recovery::Strategy::kRecoverWithMetaVersionOrRaze),
SqliteResultCode::kOk);
});
TestRecoverDatabase(non_wal_db, wal_db_path, /*with_meta=*/true,

@ -807,9 +807,9 @@ QuotaError QuotaDatabase::SetIsBootstrapped(bool bootstrap_flag) {
bool QuotaDatabase::RecoverOrRaze(int error_code) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::ignore = sql::BuiltInRecovery::RecoverIfPossible(
std::ignore = sql::Recovery::RecoverIfPossible(
db_.get(), error_code,
sql::BuiltInRecovery::Strategy::kRecoverWithMetaVersionOrRaze);
sql::Recovery::Strategy::kRecoverWithMetaVersionOrRaze);
db_.reset();
EnsureOpened();

@ -230,8 +230,8 @@ chromium-metrics-reviews@google.com.
<enum name="SqlRecoveryResult">
<summary>
Outcome of attempting to recover a database with sql::BuiltInRecovery. See
sql::BuiltInRecovery::Result for descriptions.
Outcome of attempting to recover a database with sql::Recovery. See
sql::Recovery::Result for descriptions.
</summary>
<int value="0" label="kUnknown"/>
<int value="1" label="kSuccess"/>

@ -272,7 +272,7 @@ chromium-metrics-reviews@google.com.
<owner>asully@chromium.org</owner>
<owner>chrome-owp-storage@google.com</owner>
<summary>
Outcome of attempting to recover a database with sql::BuiltInRecovery.
Outcome of attempting to recover a database with sql::Recovery.
</summary>
</histogram>
@ -282,7 +282,7 @@ chromium-metrics-reviews@google.com.
<owner>chrome-owp-storage@google.com</owner>
<summary>
Outcome of attempting to recover the {DatabaseTag} database with
sql::BuiltInRecovery.
sql::Recovery.
</summary>
<token key="DatabaseTag" variants="DatabaseTag"/>
</histogram>
@ -292,11 +292,11 @@ chromium-metrics-reviews@google.com.
<owner>asully@chromium.org</owner>
<owner>chrome-owp-storage@google.com</owner>
<summary>
SQLite result code from attempting to recover a database with
sql::BuiltInRecovery. Note that kNoError does not necessarily indicate that
recovery succeeded (see Sql.Recovery.Result for that information), since not
all recoveries fail due to SQLite errors (e.g. if a version number could not
be read from the meta table).
SQLite result code from attempting to recover a database with sql::Recovery.
Note that kNoError does not necessarily indicate that recovery succeeded
(see Sql.Recovery.Result for that information), since not all recoveries
fail due to SQLite errors (e.g. if a version number could not be read from
the meta table).
</summary>
</histogram>
@ -306,10 +306,10 @@ chromium-metrics-reviews@google.com.
<owner>chrome-owp-storage@google.com</owner>
<summary>
SQLite result code from attempting to recover the {DatabaseTag} database
with sql::BuiltInRecovery. Note that kNoError does not necessarily indicate
that recovery succeeded (see Sql.Recovery.Result for that information),
since not all recoveries fail due to SQLite errors (e.g. if a version number
could not be read from the meta table).
with sql::Recovery. Note that kNoError does not necessarily indicate that
recovery succeeded (see Sql.Recovery.Result for that information), since not
all recoveries fail due to SQLite errors (e.g. if a version number could not
be read from the meta table).
</summary>
<token key="DatabaseTag" variants="DatabaseTag"/>
</histogram>