diff --git a/components/brave_rewards/browser/database/database_contribution_queue.cc b/components/brave_rewards/browser/database/database_contribution_queue.cc index 3448b8d014a4..b7f81b8cc1fe 100644 --- a/components/brave_rewards/browser/database/database_contribution_queue.cc +++ b/components/brave_rewards/browser/database/database_contribution_queue.cc @@ -49,6 +49,14 @@ bool DatabaseContributionQueue::Init(sql::Database* db) { return false; } + // We need to clear queue if user has corrupted foreign key + // more info: https://github.com/brave/brave-browser/issues/7579 + if (publishers_->HasCorruptedForeignKey(db)) { + if (!DeleteAllRecords(db)) { + return false; + } + } + return transaction.Commit(); } @@ -148,8 +156,13 @@ ledger::ContributionQueuePtr DatabaseContributionQueue::GetFirstRecord( bool DatabaseContributionQueue::DeleteRecord( sql::Database* db, const uint64_t id) { - if (!db->Execute("PRAGMA foreign_keys=1;")) { - LOG(ERROR) << "Error: deleting record for contribution queue with id" << id; + DCHECK(db); + if (id == 0 || !db) { + return false; + } + + sql::Transaction transaction(db); + if (!transaction.Begin()) { return false; } @@ -163,11 +176,41 @@ bool DatabaseContributionQueue::DeleteRecord( bool success = statement.Run(); - if (!db->Execute("PRAGMA foreign_keys=0;")) { + if (!success) { + return false; + } + + if (!publishers_->DeleteRecordsByQueueId(db, id)) { + return false; + } + + return transaction.Commit(); +} + +bool DatabaseContributionQueue::DeleteAllRecords(sql::Database* db) { + DCHECK(db); + if (!db) { return false; } - return success; + sql::Transaction transaction(db); + if (!transaction.Begin()) { + return false; + } + + const std::string query = base::StringPrintf("DELETE FROM %s", table_name_); + sql::Statement statement(db->GetUniqueStatement(query.c_str())); + bool success = statement.Run(); + + if (!success) { + return false; + } + + if (!publishers_->DeleteAllRecords(db)) { + return false; + } + + return transaction.Commit(); } } // namespace brave_rewards diff --git a/components/brave_rewards/browser/database/database_contribution_queue.h b/components/brave_rewards/browser/database/database_contribution_queue.h index 06f87636005e..449d32c50e29 100644 --- a/components/brave_rewards/browser/database/database_contribution_queue.h +++ b/components/brave_rewards/browser/database/database_contribution_queue.h @@ -32,6 +32,8 @@ class DatabaseContributionQueue: public DatabaseTable { bool DeleteRecord(sql::Database* db, const uint64_t id); + bool DeleteAllRecords(sql::Database* db); + private: std::string GetIdColumnName(); diff --git a/components/brave_rewards/browser/database/database_contribution_queue_publishers.cc b/components/brave_rewards/browser/database/database_contribution_queue_publishers.cc index b26709d22917..9e8d2b3d672d 100644 --- a/components/brave_rewards/browser/database/database_contribution_queue_publishers.cc +++ b/components/brave_rewards/browser/database/database_contribution_queue_publishers.cc @@ -13,6 +13,10 @@ #include "sql/statement.h" #include "sql/transaction.h" +namespace { + const char kCorruptedForeignKey[] = "publisher_info_old"; +} // namespace + namespace brave_rewards { DatabaseContributionQueuePublishers::DatabaseContributionQueuePublishers( @@ -121,4 +125,56 @@ DatabaseContributionQueuePublishers::GetRecords( return list; } +bool DatabaseContributionQueuePublishers::DeleteRecordsByQueueId( + sql::Database* db, + const uint64_t queue_id) { + DCHECK(db); + if (queue_id == 0 || !db) { + return false; + } + + const std::string query = base::StringPrintf( + "DELETE FROM %s WHERE %s_id = ?", + table_name_, + parent_table_name_); + + sql::Statement statement(db->GetUniqueStatement(query.c_str())); + statement.BindInt64(0, queue_id); + + return statement.Run(); +} + +bool DatabaseContributionQueuePublishers::HasCorruptedForeignKey( + sql::Database* db) { + DCHECK(db); + if (!db) { + return false; + } + + const std::string query = base::StringPrintf( + "PRAGMA foreign_key_list(%s)", + table_name_); + + sql::Statement statement(db->GetUniqueStatement(query.c_str())); + + while (statement.Step()) { + if (statement.ColumnString(2) == kCorruptedForeignKey) { + return true; + } + } + + return false; +} + +bool DatabaseContributionQueuePublishers::DeleteAllRecords(sql::Database* db) { + DCHECK(db); + if (!db) { + return false; + } + + const std::string query = base::StringPrintf("DELETE FROM %s", table_name_); + sql::Statement statement(db->GetUniqueStatement(query.c_str())); + return statement.Run(); +} + } // namespace brave_rewards diff --git a/components/brave_rewards/browser/database/database_contribution_queue_publishers.h b/components/brave_rewards/browser/database/database_contribution_queue_publishers.h index 0e7f2b9f5eb5..47a55dca4f9b 100644 --- a/components/brave_rewards/browser/database/database_contribution_queue_publishers.h +++ b/components/brave_rewards/browser/database/database_contribution_queue_publishers.h @@ -33,6 +33,12 @@ class DatabaseContributionQueuePublishers: public DatabaseTable { sql::Database* db, const uint64_t queue_id); + bool DeleteRecordsByQueueId(sql::Database* db, const uint64_t queue_id); + + bool HasCorruptedForeignKey(sql::Database* db); + + bool DeleteAllRecords(sql::Database* db); + private: const char* table_name_ = "contribution_queue_publishers"; const int minimum_version_ = 9; diff --git a/components/brave_rewards/browser/database/publisher_info_database_unittest.cc b/components/brave_rewards/browser/database/publisher_info_database_unittest.cc index bb6ca215f5e5..93c96131526d 100644 --- a/components/brave_rewards/browser/database/publisher_info_database_unittest.cc +++ b/components/brave_rewards/browser/database/publisher_info_database_unittest.cc @@ -61,6 +61,16 @@ class PublisherInfoDatabaseTest : public ::testing::Test { int end_version) { const std::string file_name = "publisher_info_db_v" + std::to_string(start_version); + + LoadDatabaseFile(temp_dir, db_file, file_name, "migration", end_version); + } + + void LoadDatabaseFile( + base::ScopedTempDir* temp_dir, + base::FilePath* db_file, + const std::string& file_name, + const std::string& folder, + int end_version) { ASSERT_TRUE(temp_dir->CreateUniqueTempDir()); *db_file = temp_dir->GetPath().AppendASCII(file_name); @@ -69,7 +79,7 @@ class PublisherInfoDatabaseTest : public ::testing::Test { ASSERT_TRUE(base::PathService::Get(brave::DIR_TEST_DATA, &path)); path = path.AppendASCII("rewards-data"); ASSERT_TRUE(base::PathExists(path)); - path = path.AppendASCII("migration"); + path = path.AppendASCII(folder); ASSERT_TRUE(base::PathExists(path)); path = path.AppendASCII(file_name); ASSERT_TRUE(base::PathExists(path)); @@ -1541,4 +1551,36 @@ TEST_F(PublisherInfoDatabaseTest, RemoveAllPendingContributions) { EXPECT_EQ(CountTableRows("pending_contribution"), 0); } +TEST_F(PublisherInfoDatabaseTest, + ContributionQueueKeyCorrupted) { + base::ScopedTempDir temp_dir; + base::FilePath db_file; + LoadDatabaseFile( + &temp_dir, + &db_file, + "contribution_queue_key_corrupted", + "database", + 9); + EXPECT_TRUE(publisher_info_database_->Init()); + + EXPECT_EQ(CountTableRows("contribution_queue"), 0); + EXPECT_EQ(CountTableRows("contribution_queue_publishers"), 0); +} + +TEST_F(PublisherInfoDatabaseTest, + ContributionQueueKeyOk) { + base::ScopedTempDir temp_dir; + base::FilePath db_file; + LoadDatabaseFile( + &temp_dir, + &db_file, + "contribution_queue_key_ok", + "database", + 9); + EXPECT_TRUE(publisher_info_database_->Init()); + + EXPECT_EQ(CountTableRows("contribution_queue"), 1); + EXPECT_EQ(CountTableRows("contribution_queue_publishers"), 1); +} + } // namespace brave_rewards diff --git a/test/data/rewards-data/database/contribution_queue_key_corrupted b/test/data/rewards-data/database/contribution_queue_key_corrupted new file mode 100644 index 000000000000..39b634c12767 Binary files /dev/null and b/test/data/rewards-data/database/contribution_queue_key_corrupted differ diff --git a/test/data/rewards-data/database/contribution_queue_key_ok b/test/data/rewards-data/database/contribution_queue_key_ok new file mode 100644 index 000000000000..9815d5fb2eeb Binary files /dev/null and b/test/data/rewards-data/database/contribution_queue_key_ok differ