From 5653d4e2de78896a4cb819a8cdf06c92110007f0 Mon Sep 17 00:00:00 2001 From: Shauren Date: Fri, 1 Nov 2019 16:21:14 +0100 Subject: [PATCH] Core/DBLayer: Support using mysql 8 --- cmake/macros/FindMySQL.cmake | 26 ++ src/server/database/CMakeLists.txt | 4 +- src/server/database/Database/DatabaseEnvFwd.h | 10 +- .../database/Database/DatabaseWorkerPool.cpp | 11 +- src/server/database/Database/Field.cpp | 7 +- src/server/database/Database/Field.h | 2 +- .../Implementation/CharacterDatabase.cpp | 2 +- .../Implementation/HotfixDatabase.cpp | 2 +- .../Database/Implementation/LoginDatabase.cpp | 2 +- .../Database/Implementation/WorldDatabase.cpp | 2 +- .../database/Database/MySQLConnection.cpp | 42 ++- .../database/Database/MySQLConnection.h | 13 +- src/server/database/Database/MySQLHacks.h | 34 +++ .../Database/MySQLPreparedStatement.cpp | 274 ++++++++++++++++++ .../Database/MySQLPreparedStatement.h | 77 +++++ .../database/Database/MySQLThreading.cpp | 10 +- src/server/database/Database/MySQLThreading.h | 5 +- .../database/Database/MySQLWorkaround.h | 16 + .../database/Database/PreparedStatement.cpp | 258 +---------------- .../database/Database/PreparedStatement.h | 48 +-- src/server/database/Database/QueryResult.cpp | 20 +- src/server/database/Database/QueryResult.h | 14 +- src/server/database/Updater/DBUpdater.cpp | 5 +- src/server/scripts/Commands/cs_server.cpp | 4 +- 24 files changed, 510 insertions(+), 378 deletions(-) create mode 100644 src/server/database/Database/MySQLHacks.h create mode 100644 src/server/database/Database/MySQLPreparedStatement.cpp create mode 100644 src/server/database/Database/MySQLPreparedStatement.h create mode 100644 src/server/database/Database/MySQLWorkaround.h diff --git a/cmake/macros/FindMySQL.cmake b/cmake/macros/FindMySQL.cmake index 02d17ab48f2..6ce7e06585e 100644 --- a/cmake/macros/FindMySQL.cmake +++ b/cmake/macros/FindMySQL.cmake @@ -86,29 +86,35 @@ find_path(MYSQL_INCLUDE_DIR /usr/local/include /usr/local/include/mysql /usr/local/mysql/include + "${PROGRAM_FILES_64}/MySQL/MySQL Server 8.0/include" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.7/include" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.6/include" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.5/include" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.1/include" "${PROGRAM_FILES_64}/MySQL/include" + "${PROGRAM_FILES_32}/MySQL/MySQL Server 8.0/include" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.7/include" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.6/include" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.5/include" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.1/include" "${PROGRAM_FILES_32}/MySQL/include" "C:/MySQL/include" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 8.0;Location]/include" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.7;Location]/include" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.6;Location]/include" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.5;Location]/include" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.1;Location]/include" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 8.0;Location]/include" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.7;Location]/include" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.6;Location]/include" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.5;Location]/include" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.1;Location]/include" + "$ENV{ProgramFiles}/MySQL/MySQL Server 8.0/include" "$ENV{ProgramFiles}/MySQL/MySQL Server 5.7/include" "$ENV{ProgramFiles}/MySQL/MySQL Server 5.6/include" "$ENV{ProgramFiles}/MySQL/MySQL Server 5.5/include" "$ENV{ProgramFiles}/MySQL/MySQL Server 5.1/include" + "$ENV{SystemDrive}/MySQL/MySQL Server 8.0/include" "$ENV{SystemDrive}/MySQL/MySQL Server 5.7/include" "$ENV{SystemDrive}/MySQL/MySQL Server 5.6/include" "$ENV{SystemDrive}/MySQL/MySQL Server 5.5/include" @@ -142,45 +148,55 @@ if( WIN32 ) libmysql PATHS ${MYSQL_ADD_LIBRARIES_PATH} + "${PROGRAM_FILES_64}/MySQL/MySQL Server 8.0/lib" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.7/lib" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.6/lib" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.5/lib" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.1/lib" + "${PROGRAM_FILES_64}/MySQL/MySQL Server 8.0/lib/opt" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.7/lib/opt" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.6/lib/opt" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.5/lib/opt" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.1/lib/opt" "${PROGRAM_FILES_64}/MySQL/lib" + "${PROGRAM_FILES_32}/MySQL/MySQL Server 8.0/lib" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.7/lib" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.6/lib" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.5/lib" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.1/lib" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.7/lib/opt" + "${PROGRAM_FILES_32}/MySQL/MySQL Server 8.0/lib/opt" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.6/lib/opt" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.5/lib/opt" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.1/lib/opt" "${PROGRAM_FILES_32}/MySQL/lib" "C:/MySQL/lib/debug" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 8.0;Location]/lib" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.7;Location]/lib" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.6;Location]/lib" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.5;Location]/lib" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.1;Location]/lib" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 8.0;Location]/lib/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.7;Location]/lib/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.6;Location]/lib/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.5;Location]/lib/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.1;Location]/lib/opt" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 8.0;Location]/lib" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.7;Location]/lib" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.6;Location]/lib" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.5;Location]/lib" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.1;Location]/lib" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 8.0;Location]/lib/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.7;Location]/lib/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.6;Location]/lib/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.5;Location]/lib/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.1;Location]/lib/opt" + "$ENV{ProgramFiles}/MySQL/MySQL Server 8.0/lib/opt" "$ENV{ProgramFiles}/MySQL/MySQL Server 5.7/lib/opt" "$ENV{ProgramFiles}/MySQL/MySQL Server 5.6/lib/opt" "$ENV{ProgramFiles}/MySQL/MySQL Server 5.5/lib/opt" "$ENV{ProgramFiles}/MySQL/MySQL Server 5.1/lib/opt" + "$ENV{SystemDrive}/MySQL/MySQL Server 8.0/lib/opt" "$ENV{SystemDrive}/MySQL/MySQL Server 5.7/lib/opt" "$ENV{SystemDrive}/MySQL/MySQL Server 5.6/lib/opt" "$ENV{SystemDrive}/MySQL/MySQL Server 5.5/lib/opt" @@ -223,46 +239,56 @@ endif( UNIX ) if( WIN32 ) find_program(MYSQL_EXECUTABLE mysql PATHS + "${PROGRAM_FILES_64}/MySQL/MySQL Server 8.0/bin" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.7/bin" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.6/bin" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.5/bin" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.1/bin" + "${PROGRAM_FILES_64}/MySQL/MySQL Server 8.0/bin/opt" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.7/bin/opt" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.6/bin/opt" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.5/bin/opt" "${PROGRAM_FILES_64}/MySQL/MySQL Server 5.1/bin/opt" "${PROGRAM_FILES_64}/MySQL/bin" + "${PROGRAM_FILES_32}/MySQL/MySQL Server 8.0/bin" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.7/bin" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.6/bin" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.5/bin" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.1/bin" + "${PROGRAM_FILES_32}/MySQL/MySQL Server 8.0/bin/opt" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.7/bin/opt" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.6/bin/opt" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.5/bin/opt" "${PROGRAM_FILES_32}/MySQL/MySQL Server 5.1/bin/opt" "${PROGRAM_FILES_32}/MySQL/bin" "C:/MySQL/bin/debug" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 8.0;Location]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.7;Location]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.6;Location]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.5;Location]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.1;Location]/bin" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 8.0;Location]/bin/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.7;Location]/bin/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.6;Location]/bin/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.5;Location]/bin/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\MySQL AB\\MySQL Server 5.1;Location]/bin/opt" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 8.0;Location]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.7;Location]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.6;Location]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.5;Location]/bin" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.1;Location]/bin" + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 8.0;Location]/bin/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.7;Location]/bin/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.6;Location]/bin/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.5;Location]/bin/opt" "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\MySQL AB\\MySQL Server 5.1;Location]/bin/opt" + "$ENV{ProgramFiles}/MySQL/MySQL Server 8.0/bin/opt" "$ENV{ProgramFiles}/MySQL/MySQL Server 5.7/bin/opt" "$ENV{ProgramFiles}/MySQL/MySQL Server 5.6/bin/opt" "$ENV{ProgramFiles}/MySQL/MySQL Server 5.5/bin/opt" "$ENV{ProgramFiles}/MySQL/MySQL Server 5.1/bin/opt" "$ENV{SystemDrive}/MySQL/MySQL Server 5.7/bin/opt" + "$ENV{SystemDrive}/MySQL/MySQL Server 8.0/bin/opt" "$ENV{SystemDrive}/MySQL/MySQL Server 5.6/bin/opt" "$ENV{SystemDrive}/MySQL/MySQL Server 5.5/bin/opt" "$ENV{SystemDrive}/MySQL/MySQL Server 5.1/bin/opt" diff --git a/src/server/database/CMakeLists.txt b/src/server/database/CMakeLists.txt index a998de00c0d..9b6e0ee634f 100644 --- a/src/server/database/CMakeLists.txt +++ b/src/server/database/CMakeLists.txt @@ -50,9 +50,9 @@ add_definitions(-DTRINITY_API_EXPORT_DATABASE) target_link_libraries(database PRIVATE trinity-core-interface + mysql PUBLIC - common - mysql) + common) set_target_properties(database PROPERTIES diff --git a/src/server/database/Database/DatabaseEnvFwd.h b/src/server/database/Database/DatabaseEnvFwd.h index bf8ea33873c..9223e6117e5 100644 --- a/src/server/database/Database/DatabaseEnvFwd.h +++ b/src/server/database/Database/DatabaseEnvFwd.h @@ -45,10 +45,10 @@ typedef std::future QueryResultHolderFuture; typedef std::promise QueryResultHolderPromise; // mysql -typedef struct st_mysql MYSQL; -typedef struct st_mysql_res MYSQL_RES; -typedef struct st_mysql_field MYSQL_FIELD; -typedef struct st_mysql_bind MYSQL_BIND; -typedef struct st_mysql_stmt MYSQL_STMT; +struct MySQLHandle; +struct MySQLResult; +struct MySQLField; +struct MySQLBind; +struct MySQLStmt; #endif // DatabaseEnvFwd_h__ diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp index 05b3038c0a8..5a75a5d0144 100644 --- a/src/server/database/Database/DatabaseWorkerPool.cpp +++ b/src/server/database/Database/DatabaseWorkerPool.cpp @@ -24,6 +24,7 @@ #include "Implementation/CharacterDatabase.h" #include "Implementation/HotfixDatabase.h" #include "Log.h" +#include "MySQLPreparedStatement.h" #include "PreparedStatement.h" #include "ProducerConsumerQueue.h" #include "QueryCallback.h" @@ -31,10 +32,7 @@ #include "QueryResult.h" #include "SQLOperation.h" #include "Transaction.h" -#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7 -#include -#endif -#include +#include "MySQLWorkaround.h" #include #define MIN_MYSQL_SERVER_VERSION 50100u @@ -356,7 +354,7 @@ uint32 DatabaseWorkerPool::OpenConnections(InternalIndex type, uint8 numConne _connections[type].clear(); return error; } - else if (mysql_get_server_version(connection->GetHandle()) < MIN_MYSQL_SERVER_VERSION) + else if (connection->GetServerVersion() < MIN_MYSQL_SERVER_VERSION) { TC_LOG_ERROR("sql.driver", "TrinityCore does not support MySQL versions below 5.1"); return 1; @@ -377,8 +375,7 @@ unsigned long DatabaseWorkerPool::EscapeString(char* to, char const* from, un if (!to || !from || !length) return 0; - return mysql_real_escape_string( - _connections[IDX_SYNCH].front()->GetHandle(), to, from, length); + return _connections[IDX_SYNCH].front()->EscapeString(to, from, length); } template diff --git a/src/server/database/Database/Field.cpp b/src/server/database/Database/Field.cpp index 66df6948ce8..2af37e6f8b0 100644 --- a/src/server/database/Database/Field.cpp +++ b/src/server/database/Database/Field.cpp @@ -304,10 +304,7 @@ bool Field::IsNumeric() const #ifdef TRINITY_DEBUG -#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7 -#include -#endif -#include +#include "MySQLHacks.h" static char const* FieldTypeToString(enum_field_types type) { @@ -343,7 +340,7 @@ static char const* FieldTypeToString(enum_field_types type) } } -void Field::SetMetadata(MYSQL_FIELD* field, uint32 fieldIndex) +void Field::SetMetadata(MySQLField* field, uint32 fieldIndex) { meta.TableName = field->org_table; meta.TableAlias = field->table; diff --git a/src/server/database/Database/Field.h b/src/server/database/Database/Field.h index 64e054749fd..b93433e17f0 100644 --- a/src/server/database/Database/Field.h +++ b/src/server/database/Database/Field.h @@ -139,7 +139,7 @@ class TC_DATABASE_API Field private: #ifdef TRINITY_DEBUG - void SetMetadata(MYSQL_FIELD* field, uint32 fieldIndex); + void SetMetadata(MySQLField* field, uint32 fieldIndex); Metadata meta; #endif }; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 59d0008d7e0..590a411006b 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -16,7 +16,7 @@ */ #include "CharacterDatabase.h" -#include "PreparedStatement.h" +#include "MySQLPreparedStatement.h" void CharacterDatabaseConnection::DoPrepareStatements() { diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index 457657da51a..607bf044c0a 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -19,7 +19,7 @@ // Autogenerated from DB2Structure.h #include "HotfixDatabase.h" -#include "PreparedStatement.h" +#include "MySQLPreparedStatement.h" // Force locale statments to appear exactly in locale declaration order, right after normal data fetch statement #define PREPARE_LOCALE_STMT(stmtBase, sql, con) \ diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 91a21d03d07..57668032953 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -16,7 +16,7 @@ */ #include "LoginDatabase.h" -#include "PreparedStatement.h" +#include "MySQLPreparedStatement.h" void LoginDatabaseConnection::DoPrepareStatements() { diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index 49892378ee8..7f8ee7b7e7a 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -16,7 +16,7 @@ */ #include "WorldDatabase.h" -#include "PreparedStatement.h" +#include "MySQLPreparedStatement.h" void WorldDatabaseConnection::DoPrepareStatements() { diff --git a/src/server/database/Database/MySQLConnection.cpp b/src/server/database/Database/MySQLConnection.cpp index 58c8ad46bae..3612f6c8f83 100644 --- a/src/server/database/Database/MySQLConnection.cpp +++ b/src/server/database/Database/MySQLConnection.cpp @@ -20,16 +20,15 @@ #include "Common.h" #include "DatabaseWorker.h" #include "Log.h" +#include "MySQLHacks.h" +#include "MySQLPreparedStatement.h" #include "PreparedStatement.h" #include "QueryResult.h" #include "Timer.h" #include "Transaction.h" #include "Util.h" #include -#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7 -#include -#endif -#include +#include "MySQLWorkaround.h" #include MySQLConnectionInfo::MySQLConnectionInfo(std::string const& infoString) @@ -129,8 +128,8 @@ uint32 MySQLConnection::Open() } #endif - m_Mysql = mysql_real_connect(mysqlInit, m_connectionInfo.host.c_str(), m_connectionInfo.user.c_str(), - m_connectionInfo.password.c_str(), m_connectionInfo.database.c_str(), port, unix_socket, 0); + m_Mysql = reinterpret_cast(mysql_real_connect(mysqlInit, m_connectionInfo.host.c_str(), m_connectionInfo.user.c_str(), + m_connectionInfo.password.c_str(), m_connectionInfo.database.c_str(), port, unix_socket, 0)); if (m_Mysql) { @@ -240,7 +239,7 @@ bool MySQLConnection::Execute(PreparedStatement* stmt) return true; } -bool MySQLConnection::_Query(PreparedStatement* stmt, MYSQL_RES **pResult, uint64* pRowCount, uint32* pFieldCount) +bool MySQLConnection::_Query(PreparedStatement* stmt, MySQLResult**pResult, uint64* pRowCount, uint32* pFieldCount) { if (!m_Mysql) return false; @@ -287,7 +286,7 @@ bool MySQLConnection::_Query(PreparedStatement* stmt, MYSQL_RES **pResult, uint6 m_mStmt->ClearParameters(); - *pResult = mysql_stmt_result_metadata(msql_STMT); + *pResult = reinterpret_cast(mysql_stmt_result_metadata(msql_STMT)); *pRowCount = mysql_stmt_num_rows(msql_STMT); *pFieldCount = mysql_stmt_field_count(msql_STMT); @@ -299,8 +298,8 @@ ResultSet* MySQLConnection::Query(char const* sql) if (!sql) return nullptr; - MYSQL_RES *result = nullptr; - MYSQL_FIELD *fields = nullptr; + MySQLResult* result = nullptr; + MySQLField* fields = nullptr; uint64 rowCount = 0; uint32 fieldCount = 0; @@ -310,7 +309,7 @@ ResultSet* MySQLConnection::Query(char const* sql) return new ResultSet(result, fields, rowCount, fieldCount); } -bool MySQLConnection::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount) +bool MySQLConnection::_Query(const char *sql, MySQLResult**pResult, MySQLField**pFields, uint64* pRowCount, uint32* pFieldCount) { if (!m_Mysql) return false; @@ -332,7 +331,7 @@ bool MySQLConnection::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD * else TC_LOG_DEBUG("sql.sql", "[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql); - *pResult = mysql_store_result(m_Mysql); + *pResult = reinterpret_cast(mysql_store_result(m_Mysql)); *pRowCount = mysql_affected_rows(m_Mysql); *pFieldCount = mysql_field_count(m_Mysql); } @@ -346,7 +345,7 @@ bool MySQLConnection::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD * return false; } - *pFields = mysql_fetch_fields(*pResult); + *pFields = reinterpret_cast(mysql_fetch_fields(*pResult)); return true; } @@ -417,6 +416,11 @@ int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction) return 0; } +size_t MySQLConnection::EscapeString(char* to, const char* from, size_t length) +{ + return mysql_real_escape_string(m_Mysql, to, from, length); +} + void MySQLConnection::Ping() { mysql_ping(m_Mysql); @@ -437,6 +441,12 @@ void MySQLConnection::Unlock() m_Mutex.unlock(); } +uint32 MySQLConnection::GetServerVersion() const +{ + return mysql_get_server_version(m_Mysql); +} + + MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index) { ASSERT(index < m_stmts.size()); @@ -476,13 +486,13 @@ void MySQLConnection::PrepareStatement(uint32 index, std::string const& sql, Con m_prepareError = true; } else - m_stmts[index] = Trinity::make_unique(stmt, sql); + m_stmts[index] = Trinity::make_unique(reinterpret_cast(stmt), sql); } } PreparedResultSet* MySQLConnection::Query(PreparedStatement* stmt) { - MYSQL_RES *result = nullptr; + MySQLResult* result = nullptr; uint64 rowCount = 0; uint32 fieldCount = 0; @@ -509,7 +519,7 @@ bool MySQLConnection::_HandleMySQLErrno(uint32 errNo, uint8 attempts /*= 5*/) { TC_LOG_ERROR("sql.sql", "Lost the connection to the MySQL server!"); - mysql_close(GetHandle()); + mysql_close(m_Mysql); m_Mysql = nullptr; } } diff --git a/src/server/database/Database/MySQLConnection.h b/src/server/database/Database/MySQLConnection.h index 66601beb788..85ead19934e 100644 --- a/src/server/database/Database/MySQLConnection.h +++ b/src/server/database/Database/MySQLConnection.h @@ -66,19 +66,18 @@ class TC_DATABASE_API MySQLConnection bool PrepareStatements(); - public: bool Execute(char const* sql); bool Execute(PreparedStatement* stmt); ResultSet* Query(char const* sql); PreparedResultSet* Query(PreparedStatement* stmt); - bool _Query(char const* sql, MYSQL_RES** pResult, MYSQL_FIELD** pFields, uint64* pRowCount, uint32* pFieldCount); - bool _Query(PreparedStatement* stmt, MYSQL_RES** pResult, uint64* pRowCount, uint32* pFieldCount); + bool _Query(char const* sql, MySQLResult** pResult, MySQLField** pFields, uint64* pRowCount, uint32* pFieldCount); + bool _Query(PreparedStatement* stmt, MySQLResult** pResult, uint64* pRowCount, uint32* pFieldCount); void BeginTransaction(); void RollbackTransaction(); void CommitTransaction(); int ExecuteTransaction(SQLTransaction& transaction); - + size_t EscapeString(char* to, const char* from, size_t length); void Ping(); uint32 GetLastError(); @@ -91,13 +90,12 @@ class TC_DATABASE_API MySQLConnection /// Called by parent databasepool. Will let other threads access this connection void Unlock(); - MYSQL* GetHandle() { return m_Mysql; } + uint32 GetServerVersion() const; MySQLPreparedStatement* GetPreparedStatement(uint32 index); void PrepareStatement(uint32 index, std::string const& sql, ConnectionFlags flags); virtual void DoPrepareStatements() = 0; - protected: typedef std::vector> PreparedStatementContainer; PreparedStatementContainer m_stmts; //! PreparedStatements storage @@ -107,10 +105,9 @@ class TC_DATABASE_API MySQLConnection private: bool _HandleMySQLErrno(uint32 errNo, uint8 attempts = 5); - private: ProducerConsumerQueue* m_queue; //! Queue shared with other asynchronous connections. std::unique_ptr m_worker; //! Core worker task. - MYSQL* m_Mysql; //! MySQL Handle. + MySQLHandle* m_Mysql; //! MySQL Handle. MySQLConnectionInfo& m_connectionInfo; //! Connection info (used for logging) ConnectionFlags m_connectionFlags; //! Connection flags (for preparing relevant statements) std::mutex m_Mutex; diff --git a/src/server/database/Database/MySQLHacks.h b/src/server/database/Database/MySQLHacks.h new file mode 100644 index 00000000000..6f2767fb1d1 --- /dev/null +++ b/src/server/database/Database/MySQLHacks.h @@ -0,0 +1,34 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef MySQLHacks_h__ +#define MySQLHacks_h__ + +#include "MySQLWorkaround.h" +#include + +struct MySQLHandle : MYSQL { }; +struct MySQLResult : MYSQL_RES { }; +struct MySQLField : MYSQL_FIELD { }; +struct MySQLBind : MYSQL_BIND { }; +struct MySQLStmt : MYSQL_STMT { }; + +// mysql 8 removed my_bool typedef (it was char) and started using bools directly +// to maintain compatibility we use this trick to retrieve which type is being used +using MySQLBool = std::remove_pointer_t().is_null)>; + +#endif // MySQLHacks_h__ diff --git a/src/server/database/Database/MySQLPreparedStatement.cpp b/src/server/database/Database/MySQLPreparedStatement.cpp new file mode 100644 index 00000000000..2e848804902 --- /dev/null +++ b/src/server/database/Database/MySQLPreparedStatement.cpp @@ -0,0 +1,274 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "MySQLPreparedStatement.h" +#include "Errors.h" +#include "Log.h" +#include "MySQLHacks.h" +#include "PreparedStatement.h" +#include + +MySQLPreparedStatement::MySQLPreparedStatement(MySQLStmt* stmt, std::string queryString) : + m_stmt(nullptr), m_Mstmt(stmt), m_bind(nullptr), m_queryString(std::move(queryString)) +{ + /// Initialize variable parameters + m_paramCount = mysql_stmt_param_count(stmt); + m_paramsSet.assign(m_paramCount, false); + m_bind = new MySQLBind[m_paramCount]; + memset(m_bind, 0, sizeof(MySQLBind) * m_paramCount); + + /// "If set to 1, causes mysql_stmt_store_result() to update the metadata MYSQL_FIELD->max_length value." + MySQLBool bool_tmp = MySQLBool(1); + mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &bool_tmp); +} + +MySQLPreparedStatement::~MySQLPreparedStatement() +{ + ClearParameters(); + if (m_Mstmt->bind_result_done) + { + delete[] m_Mstmt->bind->length; + delete[] m_Mstmt->bind->is_null; + } + mysql_stmt_close(m_Mstmt); + delete[] m_bind; +} + +void MySQLPreparedStatement::ClearParameters() +{ + for (uint32 i=0; i < m_paramCount; ++i) + { + delete m_bind[i].length; + m_bind[i].length = nullptr; + delete[] (char*) m_bind[i].buffer; + m_bind[i].buffer = nullptr; + m_paramsSet[i] = false; + } +} + +static bool ParamenterIndexAssertFail(uint32 stmtIndex, uint8 index, uint32 paramCount) +{ + TC_LOG_ERROR("sql.driver", "Attempted to bind parameter %u%s on a PreparedStatement %u (statement has only %u parameters)", uint32(index) + 1, (index == 1 ? "st" : (index == 2 ? "nd" : (index == 3 ? "rd" : "nd"))), stmtIndex, paramCount); + return false; +} + +static void SetParameterValue(MYSQL_BIND* param, enum_field_types type, const void* value, uint32 len, bool isUnsigned) +{ + param->buffer_type = type; + delete[] static_cast(param->buffer); + param->buffer = new char[len]; + param->buffer_length = 0; + param->is_null_value = 0; + param->length = nullptr; // Only != NULL for strings + param->is_unsigned = isUnsigned; + + memcpy(param->buffer, value, len); +} + +//- Bind on mysql level +void MySQLPreparedStatement::AssertValidIndex(uint8 index) +{ + ASSERT(index < m_paramCount || ParamenterIndexAssertFail(m_stmt->m_index, index, m_paramCount)); + + if (m_paramsSet[index]) + TC_LOG_ERROR("sql.sql", "[ERROR] Prepared Statement (id: %u) trying to bind value on already bound index (%u).", m_stmt->m_index, index); +} + +void MySQLPreparedStatement::setNull(const uint8 index) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + param->buffer_type = MYSQL_TYPE_NULL; + delete[] static_cast(param->buffer); + param->buffer = nullptr; + param->buffer_length = 0; + param->is_null_value = 1; + delete param->length; + param->length = nullptr; +} + +void MySQLPreparedStatement::setBool(const uint8 index, const bool value) +{ + setUInt8(index, value ? 1 : 0); +} + +void MySQLPreparedStatement::setUInt8(const uint8 index, const uint8 value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + SetParameterValue(param, MYSQL_TYPE_TINY, &value, sizeof(uint8), true); +} + +void MySQLPreparedStatement::setUInt16(const uint8 index, const uint16 value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + SetParameterValue(param, MYSQL_TYPE_SHORT, &value, sizeof(uint16), true); +} + +void MySQLPreparedStatement::setUInt32(const uint8 index, const uint32 value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + SetParameterValue(param, MYSQL_TYPE_LONG, &value, sizeof(uint32), true); +} + +void MySQLPreparedStatement::setUInt64(const uint8 index, const uint64 value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + SetParameterValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(uint64), true); +} + +void MySQLPreparedStatement::setInt8(const uint8 index, const int8 value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + SetParameterValue(param, MYSQL_TYPE_TINY, &value, sizeof(int8), false); +} + +void MySQLPreparedStatement::setInt16(const uint8 index, const int16 value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + SetParameterValue(param, MYSQL_TYPE_SHORT, &value, sizeof(int16), false); +} + +void MySQLPreparedStatement::setInt32(const uint8 index, const int32 value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + SetParameterValue(param, MYSQL_TYPE_LONG, &value, sizeof(int32), false); +} + +void MySQLPreparedStatement::setInt64(const uint8 index, const int64 value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + SetParameterValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(int64), false); +} + +void MySQLPreparedStatement::setFloat(const uint8 index, const float value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + SetParameterValue(param, MYSQL_TYPE_FLOAT, &value, sizeof(float), (value > 0.0f)); +} + +void MySQLPreparedStatement::setDouble(const uint8 index, const double value) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + SetParameterValue(param, MYSQL_TYPE_DOUBLE, &value, sizeof(double), (value > 0.0f)); +} + +void MySQLPreparedStatement::setBinary(const uint8 index, const std::vector& value, bool isString) +{ + AssertValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + uint32 len = uint32(value.size()); + param->buffer_type = MYSQL_TYPE_BLOB; + delete [] static_cast(param->buffer); + param->buffer = new char[len]; + param->buffer_length = len; + param->is_null_value = 0; + delete param->length; + param->length = new unsigned long(len); + if (isString) + { + *param->length -= 1; + param->buffer_type = MYSQL_TYPE_VAR_STRING; + } + + memcpy(param->buffer, value.data(), len); +} + +std::string MySQLPreparedStatement::getQueryString() const +{ + std::string queryString(m_queryString); + + size_t pos = 0; + for (uint32 i = 0; i < m_stmt->statement_data.size(); i++) + { + pos = queryString.find('?', pos); + std::stringstream ss; + + switch (m_stmt->statement_data[i].type) + { + case TYPE_BOOL: + ss << uint16(m_stmt->statement_data[i].data.boolean); + break; + case TYPE_UI8: + ss << uint16(m_stmt->statement_data[i].data.ui8); // stringstream will append a character with that code instead of numeric representation + break; + case TYPE_UI16: + ss << m_stmt->statement_data[i].data.ui16; + break; + case TYPE_UI32: + ss << m_stmt->statement_data[i].data.ui32; + break; + case TYPE_I8: + ss << int16(m_stmt->statement_data[i].data.i8); // stringstream will append a character with that code instead of numeric representation + break; + case TYPE_I16: + ss << m_stmt->statement_data[i].data.i16; + break; + case TYPE_I32: + ss << m_stmt->statement_data[i].data.i32; + break; + case TYPE_UI64: + ss << m_stmt->statement_data[i].data.ui64; + break; + case TYPE_I64: + ss << m_stmt->statement_data[i].data.i64; + break; + case TYPE_FLOAT: + ss << m_stmt->statement_data[i].data.f; + break; + case TYPE_DOUBLE: + ss << m_stmt->statement_data[i].data.d; + break; + case TYPE_STRING: + ss << '\'' << (char const*)m_stmt->statement_data[i].binary.data() << '\''; + break; + case TYPE_BINARY: + ss << "BINARY"; + break; + case TYPE_NULL: + ss << "NULL"; + break; + } + + std::string replaceStr = ss.str(); + queryString.replace(pos, 1, replaceStr); + pos += replaceStr.length(); + } + + return queryString; +} diff --git a/src/server/database/Database/MySQLPreparedStatement.h b/src/server/database/Database/MySQLPreparedStatement.h new file mode 100644 index 00000000000..a8b69feca71 --- /dev/null +++ b/src/server/database/Database/MySQLPreparedStatement.h @@ -0,0 +1,77 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef MySQLPreparedStatement_h__ +#define MySQLPreparedStatement_h__ + +#include "DatabaseEnvFwd.h" +#include "Define.h" +#include "MySQLWorkaround.h" +#include +#include + +class MySQLConnection; +class PreparedStatement; + +//- Class of which the instances are unique per MySQLConnection +//- access to these class objects is only done when a prepared statement task +//- is executed. +class TC_DATABASE_API MySQLPreparedStatement +{ + friend class MySQLConnection; + friend class PreparedStatementBase; + + public: + MySQLPreparedStatement(MySQLStmt* stmt, std::string queryString); + ~MySQLPreparedStatement(); + + void setNull(const uint8 index); + void setBool(const uint8 index, const bool value); + void setUInt8(const uint8 index, const uint8 value); + void setUInt16(const uint8 index, const uint16 value); + void setUInt32(const uint8 index, const uint32 value); + void setUInt64(const uint8 index, const uint64 value); + void setInt8(const uint8 index, const int8 value); + void setInt16(const uint8 index, const int16 value); + void setInt32(const uint8 index, const int32 value); + void setInt64(const uint8 index, const int64 value); + void setFloat(const uint8 index, const float value); + void setDouble(const uint8 index, const double value); + void setBinary(const uint8 index, const std::vector& value, bool isString); + + uint32 GetParameterCount() const { return m_paramCount; } + + protected: + MySQLStmt* GetSTMT() { return m_Mstmt; } + MySQLBind* GetBind() { return m_bind; } + PreparedStatement* m_stmt; + void ClearParameters(); + void AssertValidIndex(uint8 index); + std::string getQueryString() const; + + private: + MySQLStmt* m_Mstmt; + uint32 m_paramCount; + std::vector m_paramsSet; + MySQLBind* m_bind; + std::string const m_queryString; + + MySQLPreparedStatement(MySQLPreparedStatement const& right) = delete; + MySQLPreparedStatement& operator=(MySQLPreparedStatement const& right) = delete; +}; + +#endif // MySQLPreparedStatement_h__ diff --git a/src/server/database/Database/MySQLThreading.cpp b/src/server/database/Database/MySQLThreading.cpp index 95418fba795..d373393f284 100644 --- a/src/server/database/Database/MySQLThreading.cpp +++ b/src/server/database/Database/MySQLThreading.cpp @@ -16,10 +16,7 @@ */ #include "MySQLThreading.h" -#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7 -#include -#endif -#include +#include "MySQLWorkaround.h" void MySQL::Library_Init() { @@ -30,3 +27,8 @@ void MySQL::Library_End() { mysql_library_end(); } + +char const* MySQL::GetLibraryVersion() +{ + return MYSQL_SERVER_VERSION; +} diff --git a/src/server/database/Database/MySQLThreading.h b/src/server/database/Database/MySQLThreading.h index 052955561ac..57710ead1ea 100644 --- a/src/server/database/Database/MySQLThreading.h +++ b/src/server/database/Database/MySQLThreading.h @@ -22,8 +22,9 @@ namespace MySQL { - void TC_DATABASE_API Library_Init(); - void TC_DATABASE_API Library_End(); + TC_DATABASE_API void Library_Init(); + TC_DATABASE_API void Library_End(); + TC_DATABASE_API char const* GetLibraryVersion(); } #endif diff --git a/src/server/database/Database/MySQLWorkaround.h b/src/server/database/Database/MySQLWorkaround.h new file mode 100644 index 00000000000..4f0bbf50525 --- /dev/null +++ b/src/server/database/Database/MySQLWorkaround.h @@ -0,0 +1,16 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ diff --git a/src/server/database/Database/PreparedStatement.cpp b/src/server/database/Database/PreparedStatement.cpp index 7d2707420a9..d400adb87bb 100644 --- a/src/server/database/Database/PreparedStatement.cpp +++ b/src/server/database/Database/PreparedStatement.cpp @@ -18,13 +18,10 @@ #include "PreparedStatement.h" #include "Errors.h" #include "MySQLConnection.h" +#include "MySQLPreparedStatement.h" #include "QueryResult.h" #include "Log.h" -#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7 -#include -#endif -#include -#include +#include "MySQLWorkaround.h" PreparedStatement::PreparedStatement(uint32 index, uint8 capacity) : m_stmt(nullptr), m_index(index), statement_data(capacity) { } @@ -190,257 +187,6 @@ void PreparedStatement::setNull(const uint8 index) statement_data[index].type = TYPE_NULL; } -MySQLPreparedStatement::MySQLPreparedStatement(MYSQL_STMT* stmt, std::string queryString) : -m_stmt(nullptr), m_Mstmt(stmt), m_bind(nullptr), m_queryString(std::move(queryString)) -{ - /// Initialize variable parameters - m_paramCount = mysql_stmt_param_count(stmt); - m_paramsSet.assign(m_paramCount, false); - m_bind = new MYSQL_BIND[m_paramCount]; - memset(m_bind, 0, sizeof(MYSQL_BIND)*m_paramCount); - - /// "If set to 1, causes mysql_stmt_store_result() to update the metadata MYSQL_FIELD->max_length value." - my_bool bool_tmp = 1; - mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &bool_tmp); -} - -MySQLPreparedStatement::~MySQLPreparedStatement() -{ - ClearParameters(); - if (m_Mstmt->bind_result_done) - { - delete[] m_Mstmt->bind->length; - delete[] m_Mstmt->bind->is_null; - } - mysql_stmt_close(m_Mstmt); - delete[] m_bind; -} - -void MySQLPreparedStatement::ClearParameters() -{ - for (uint32 i=0; i < m_paramCount; ++i) - { - delete m_bind[i].length; - m_bind[i].length = nullptr; - delete[] (char*) m_bind[i].buffer; - m_bind[i].buffer = nullptr; - m_paramsSet[i] = false; - } -} - -static bool ParamenterIndexAssertFail(uint32 stmtIndex, uint8 index, uint32 paramCount) -{ - TC_LOG_ERROR("sql.driver", "Attempted to bind parameter %u%s on a PreparedStatement %u (statement has only %u parameters)", uint32(index) + 1, (index == 1 ? "st" : (index == 2 ? "nd" : (index == 3 ? "rd" : "nd"))), stmtIndex, paramCount); - return false; -} - -static void SetParameterValue(MYSQL_BIND* param, enum_field_types type, void const* value, uint32 len, bool isUnsigned) -{ - param->buffer_type = type; - delete[] static_cast(param->buffer); - param->buffer = new char[len]; - param->buffer_length = 0; - param->is_null_value = 0; - param->length = nullptr; // Only != NULL for strings - param->is_unsigned = isUnsigned; - - memcpy(param->buffer, value, len); -} - -//- Bind on mysql level -void MySQLPreparedStatement::AssertValidIndex(uint8 index) -{ - ASSERT(index < m_paramCount || ParamenterIndexAssertFail(m_stmt->m_index, index, m_paramCount)); - - if (m_paramsSet[index]) - TC_LOG_ERROR("sql.sql", "[ERROR] Prepared Statement (id: %u) trying to bind value on already bound index (%u).", m_stmt->m_index, index); -} - -void MySQLPreparedStatement::setNull(uint8 index) -{ - AssertValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - param->buffer_type = MYSQL_TYPE_NULL; - delete[] static_cast(param->buffer); - param->buffer = nullptr; - param->buffer_length = 0; - param->is_null_value = 1; - delete param->length; - param->length = nullptr; -} - -void MySQLPreparedStatement::setBool(uint8 index, bool value) -{ - setUInt8(index, value ? 1 : 0); -} - -void MySQLPreparedStatement::setUInt8(uint8 index, uint8 value) -{ - AssertValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - SetParameterValue(param, MYSQL_TYPE_TINY, &value, sizeof(uint8), true); -} - -void MySQLPreparedStatement::setUInt16(uint8 index, uint16 value) -{ - AssertValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - SetParameterValue(param, MYSQL_TYPE_SHORT, &value, sizeof(uint16), true); -} - -void MySQLPreparedStatement::setUInt32(uint8 index, uint32 value) -{ - AssertValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - SetParameterValue(param, MYSQL_TYPE_LONG, &value, sizeof(uint32), true); -} - -void MySQLPreparedStatement::setUInt64(uint8 index, uint64 value) -{ - AssertValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - SetParameterValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(uint64), true); -} - -void MySQLPreparedStatement::setInt8(uint8 index, int8 value) -{ - AssertValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - SetParameterValue(param, MYSQL_TYPE_TINY, &value, sizeof(int8), false); -} - -void MySQLPreparedStatement::setInt16(uint8 index, int16 value) -{ - AssertValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - SetParameterValue(param, MYSQL_TYPE_SHORT, &value, sizeof(int16), false); -} - -void MySQLPreparedStatement::setInt32(uint8 index, int32 value) -{ - AssertValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - SetParameterValue(param, MYSQL_TYPE_LONG, &value, sizeof(int32), false); -} - -void MySQLPreparedStatement::setInt64(uint8 index, int64 value) -{ - AssertValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - SetParameterValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(int64), false); -} - -void MySQLPreparedStatement::setFloat(uint8 index, float value) -{ - AssertValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - SetParameterValue(param, MYSQL_TYPE_FLOAT, &value, sizeof(float), (value > 0.0f)); -} - -void MySQLPreparedStatement::setDouble(uint8 index, double value) -{ - AssertValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - SetParameterValue(param, MYSQL_TYPE_DOUBLE, &value, sizeof(double), (value > 0.0f)); -} - -void MySQLPreparedStatement::setBinary(uint8 index, std::vector const& value, bool isString) -{ - AssertValidIndex(index); - m_paramsSet[index] = true; - MYSQL_BIND* param = &m_bind[index]; - uint32 len = uint32(value.size()); - param->buffer_type = MYSQL_TYPE_BLOB; - delete [] static_cast(param->buffer); - param->buffer = new char[len]; - param->buffer_length = len; - param->is_null_value = 0; - delete param->length; - param->length = new unsigned long(len); - if (isString) - { - *param->length -= 1; - param->buffer_type = MYSQL_TYPE_VAR_STRING; - } - - memcpy(param->buffer, value.data(), len); -} - -std::string MySQLPreparedStatement::getQueryString() const -{ - std::string queryString(m_queryString); - - size_t pos = 0; - for (uint32 i = 0; i < m_stmt->statement_data.size(); i++) - { - pos = queryString.find('?', pos); - std::stringstream ss; - - switch (m_stmt->statement_data[i].type) - { - case TYPE_BOOL: - ss << uint16(m_stmt->statement_data[i].data.boolean); - break; - case TYPE_UI8: - ss << uint16(m_stmt->statement_data[i].data.ui8); // stringstream will append a character with that code instead of numeric representation - break; - case TYPE_UI16: - ss << m_stmt->statement_data[i].data.ui16; - break; - case TYPE_UI32: - ss << m_stmt->statement_data[i].data.ui32; - break; - case TYPE_I8: - ss << int16(m_stmt->statement_data[i].data.i8); // stringstream will append a character with that code instead of numeric representation - break; - case TYPE_I16: - ss << m_stmt->statement_data[i].data.i16; - break; - case TYPE_I32: - ss << m_stmt->statement_data[i].data.i32; - break; - case TYPE_UI64: - ss << m_stmt->statement_data[i].data.ui64; - break; - case TYPE_I64: - ss << m_stmt->statement_data[i].data.i64; - break; - case TYPE_FLOAT: - ss << m_stmt->statement_data[i].data.f; - break; - case TYPE_DOUBLE: - ss << m_stmt->statement_data[i].data.d; - break; - case TYPE_STRING: - ss << '\'' << (char const*)m_stmt->statement_data[i].binary.data() << '\''; - break; - case TYPE_BINARY: - ss << "BINARY"; - break; - case TYPE_NULL: - ss << "nullptr"; - break; - } - - std::string replaceStr = ss.str(); - queryString.replace(pos, 1, replaceStr); - pos += replaceStr.length(); - } - - return queryString; -} - //- Execution PreparedStatementTask::PreparedStatementTask(PreparedStatement* stmt, bool async) : m_stmt(stmt), m_result(nullptr) diff --git a/src/server/database/Database/PreparedStatement.h b/src/server/database/Database/PreparedStatement.h index 9486c44edc5..57fc0feb97f 100644 --- a/src/server/database/Database/PreparedStatement.h +++ b/src/server/database/Database/PreparedStatement.h @@ -98,6 +98,7 @@ class TC_DATABASE_API PreparedStatement void setString(uint8 index, std::string const& value); void setBinary(uint8 index, std::vector const& value); + uint32 GetIndex() const { return m_index; } protected: void BindParameters(MySQLPreparedStatement* stmt); @@ -112,53 +113,6 @@ class TC_DATABASE_API PreparedStatement PreparedStatement& operator=(PreparedStatement const& right) = delete; }; -//- Class of which the instances are unique per MySQLConnection -//- access to these class objects is only done when a prepared statement task -//- is executed. -class TC_DATABASE_API MySQLPreparedStatement -{ - friend class MySQLConnection; - friend class PreparedStatement; - - public: - MySQLPreparedStatement(MYSQL_STMT* stmt, std::string queryString); - ~MySQLPreparedStatement(); - - void setNull(uint8 index); - void setBool(uint8 index, bool value); - void setUInt8(uint8 index, uint8 value); - void setUInt16(uint8 index, uint16 value); - void setUInt32(uint8 index, uint32 value); - void setUInt64(uint8 index, uint64 value); - void setInt8(uint8 index, int8 value); - void setInt16(uint8 index, int16 value); - void setInt32(uint8 index, int32 value); - void setInt64(uint8 index, int64 value); - void setFloat(uint8 index, float value); - void setDouble(uint8 index, double value); - void setBinary(uint8 index, std::vector const& value, bool isString); - - uint32 GetParameterCount() const { return m_paramCount; } - - protected: - MYSQL_STMT* GetSTMT() { return m_Mstmt; } - MYSQL_BIND* GetBind() { return m_bind; } - PreparedStatement* m_stmt; - void ClearParameters(); - void AssertValidIndex(uint8 index); - std::string getQueryString() const; - - private: - MYSQL_STMT* m_Mstmt; - uint32 m_paramCount; - std::vector m_paramsSet; - MYSQL_BIND* m_bind; - std::string const m_queryString; - - MySQLPreparedStatement(MySQLPreparedStatement const& right) = delete; - MySQLPreparedStatement& operator=(MySQLPreparedStatement const& right) = delete; -}; - //- Lower-level class, enqueuable operation class TC_DATABASE_API PreparedStatementTask : public SQLOperation { diff --git a/src/server/database/Database/QueryResult.cpp b/src/server/database/Database/QueryResult.cpp index 741444fdb51..29cff4ae571 100644 --- a/src/server/database/Database/QueryResult.cpp +++ b/src/server/database/Database/QueryResult.cpp @@ -19,10 +19,8 @@ #include "Errors.h" #include "Field.h" #include "Log.h" -#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7 -#include -#endif -#include +#include "MySQLHacks.h" +#include "MySQLWorkaround.h" static uint32 SizeForType(MYSQL_FIELD* field) { @@ -118,7 +116,7 @@ DatabaseFieldTypes MysqlTypeToFieldType(enum_field_types type) return DatabaseFieldTypes::Null; } -ResultSet::ResultSet(MYSQL_RES *result, MYSQL_FIELD *fields, uint64 rowCount, uint32 fieldCount) : +ResultSet::ResultSet(MySQLResult*result, MySQLField*fields, uint64 rowCount, uint32 fieldCount) : _rowCount(rowCount), _fieldCount(fieldCount), _result(result), @@ -131,7 +129,7 @@ _fields(fields) #endif } -PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES *result, uint64 rowCount, uint32 fieldCount) : +PreparedResultSet::PreparedResultSet(MySQLStmt* stmt, MySQLResult*result, uint64 rowCount, uint32 fieldCount) : m_rowCount(rowCount), m_rowPosition(0), m_fieldCount(fieldCount), @@ -148,16 +146,16 @@ m_metadataResult(result) delete[] m_stmt->bind->is_null; } - m_rBind = new MYSQL_BIND[m_fieldCount]; + m_rBind = new MySQLBind[m_fieldCount]; //- for future readers wondering where the fuck this is freed - mysql_stmt_bind_result moves pointers to these // from m_rBind to m_stmt->bind and it is later freed by the `if (m_stmt->bind_result_done)` block just above here // MYSQL_STMT lifetime is equal to connection lifetime - my_bool* m_isNull = new my_bool[m_fieldCount]; + MySQLBool* m_isNull = new MySQLBool[m_fieldCount]; unsigned long* m_length = new unsigned long[m_fieldCount]; - memset(m_isNull, 0, sizeof(my_bool) * m_fieldCount); - memset(m_rBind, 0, sizeof(MYSQL_BIND) * m_fieldCount); + memset(m_isNull, 0, sizeof(MySQLBool) * m_fieldCount); + memset(m_rBind, 0, sizeof(MySQLBind) * m_fieldCount); memset(m_length, 0, sizeof(unsigned long) * m_fieldCount); //- This is where we store the (entire) resultset @@ -173,7 +171,7 @@ m_metadataResult(result) m_rowCount = mysql_stmt_num_rows(m_stmt); //- This is where we prepare the buffer based on metadata - MYSQL_FIELD* field = mysql_fetch_fields(m_metadataResult); + MySQLField* field = reinterpret_cast(mysql_fetch_fields(m_metadataResult)); std::size_t rowSize = 0; for (uint32 i = 0; i < m_fieldCount; ++i) { diff --git a/src/server/database/Database/QueryResult.h b/src/server/database/Database/QueryResult.h index 258d95666c2..cb2780e84e4 100644 --- a/src/server/database/Database/QueryResult.h +++ b/src/server/database/Database/QueryResult.h @@ -25,7 +25,7 @@ class TC_DATABASE_API ResultSet { public: - ResultSet(MYSQL_RES* result, MYSQL_FIELD* fields, uint64 rowCount, uint32 fieldCount); + ResultSet(MySQLResult* result, MySQLField* fields, uint64 rowCount, uint32 fieldCount); ~ResultSet(); bool NextRow(); @@ -42,8 +42,8 @@ class TC_DATABASE_API ResultSet private: void CleanUp(); - MYSQL_RES* _result; - MYSQL_FIELD* _fields; + MySQLResult* _result; + MySQLField* _fields; ResultSet(ResultSet const& right) = delete; ResultSet& operator=(ResultSet const& right) = delete; @@ -52,7 +52,7 @@ class TC_DATABASE_API ResultSet class TC_DATABASE_API PreparedResultSet { public: - PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64 rowCount, uint32 fieldCount); + PreparedResultSet(MySQLStmt* stmt, MySQLResult* result, uint64 rowCount, uint32 fieldCount); ~PreparedResultSet(); bool NextRow(); @@ -69,9 +69,9 @@ class TC_DATABASE_API PreparedResultSet uint32 m_fieldCount; private: - MYSQL_BIND* m_rBind; - MYSQL_STMT* m_stmt; - MYSQL_RES* m_metadataResult; ///< Field metadata, returned by mysql_stmt_result_metadata + MySQLBind* m_rBind; + MySQLStmt* m_stmt; + MySQLResult* m_metadataResult; ///< Field metadata, returned by mysql_stmt_result_metadata void CleanUp(); bool _NextRow(); diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp index e7d01d8a107..1fcfff23741 100644 --- a/src/server/database/Updater/DBUpdater.cpp +++ b/src/server/database/Updater/DBUpdater.cpp @@ -379,7 +379,10 @@ void DBUpdater::ApplyFile(DatabaseWorkerPool& pool, std::string const& hos // Check if we want to connect through ip or socket (Unix only) #ifdef _WIN32 - args.push_back("-P" + port_or_socket); + if (host == ".") + args.push_back("--protocol=PIPE"); + else + args.push_back("-P" + port_or_socket); #else diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp index cf89e31e9b0..509999edfc7 100644 --- a/src/server/scripts/Commands/cs_server.cpp +++ b/src/server/scripts/Commands/cs_server.cpp @@ -31,6 +31,7 @@ EndScriptData */ #include "GitRevision.h" #include "Language.h" #include "Log.h" +#include "MySQLThreading.h" #include "ObjectAccessor.h" #include "Player.h" #include "RBAC.h" @@ -44,7 +45,6 @@ EndScriptData */ #include #include -#include #include #include @@ -137,7 +137,7 @@ public: handler->PSendSysMessage("%s", GitRevision::GetFullVersion()); handler->PSendSysMessage("Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); handler->PSendSysMessage("Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); - handler->PSendSysMessage("Using MySQL version: %s", MYSQL_SERVER_VERSION); + handler->PSendSysMessage("Using MySQL version: %s", MySQL::GetLibraryVersion()); handler->PSendSysMessage("Using CMake version: %s", GitRevision::GetCMakeVersion()); handler->PSendSysMessage("Compiled on: %s", GitRevision::GetHostOSVersion());