diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a63563983..35cb7eb7f 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -39,6 +39,8 @@ SET(common_sources MiscFunctions.cpp moremath.cpp Mutex.cpp + MySQLRequestResult.cpp + MySQLRequestRow.cpp opcode_map.cpp opcodemgr.cpp packet_dump.cpp @@ -149,6 +151,8 @@ SET(common_headers MiscFunctions.h moremath.h Mutex.h + MySQLRequestResult.h + MySQLRequestRow.h op_codes.h opcode_dispatch.h opcodemgr.h diff --git a/common/MySQLRequestResult.cpp b/common/MySQLRequestResult.cpp new file mode 100644 index 000000000..16d7fe9a0 --- /dev/null +++ b/common/MySQLRequestResult.cpp @@ -0,0 +1,103 @@ +#include "MySQLRequestResult.h" + + +MySQLRequestResult::MySQLRequestResult() + : m_CurrentRow(), m_OneBeyondRow() +{ + ZeroOut(); +} + +MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, uint32 rowCount, uint32 columnCount, uint32 lastInsertedID, uint32 errorNumber, char *errorBuffer) + : m_CurrentRow(result), m_OneBeyondRow() +{ + if (errorBuffer != nullptr) + m_Success = false; + else if (errorBuffer == nullptr && result == nullptr) + { + m_Success = false; + +#ifdef _EQDEBUG + std::cout << "DB Query Error: No Result" << std::endl; +#endif + m_ErrorNumber = UINT_MAX; + m_ErrorBuffer = new char[MYSQL_ERRMSG_SIZE]; + + strcpy(m_ErrorBuffer, "DBcore::RunQuery: No Result"); + } + else + m_Success = true; + + m_Result = result; + m_ErrorBuffer = errorBuffer; + m_RowsAffected = rowsAffected; + m_RowCount = rowCount; + m_ColumnCount = columnCount; + m_LastInsertedID = lastInsertedID; + m_ErrorNumber = errorNumber; +} + +void MySQLRequestResult::FreeInternals() +{ + safe_delete_array(m_ErrorBuffer); + + if (m_Result != nullptr) + mysql_free_result(m_Result); + + ZeroOut(); +} + +void MySQLRequestResult::ZeroOut() +{ + m_Success = false; + m_Result = nullptr; + m_ErrorBuffer = nullptr; + m_RowCount = 0; + m_RowsAffected = 0; + m_LastInsertedID = 0; +} + +MySQLRequestResult::~MySQLRequestResult() +{ + FreeInternals(); +} + +MySQLRequestResult::MySQLRequestResult(MySQLRequestResult&& moveItem) + : m_CurrentRow(moveItem.m_CurrentRow), m_OneBeyondRow() +{ + m_Result = moveItem.m_Result; + m_ErrorBuffer = moveItem.m_ErrorBuffer; + m_Success = moveItem.m_Success; + m_RowCount = moveItem.m_RowCount; + m_RowsAffected = moveItem.m_RowsAffected; + m_LastInsertedID = moveItem.m_LastInsertedID; + + // Keeps deconstructor from double freeing + // pre move instance. + moveItem.ZeroOut(); +} + +MySQLRequestResult& MySQLRequestResult::operator=(MySQLRequestResult&& other) +{ + // Assigning something to itself? + // Silly! (but happens) + if (this == &other) + return *this; + + FreeInternals(); + + m_Success = other.m_Success; + + m_Result = other.m_Result; + m_ErrorBuffer = other.m_ErrorBuffer; + + m_RowCount = other.m_RowCount; + m_RowsAffected = other.m_RowsAffected; + m_LastInsertedID = other.m_LastInsertedID; + m_CurrentRow = other.m_CurrentRow; + m_OneBeyondRow = other.m_OneBeyondRow; + + // Keeps deconstructor from double freeing + // pre move instance. + other.ZeroOut(); + return *this; +} \ No newline at end of file diff --git a/common/MySQLRequestResult.h b/common/MySQLRequestResult.h new file mode 100644 index 000000000..807efdfc0 --- /dev/null +++ b/common/MySQLRequestResult.h @@ -0,0 +1,54 @@ +#ifndef MYSQL_REQUEST_RESULT_H +#define MYSQL_REQUEST_RESULT_H + +#ifdef _WINDOWS + #include + #include +#endif + +#include +#include <../common/types.h> +#include <../common/MySQLRequestRow.h> +#include + +class MySQLRequestResult { +private: + MYSQL_RES* m_Result; + char* m_ErrorBuffer; + MySQLRequestRow m_CurrentRow; + MySQLRequestRow m_OneBeyondRow; + + bool m_Success; + uint32 m_RowsAffected; + uint32 m_RowCount; + uint32 m_ColumnCount; + uint32 m_LastInsertedID; + uint32 m_ErrorNumber; + + +public: + + MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected = 0, uint32 rowCount = 0, uint32 columnCount = 0, uint32 lastInsertedID = 0, uint32 errorNumber = 0, char *errorBuffer = nullptr); + MySQLRequestResult(); + MySQLRequestResult(MySQLRequestResult&& moveItem); + ~MySQLRequestResult(); + + MySQLRequestResult& operator=(MySQLRequestResult&& other); + + bool Success() const { return m_Success;} + std::string ErrorMessage() const {return std::string(m_ErrorBuffer);} + uint32 ErrorNumber() const {return m_ErrorNumber;} + uint32 RowsAffected() const {return m_RowsAffected;} + uint32 RowCount() const {return m_RowCount;} + uint32 ColumnCount() const {return m_ColumnCount;} + + MySQLRequestRow& begin() { return m_CurrentRow; } + MySQLRequestRow& end() { return m_OneBeyondRow;} + +private: + void FreeInternals(); + void ZeroOut(); +}; + + +#endif \ No newline at end of file diff --git a/common/MySQLRequestRow.cpp b/common/MySQLRequestRow.cpp new file mode 100644 index 000000000..5fc4a40e8 --- /dev/null +++ b/common/MySQLRequestRow.cpp @@ -0,0 +1,54 @@ +#include "MySQLRequestRow.h" + +MySQLRequestRow::MySQLRequestRow(const MySQLRequestRow& row) + : m_Result(row.m_Result), m_MySQLRow(row.m_MySQLRow) +{ +} + +MySQLRequestRow::MySQLRequestRow() + : m_Result(nullptr), m_MySQLRow(nullptr) +{ +} + +MySQLRequestRow::MySQLRequestRow(MySQLRequestRow&& moveItem) +{ + m_Result = moveItem.m_Result; + m_MySQLRow = moveItem.m_MySQLRow; + + moveItem.m_Result = nullptr; + moveItem.m_MySQLRow = nullptr; +} + +MySQLRequestRow::MySQLRequestRow(MYSQL_RES *result) + : m_Result(result), m_MySQLRow(mysql_fetch_row(m_Result)) +{ +} + +MySQLRequestRow& MySQLRequestRow::operator++() +{ + m_MySQLRow = mysql_fetch_row(m_Result); + return *this; +} + +MySQLRequestRow MySQLRequestRow::operator++(int) +{ + MySQLRequestRow tmp(*this); + operator++(); + return tmp; +} + +bool MySQLRequestRow::operator==(const MySQLRequestRow& rhs) +{ + return m_MySQLRow == rhs.m_MySQLRow; +} + +bool MySQLRequestRow::operator!=(const MySQLRequestRow& rhs) +{ + return m_MySQLRow != rhs.m_MySQLRow; +} + +char* MySQLRequestRow::operator[](int index) +{ + + return m_MySQLRow[index]; +} \ No newline at end of file diff --git a/common/MySQLRequestRow.h b/common/MySQLRequestRow.h new file mode 100644 index 000000000..615a75371 --- /dev/null +++ b/common/MySQLRequestRow.h @@ -0,0 +1,37 @@ +#ifndef MYSQL_REQUEST_ROW_H +#define MYSQL_REQUEST_ROW_H + +#ifdef _WINDOWS + #include + #include +#endif + +#include +#include +#include <../common/types.h> + +class MySQLRequestRow : public std::iterator +{ + +private: + MYSQL_RES* m_Result; + MYSQL_ROW m_MySQLRow; + +public: + + MySQLRequestRow(); + MySQLRequestRow(MYSQL_RES *result); + MySQLRequestRow(const MySQLRequestRow& row); + MySQLRequestRow(MySQLRequestRow&& moveItem); + MySQLRequestRow& operator++(); + MySQLRequestRow operator++(int); + bool operator==(const MySQLRequestRow& rhs); + bool operator!=(const MySQLRequestRow& rhs); + + char* operator[](int index); + +}; + + + +#endif \ No newline at end of file diff --git a/common/dbcore.cpp b/common/dbcore.cpp index a2c6bc7f8..ce1b41bdc 100644 --- a/common/dbcore.cpp +++ b/common/dbcore.cpp @@ -58,6 +58,83 @@ void DBcore::ping() { MDatabase.unlock(); } +MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, bool retryOnFailureOnce) +{ + LockMutex lock(&MDatabase); + + // Reconnect if we are not connected before hand. + if (pStatus != Connected) + Open(); + + // request query. != 0 indicates some kind of error. + if (mysql_real_query(&mysql, query, querylen) != 0) + { + unsigned int errorNumber = mysql_errno(&mysql); + + if (errorNumber == CR_SERVER_GONE_ERROR) + pStatus = Error; + + // error appears to be a disconnect error, may need to try again. + if (errorNumber == CR_SERVER_LOST || errorNumber == CR_SERVER_GONE_ERROR) + { + + if (retryOnFailureOnce) + { + std::cout << "Database Error: Lost connection, attempting to recover...." << std::endl; + MySQLRequestResult requestResult = QueryDatabase(query, querylen, false); + + if (requestResult.Success()) + { + std::cout << "Reconnection to database successful." << std::endl; + return requestResult; + } + + } + + pStatus = Error; + + char *errorBuffer = new char[MYSQL_ERRMSG_SIZE]; + + snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); + + std::cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << std::endl; + + return MySQLRequestResult(nullptr, 0, 0, 0, 0, (uint32)mysql_errno(&mysql), errorBuffer); + } + + char *errorBuffer = new char[MYSQL_ERRMSG_SIZE]; + snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); + +#ifdef _EQDEBUG + std::cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << std::endl; +#endif + return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql),errorBuffer); + + } + + // successful query. get results. + MYSQL_RES* res = mysql_store_result(&mysql); + MySQLRequestResult requestResult(res, (uint32)mysql_affected_rows(&mysql), (uint32)mysql_num_rows(res), (uint32)mysql_field_count(&mysql), (uint32)mysql_insert_id(&mysql)); + + +#if DEBUG_MYSQL_QUERIES >= 1 + if (requestResult.Success()) + { + std::cout << "query successful"; + if (requestResult.Result()) + std::cout << ", " << (int) mysql_num_rows(requestResult.Result()) << " rows returned"; + + std::cout << ", " << requestResult.RowCount() << " rows affected"; + std::cout<< std::endl; + } + else { + std::cout << "QUERY: query FAILED" << std::endl; + } +#endif + + return requestResult; +} + bool DBcore::RunQuery(const char* query, uint32 querylen, char* errbuf, MYSQL_RES** result, uint32* affected_rows, uint32* last_insert_id, uint32* errnum, bool retry) { if (errnum) *errnum = 0; @@ -111,7 +188,7 @@ bool DBcore::RunQuery(const char* query, uint32 querylen, char* errbuf, MYSQL_RE if (affected_rows) *affected_rows = mysql_affected_rows(&mysql); if (last_insert_id) - *last_insert_id = mysql_insert_id(&mysql); + *last_insert_id = (uint32)mysql_insert_id(&mysql); if (result) { if (*result) { ret = true; diff --git a/common/dbcore.h b/common/dbcore.h index ed5e62133..13a395eac 100644 --- a/common/dbcore.h +++ b/common/dbcore.h @@ -4,8 +4,8 @@ #ifdef _WINDOWS #include #include - //#include #endif + #include #include "../common/types.h" #include "../common/Mutex.h" @@ -13,6 +13,7 @@ #include "../common/queue.h" #include "../common/timer.h" #include "../common/Condition.h" +#include "../common/MySQLRequestResult.h" class DBcore { public: @@ -22,6 +23,7 @@ public: ~DBcore(); eStatus GetStatus() { return pStatus; } bool RunQuery(const char* query, uint32 querylen, char* errbuf = 0, MYSQL_RES** result = 0, uint32* affected_rows = 0, uint32* last_insert_id = 0, uint32* errnum = 0, bool retry = true); + MySQLRequestResult QueryDatabase(const char* query, uint32 querylen, bool retryOnFailureOnce = true); uint32 DoEscapeString(char* tobuf, const char* frombuf, uint32 fromlen); void ping(); MYSQL* getMySQL(){ return &mysql; }