Database Transaction Guard

This commit is contained in:
HikikoMarmy
2025-07-03 16:55:06 +01:00
parent 6d4269485f
commit 0bbe5f1d1e
3 changed files with 169 additions and 72 deletions

View File

@@ -158,6 +158,8 @@ void Database::DeleteOldSessions()
{ {
auto stmt = m_statements[ QueryID::DeleteOldSessions ]; auto stmt = m_statements[ QueryID::DeleteOldSessions ];
SQLiteTransaction tx( m_db );
{
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
@@ -171,6 +173,8 @@ void Database::DeleteOldSessions()
Log::Info( "Old sessions deleted successfully." ); Log::Info( "Old sessions deleted successfully." );
} }
tx.commit();
}
catch( const std::exception &e ) catch( const std::exception &e )
{ {
Log::Error( "Database error: {}", std::string( e.what() ) ); Log::Error( "Database error: {}", std::string( e.what() ) );
@@ -188,6 +192,8 @@ int64_t Database::CreateNewAccount( const std::string &username,
auto hashedPassword = HashPassword( password, 1000, 32 ); auto hashedPassword = HashPassword( password, 1000, 32 );
auto stmt = m_statements[ QueryID::CreateAccount ]; auto stmt = m_statements[ QueryID::CreateAccount ];
SQLiteTransaction tx( m_db );
{
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
@@ -201,6 +207,8 @@ int64_t Database::CreateNewAccount( const std::string &username,
{ {
throw std::runtime_error( "Insert failed: " + std::string( sqlite3_errmsg( m_db ) ) ); throw std::runtime_error( "Insert failed: " + std::string( sqlite3_errmsg( m_db ) ) );
} }
}
tx.commit();
return sqlite3_last_insert_rowid( m_db ); return sqlite3_last_insert_rowid( m_db );
} }
@@ -312,6 +320,8 @@ bool Database::CreateSession( const int64_t account_id, const std::wstring &sess
auto stmt = m_statements[ QueryID::CreateSession ]; auto stmt = m_statements[ QueryID::CreateSession ];
auto sessionId = Util::WideToUTF8( session_id ); auto sessionId = Util::WideToUTF8( session_id );
SQLiteTransaction tx( m_db );
{
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
@@ -326,6 +336,8 @@ bool Database::CreateSession( const int64_t account_id, const std::wstring &sess
Log::Error( "SQLite insert failed: {}", sqlite3_errmsg( m_db ) ); Log::Error( "SQLite insert failed: {}", sqlite3_errmsg( m_db ) );
return false; return false;
} }
}
tx.commit();
return true; return true;
} }
@@ -344,6 +356,8 @@ bool Database::UpdateSession( const std::wstring &session_id, const uint32_t cha
auto stmt = m_statements[ QueryID::UpdateSession ]; auto stmt = m_statements[ QueryID::UpdateSession ];
auto sessionId = Util::WideToUTF8( session_id ); auto sessionId = Util::WideToUTF8( session_id );
SQLiteTransaction tx( m_db );
{
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
@@ -356,6 +370,8 @@ bool Database::UpdateSession( const std::wstring &session_id, const uint32_t cha
Log::Error( "SQLite update failed: {}", sqlite3_errmsg( m_db ) ); Log::Error( "SQLite update failed: {}", sqlite3_errmsg( m_db ) );
return false; return false;
} }
}
tx.commit();
Log::Debug( "Session updated: {}", session_id ); Log::Debug( "Session updated: {}", session_id );
return true; return true;
@@ -375,6 +391,8 @@ bool Database::DeleteSession( const std::wstring &session_id )
auto stmt = m_statements[ QueryID::DeleteSession ]; auto stmt = m_statements[ QueryID::DeleteSession ];
auto sessionId = Util::WideToUTF8( session_id ); auto sessionId = Util::WideToUTF8( session_id );
SQLiteTransaction tx( m_db );
{
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
@@ -385,6 +403,8 @@ bool Database::DeleteSession( const std::wstring &session_id )
Log::Error( "SQLite delete failed: {}", sqlite3_errmsg( m_db ) ); Log::Error( "SQLite delete failed: {}", sqlite3_errmsg( m_db ) );
return false; return false;
} }
}
tx.commit();
Log::Debug( "Session deleted: {}", session_id ); Log::Debug( "Session deleted: {}", session_id );
return true; return true;
@@ -451,6 +471,8 @@ uint32_t Database::CreateNewCharacter( const int64_t account_id, const Character
auto stmt = m_statements[ QueryID::CreateNewCharacter ]; auto stmt = m_statements[ QueryID::CreateNewCharacter ];
const auto meta_data = meta.Serialize(); const auto meta_data = meta.Serialize();
SQLiteTransaction tx( m_db );
{
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
@@ -464,6 +486,8 @@ uint32_t Database::CreateNewCharacter( const int64_t account_id, const Character
Log::Error( "SQLite insert failed: {}", sqlite3_errmsg( m_db ) ); Log::Error( "SQLite insert failed: {}", sqlite3_errmsg( m_db ) );
return 0; return 0;
} }
}
tx.commit();
uint32_t character_id = static_cast< uint32_t >( sqlite3_last_insert_rowid( m_db ) ); uint32_t character_id = static_cast< uint32_t >( sqlite3_last_insert_rowid( m_db ) );
@@ -490,6 +514,8 @@ bool Database::SaveCharacter( const int64_t account_id, const int32_t character_
auto stmt = m_statements[ QueryID::SaveCharacter ]; auto stmt = m_statements[ QueryID::SaveCharacter ];
const auto meta_data = meta.Serialize(); const auto meta_data = meta.Serialize();
SQLiteTransaction tx( m_db );
{
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
@@ -502,6 +528,8 @@ bool Database::SaveCharacter( const int64_t account_id, const int32_t character_
{ {
throw std::runtime_error( "Update failed: " + std::string( sqlite3_errmsg( m_db ) ) ); throw std::runtime_error( "Update failed: " + std::string( sqlite3_errmsg( m_db ) ) );
} }
}
tx.commit();
return true; return true;
} }

