#ifndef __INC_METIN_II_ASYNCSQL_H__ #define __INC_METIN_II_ASYNCSQL_H__ #include "libthecore/stdafx.h" #include "libthecore/log.h" #include #include #include #include #include #include #include #include #include #include #include #define QUERY_MAX_LEN 8192 // Modern RAII wrapper for MySQL results struct SQLResult { SQLResult() noexcept : pSQLResult(nullptr), uiNumRows(0), uiAffectedRows(0), uiInsertID(0) { } ~SQLResult() { if (pSQLResult) { mysql_free_result(pSQLResult); pSQLResult = nullptr; } } // Delete copy constructor and assignment operator (non-copyable) SQLResult(const SQLResult&) = delete; SQLResult& operator=(const SQLResult&) = delete; // Allow move semantics SQLResult(SQLResult&& other) noexcept : pSQLResult(other.pSQLResult), uiNumRows(other.uiNumRows), uiAffectedRows(other.uiAffectedRows), uiInsertID(other.uiInsertID) { other.pSQLResult = nullptr; other.uiNumRows = 0; other.uiAffectedRows = 0; other.uiInsertID = 0; } SQLResult& operator=(SQLResult&& other) noexcept { if (this != &other) { if (pSQLResult) { mysql_free_result(pSQLResult); } pSQLResult = other.pSQLResult; uiNumRows = other.uiNumRows; uiAffectedRows = other.uiAffectedRows; uiInsertID = other.uiInsertID; other.pSQLResult = nullptr; other.uiNumRows = 0; other.uiAffectedRows = 0; other.uiInsertID = 0; } return *this; } MYSQL_RES* pSQLResult; uint32_t uiNumRows; uint32_t uiAffectedRows; uint32_t uiInsertID; }; // SQL Message with improved memory management typedef struct _SQLMsg { _SQLMsg() noexcept : m_pkSQL(nullptr), iID(0), uiResultPos(0), pvUserData(nullptr), bReturn(false), uiSQLErrno(0) { } ~_SQLMsg() { vec_pkResult.clear(); } // Delete copy operations _SQLMsg(const _SQLMsg&) = delete; _SQLMsg& operator=(const _SQLMsg&) = delete; // Allow move operations _SQLMsg(_SQLMsg&&) noexcept = default; _SQLMsg& operator=(_SQLMsg&&) noexcept = default; void Store() { do { auto pRes = std::make_unique(); pRes->pSQLResult = mysql_store_result(m_pkSQL); pRes->uiInsertID = static_cast(mysql_insert_id(m_pkSQL)); pRes->uiAffectedRows = static_cast(mysql_affected_rows(m_pkSQL)); if (pRes->pSQLResult) { pRes->uiNumRows = static_cast(mysql_num_rows(pRes->pSQLResult)); } else { pRes->uiNumRows = 0; } vec_pkResult.push_back(std::move(pRes)); } while (mysql_next_result(m_pkSQL) == 0); } SQLResult* Get() { if (uiResultPos >= vec_pkResult.size()) return nullptr; return vec_pkResult[uiResultPos].get(); } bool Next() { if (uiResultPos + 1 >= vec_pkResult.size()) return false; ++uiResultPos; return true; } MYSQL* m_pkSQL; int iID; std::string stQuery; std::vector> vec_pkResult; unsigned int uiResultPos; void* pvUserData; bool bReturn; unsigned int uiSQLErrno; } SQLMsg; class CAsyncSQL { public: CAsyncSQL(); virtual ~CAsyncSQL(); void Quit(); bool Setup(const char* c_pszHost, const char* c_pszUser, const char* c_pszPassword, const char* c_pszDB, const char* c_pszLocale, bool bNoThread = false, int iPort = 0); bool Setup(CAsyncSQL* sql, bool bNoThread = false); bool Connect(); bool IsConnected() const { return m_bConnected.load(std::memory_order_acquire); } bool QueryLocaleSet(); void AsyncQuery(const char* c_pszQuery); void ReturnQuery(const char* c_pszQuery, void* pvUserData); std::unique_ptr DirectQuery(const char* c_pszQuery); DWORD CountQuery(); DWORD CountResult(); DWORD CountCopiedQueryQueue(); bool HasWorkerThread() const; void PushResult(std::unique_ptr p); bool PopResult(std::unique_ptr& p); // Legacy API compatibility - deprecated, use PopResult(unique_ptr&) instead bool PopResult(SQLMsg** pp); void ChildLoop(); MYSQL* GetSQLHandle(); int CountQueryFinished() const; void ResetQueryFinished(); size_t EscapeString(char* dst, size_t dstSize, const char* src, size_t srcSize); protected: void Destroy(); void PushQuery(std::unique_ptr p); bool PeekQuery(SQLMsg** pp); bool PopQuery(int iID); bool PeekQueryFromCopyQueue(SQLMsg** pp); int CopyQuery(); bool PopQueryFromCopyQueue(); public: int GetCopiedQueryCount() const; void ResetCopiedQueryCount(); void AddCopiedQueryCount(int iCopiedQuery); protected: // MySQL connection MYSQL m_hDB; // Connection info std::string m_stHost; std::string m_stUser; std::string m_stPassword; std::string m_stDB; std::string m_stLocale; int m_iPort; // Thread control std::unique_ptr m_thread; std::atomic m_bEnd; std::atomic m_bConnected; // Query queues with mutex protection std::queue> m_queue_query; std::queue> m_queue_query_copy; std::queue> m_queue_result; std::mutex m_mtxQuery; std::mutex m_mtxResult; std::condition_variable m_cvQuery; // Counters std::atomic m_iMsgCount; std::atomic m_iQueryFinished; std::atomic m_iCopiedQuery; std::atomic m_ulThreadID; }; class CAsyncSQL2 : public CAsyncSQL { public: void SetLocale(const std::string& stLocale); }; #endif