From 5cf3434679a6104cd5a7655be5e9655f702080a5 Mon Sep 17 00:00:00 2001 From: Gent Semaj Date: Sun, 7 Dec 2025 13:46:57 -0800 Subject: [PATCH 1/2] Delete attachments on email delete --- src/db/email.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/db/email.cpp b/src/db/email.cpp index a4c3b6901..297b7662c 100644 --- a/src/db/email.cpp +++ b/src/db/email.cpp @@ -196,18 +196,16 @@ void Database::updateEmailContent(EmailData* data) { sqlite3_finalize(stmt); } -void Database::deleteEmailAttachments(int playerID, int index, int slot) { - std::lock_guard lock(dbCrit); - +static void _deleteEmailAttachments(int playerID, int index, int slot) { sqlite3_stmt* stmt; std::string sql(R"( DELETE FROM EmailItems - WHERE PlayerID = ? AND MsgIndex = ?; + WHERE PlayerID = ? AND MsgIndex = ? )"); if (slot != -1) - sql += " AND \"Slot\" = ? "; + sql += " AND \"Slot\" = ?"; sql += ";"; sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, NULL); @@ -221,6 +219,11 @@ void Database::deleteEmailAttachments(int playerID, int index, int slot) { sqlite3_finalize(stmt); } +void Database::deleteEmailAttachments(int playerID, int index, int slot) { + std::lock_guard lock(dbCrit); + _deleteEmailAttachments(playerID, index, slot); +} + void Database::deleteEmails(int playerID, int64_t* indices) { std::lock_guard lock(dbCrit); @@ -234,12 +237,15 @@ void Database::deleteEmails(int playerID, int64_t* indices) { sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); for (int i = 0; i < 5; i++) { + int64_t msgIndex = indices[i]; sqlite3_bind_int(stmt, 1, playerID); - sqlite3_bind_int64(stmt, 2, indices[i]); + sqlite3_bind_int64(stmt, 2, msgIndex); if (sqlite3_step(stmt) != SQLITE_DONE) { std::cout << "[WARN] Database: Failed to delete an email: " << sqlite3_errmsg(db) << std::endl; } sqlite3_reset(stmt); + // delete all attachments + _deleteEmailAttachments(playerID, msgIndex, -1); } sqlite3_finalize(stmt); From 79add84ecc776f5424e97e74b828c75fc5488c75 Mon Sep 17 00:00:00 2001 From: Gent Semaj Date: Sun, 7 Dec 2025 13:47:29 -0800 Subject: [PATCH 2/2] Work around orphaned email attachments --- src/db/email.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/db/email.cpp b/src/db/email.cpp index 297b7662c..7eb8153d9 100644 --- a/src/db/email.cpp +++ b/src/db/email.cpp @@ -329,12 +329,23 @@ bool Database::sendEmail(EmailData* data, std::vector attachments, Pl sqlite3_bind_int(stmt, 7, item.iTimeLimit); if (sqlite3_step(stmt) != SQLITE_DONE) { - std::cout << "[WARN] Database: Failed to send email: " << sqlite3_errmsg(db) << std::endl; - sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); - sqlite3_finalize(stmt); - return false; + // very likely the UNIQUE constraint failing due to + // orphaned attachments from an old email. + // try deleting them first + _deleteEmailAttachments(data->PlayerId, data->MsgIndex, -1); + + // try again + sqlite3_reset(stmt); + if (sqlite3_step(stmt) != SQLITE_DONE) { + // different error, give up + std::cout << "[WARN] Database: Failed to send email: " << sqlite3_errmsg(db) << std::endl; + sqlite3_exec(db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL); + sqlite3_finalize(stmt); + return false; + } } sqlite3_reset(stmt); + sqlite3_clear_bindings(stmt); } sqlite3_finalize(stmt);