View File

@@ -8,6 +8,7 @@
#include <map> #include <map>
#include <sqlite3.h> #include <sqlite3.h>
#include "Transaction.h"
#include "../../Game/RealmCharacter.h" #include "../../Game/RealmCharacter.h"
#include "../../Game/RealmCharacterMetaKV.h" #include "../../Game/RealmCharacterMetaKV.h"

68
Database/Transaction.h Normal file
View File

@@ -0,0 +1,68 @@
#pragma once
#include <sqlite3.h>
#include <stdexcept>
class SQLiteTransaction {
sqlite3 *m_db = nullptr;
bool m_committed = false;
public:
explicit SQLiteTransaction( sqlite3 *db )
: m_db( db )
{
if( !m_db )
throw std::invalid_argument( "Null SQLite database pointer" );
if( sqlite3_exec( m_db, "BEGIN TRANSACTION;", nullptr, nullptr, nullptr ) != SQLITE_OK )
throw std::runtime_error( "Failed to begin transaction: " + std::string( sqlite3_errmsg( m_db ) ) );
}
~SQLiteTransaction()
{
if( m_committed || !m_db )
return;
sqlite3_exec( m_db, "ROLLBACK;", nullptr, nullptr, nullptr );
}
SQLiteTransaction( const SQLiteTransaction & ) = delete;
SQLiteTransaction &operator=( const SQLiteTransaction & ) = delete;
SQLiteTransaction( SQLiteTransaction &&other ) noexcept
: m_db( other.m_db ), m_committed( other.m_committed )
{
other.m_db = nullptr;
}
SQLiteTransaction &operator=( SQLiteTransaction &&other ) noexcept
{
if( this != &other )
{
commitOrRollback();
m_db = other.m_db;
m_committed = other.m_committed;
other.m_db = nullptr;
}
return *this;
}
void commit()
{
if( m_committed || !m_db )
return;
if( sqlite3_exec( m_db, "COMMIT;", nullptr, nullptr, nullptr ) != SQLITE_OK )
throw std::runtime_error( "Failed to commit transaction: " + std::string( sqlite3_errmsg( m_db ) ) );
m_committed = true;
}
private:
void commitOrRollback()
{
if( m_committed || !m_db )
return;
sqlite3_exec( m_db, "ROLLBACK;", nullptr, nullptr, nullptr );
}
};