mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
Core/DBLayer: Support using mysql 8
This commit is contained in:
@@ -86,19 +86,25 @@ 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/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/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\\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"
|
||||
"$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{SystemDrive}/MySQL/MySQL Server 8.0/include"
|
||||
"$ENV{SystemDrive}/MySQL/MySQL Server 5.7/include"
|
||||
"$ENV{SystemDrive}/MySQL/MySQL Server 5.6/include"
|
||||
"c:/msys/local/include"
|
||||
@@ -130,27 +136,37 @@ 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 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/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 8.0/lib/opt"
|
||||
"${PROGRAM_FILES_32}/MySQL/MySQL Server 5.7/lib/opt"
|
||||
"${PROGRAM_FILES_32}/MySQL/MySQL Server 5.6/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 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\\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 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"
|
||||
"$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{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"
|
||||
"c:/msys/local/include"
|
||||
@@ -191,27 +207,37 @@ 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 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/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 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/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 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\\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 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"
|
||||
"$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{SystemDrive}/MySQL/MySQL Server 8.0/bin/opt"
|
||||
"$ENV{SystemDrive}/MySQL/MySQL Server 5.7/bin/opt"
|
||||
"$ENV{SystemDrive}/MySQL/MySQL Server 5.6/bin/opt"
|
||||
"c:/msys/local/include"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -76,10 +76,10 @@ using LoginDatabaseQueryHolder = SQLQueryHolder<LoginDatabaseConnection>;
|
||||
using WorldDatabaseQueryHolder = SQLQueryHolder<WorldDatabaseConnection>;
|
||||
|
||||
// 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__
|
||||
|
||||
@@ -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 <winsock2.h>
|
||||
#endif
|
||||
#include <mysql.h>
|
||||
#include "MySQLWorkaround.h"
|
||||
#include <mysqld_error.h>
|
||||
|
||||
#define MIN_MYSQL_SERVER_VERSION 50100u
|
||||
@@ -355,7 +353,7 @@ uint32 DatabaseWorkerPool<T>::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;
|
||||
@@ -376,8 +374,7 @@ unsigned long DatabaseWorkerPool<T>::EscapeString(char *to, const char *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 <class T>
|
||||
|
||||
@@ -299,10 +299,7 @@ void Field::LogWrongType(char* getter) const
|
||||
getter, meta.Type, meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index);
|
||||
}
|
||||
|
||||
#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <mysql.h>
|
||||
#include "MySQLHacks.h"
|
||||
|
||||
static char const* FieldTypeToString(enum_field_types type)
|
||||
{
|
||||
@@ -338,7 +335,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;
|
||||
|
||||
@@ -138,7 +138,7 @@ class TC_DATABASE_API Field
|
||||
private:
|
||||
#ifdef TRINITY_DEBUG
|
||||
void LogWrongType(char* getter) const;
|
||||
void SetMetadata(MYSQL_FIELD* field, uint32 fieldIndex);
|
||||
void SetMetadata(MySQLField* field, uint32 fieldIndex);
|
||||
Metadata meta;
|
||||
#endif
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "CharacterDatabase.h"
|
||||
#include "PreparedStatement.h"
|
||||
#include "MySQLPreparedStatement.h"
|
||||
|
||||
void CharacterDatabaseConnection::DoPrepareStatements()
|
||||
{
|
||||
|
||||
@@ -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) \
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "LoginDatabase.h"
|
||||
#include "PreparedStatement.h"
|
||||
#include "MySQLPreparedStatement.h"
|
||||
|
||||
void LoginDatabaseConnection::DoPrepareStatements()
|
||||
{
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
#include "WorldDatabase.h"
|
||||
#include "PreparedStatement.h"
|
||||
#include "MySQLPreparedStatement.h"
|
||||
|
||||
void WorldDatabaseConnection::DoPrepareStatements()
|
||||
{
|
||||
|
||||
@@ -19,16 +19,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"
|
||||
#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <errmsg.h>
|
||||
#include <mysql.h>
|
||||
#include "MySQLWorkaround.h"
|
||||
#include <mysqld_error.h>
|
||||
|
||||
MySQLConnectionInfo::MySQLConnectionInfo(std::string const& infoString)
|
||||
@@ -130,8 +129,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<MySQLHandle*>(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)
|
||||
{
|
||||
@@ -241,7 +240,7 @@ bool MySQLConnection::Execute(PreparedStatementBase* stmt)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MySQLConnection::_Query(PreparedStatementBase* stmt, MYSQL_RES** pResult, uint64* pRowCount, uint32* pFieldCount)
|
||||
bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLResult** pResult, uint64* pRowCount, uint32* pFieldCount)
|
||||
{
|
||||
if (!m_Mysql)
|
||||
return false;
|
||||
@@ -288,7 +287,7 @@ bool MySQLConnection::_Query(PreparedStatementBase* stmt, MYSQL_RES** pResult, u
|
||||
|
||||
m_mStmt->ClearParameters();
|
||||
|
||||
*pResult = mysql_stmt_result_metadata(msql_STMT);
|
||||
*pResult = reinterpret_cast<MySQLResult*>(mysql_stmt_result_metadata(msql_STMT));
|
||||
*pRowCount = mysql_stmt_num_rows(msql_STMT);
|
||||
*pFieldCount = mysql_stmt_field_count(msql_STMT);
|
||||
|
||||
@@ -300,8 +299,8 @@ ResultSet* MySQLConnection::Query(const char* sql)
|
||||
if (!sql)
|
||||
return NULL;
|
||||
|
||||
MYSQL_RES *result = NULL;
|
||||
MYSQL_FIELD *fields = NULL;
|
||||
MySQLResult* result = NULL;
|
||||
MySQLField* fields = NULL;
|
||||
uint64 rowCount = 0;
|
||||
uint32 fieldCount = 0;
|
||||
|
||||
@@ -311,7 +310,7 @@ ResultSet* MySQLConnection::Query(const char* 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;
|
||||
@@ -333,7 +332,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<MySQLResult*>(mysql_store_result(m_Mysql));
|
||||
*pRowCount = mysql_affected_rows(m_Mysql);
|
||||
*pFieldCount = mysql_field_count(m_Mysql);
|
||||
}
|
||||
@@ -347,7 +346,7 @@ bool MySQLConnection::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD *
|
||||
return false;
|
||||
}
|
||||
|
||||
*pFields = mysql_fetch_fields(*pResult);
|
||||
*pFields = reinterpret_cast<MySQLField*>(mysql_fetch_fields(*pResult));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -418,6 +417,11 @@ int MySQLConnection::ExecuteTransaction(std::shared_ptr<TransactionBase> transac
|
||||
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);
|
||||
@@ -438,6 +442,11 @@ 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());
|
||||
@@ -477,13 +486,13 @@ void MySQLConnection::PrepareStatement(uint32 index, std::string const& sql, Con
|
||||
m_prepareError = true;
|
||||
}
|
||||
else
|
||||
m_stmts[index] = Trinity::make_unique<MySQLPreparedStatement>(stmt, sql);
|
||||
m_stmts[index] = Trinity::make_unique<MySQLPreparedStatement>(reinterpret_cast<MySQLStmt*>(stmt), sql);
|
||||
}
|
||||
}
|
||||
|
||||
PreparedResultSet* MySQLConnection::Query(PreparedStatementBase* stmt)
|
||||
{
|
||||
MYSQL_RES *result = NULL;
|
||||
MySQLResult* result = NULL;
|
||||
uint64 rowCount = 0;
|
||||
uint32 fieldCount = 0;
|
||||
|
||||
@@ -509,7 +518,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -66,19 +66,18 @@ class TC_DATABASE_API MySQLConnection
|
||||
|
||||
bool PrepareStatements();
|
||||
|
||||
public:
|
||||
bool Execute(const char* sql);
|
||||
bool Execute(PreparedStatementBase* stmt);
|
||||
ResultSet* Query(const char* sql);
|
||||
PreparedResultSet* Query(PreparedStatementBase* stmt);
|
||||
bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount);
|
||||
bool _Query(PreparedStatementBase* stmt, MYSQL_RES **pResult, uint64* pRowCount, uint32* pFieldCount);
|
||||
bool _Query(const char* sql, MySQLResult** pResult, MySQLField** pFields, uint64* pRowCount, uint32* pFieldCount);
|
||||
bool _Query(PreparedStatementBase* stmt, MySQLResult** pResult, uint64* pRowCount, uint32* pFieldCount);
|
||||
|
||||
void BeginTransaction();
|
||||
void RollbackTransaction();
|
||||
void CommitTransaction();
|
||||
int ExecuteTransaction(std::shared_ptr<TransactionBase> 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<std::unique_ptr<MySQLPreparedStatement>> 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<SQLOperation*>* m_queue; //! Queue shared with other asynchronous connections.
|
||||
std::unique_ptr<DatabaseWorker> 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;
|
||||
|
||||
34
src/server/database/Database/MySQLHacks.h
Normal file
34
src/server/database/Database/MySQLHacks.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MySQLHacks_h__
|
||||
#define MySQLHacks_h__
|
||||
|
||||
#include "MySQLWorkaround.h"
|
||||
#include <type_traits>
|
||||
|
||||
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<decltype(std::declval<MYSQL_BIND>().is_null)>;
|
||||
|
||||
#endif // MySQLHacks_h__
|
||||
274
src/server/database/Database/MySQLPreparedStatement.cpp
Normal file
274
src/server/database/Database/MySQLPreparedStatement.cpp
Normal file
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "MySQLPreparedStatement.h"
|
||||
#include "Errors.h"
|
||||
#include "Log.h"
|
||||
#include "MySQLHacks.h"
|
||||
#include "PreparedStatement.h"
|
||||
#include <sstream>
|
||||
|
||||
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 = NULL;
|
||||
delete[] (char*) m_bind[i].buffer;
|
||||
m_bind[i].buffer = NULL;
|
||||
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<char *>(param->buffer);
|
||||
param->buffer = new char[len];
|
||||
param->buffer_length = 0;
|
||||
param->is_null_value = 0;
|
||||
param->length = NULL; // 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<char *>(param->buffer);
|
||||
param->buffer = NULL;
|
||||
param->buffer_length = 0;
|
||||
param->is_null_value = 1;
|
||||
delete param->length;
|
||||
param->length = NULL;
|
||||
}
|
||||
|
||||
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<uint8>& 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<char *>(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;
|
||||
}
|
||||
77
src/server/database/Database/MySQLPreparedStatement.h
Normal file
77
src/server/database/Database/MySQLPreparedStatement.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef MySQLPreparedStatement_h__
|
||||
#define MySQLPreparedStatement_h__
|
||||
|
||||
#include "DatabaseEnvFwd.h"
|
||||
#include "Define.h"
|
||||
#include "MySQLWorkaround.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class MySQLConnection;
|
||||
class PreparedStatementBase;
|
||||
|
||||
//- 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<uint8>& value, bool isString);
|
||||
|
||||
uint32 GetParameterCount() const { return m_paramCount; }
|
||||
|
||||
protected:
|
||||
MySQLStmt* GetSTMT() { return m_Mstmt; }
|
||||
MySQLBind* GetBind() { return m_bind; }
|
||||
PreparedStatementBase* m_stmt;
|
||||
void ClearParameters();
|
||||
void AssertValidIndex(uint8 index);
|
||||
std::string getQueryString() const;
|
||||
|
||||
private:
|
||||
MySQLStmt* m_Mstmt;
|
||||
uint32 m_paramCount;
|
||||
std::vector<bool> m_paramsSet;
|
||||
MySQLBind* m_bind;
|
||||
std::string const m_queryString;
|
||||
|
||||
MySQLPreparedStatement(MySQLPreparedStatement const& right) = delete;
|
||||
MySQLPreparedStatement& operator=(MySQLPreparedStatement const& right) = delete;
|
||||
};
|
||||
|
||||
#endif // MySQLPreparedStatement_h__
|
||||
@@ -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 <winsock2.h>
|
||||
#endif
|
||||
#include <mysql.h>
|
||||
#include "MySQLWorkaround.h"
|
||||
|
||||
void MySQL::Library_Init()
|
||||
{
|
||||
@@ -30,3 +27,8 @@ void MySQL::Library_End()
|
||||
{
|
||||
mysql_library_end();
|
||||
}
|
||||
|
||||
char const* TC_DATABASE_API MySQL::GetLibraryVersion()
|
||||
{
|
||||
return MYSQL_SERVER_VERSION;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace MySQL
|
||||
{
|
||||
void TC_DATABASE_API Library_Init();
|
||||
void TC_DATABASE_API Library_End();
|
||||
char const* TC_DATABASE_API GetLibraryVersion();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
21
src/server/database/Database/MySQLWorkaround.h
Normal file
21
src/server/database/Database/MySQLWorkaround.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2019 TrinityCore <https://www.trinitycore.org/>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <mysql.h>
|
||||
@@ -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 <winsock2.h>
|
||||
#endif
|
||||
#include <mysql.h>
|
||||
#include <sstream>
|
||||
#include "MySQLWorkaround.h"
|
||||
|
||||
PreparedStatementBase::PreparedStatementBase(uint32 index, uint8 capacity) :
|
||||
m_stmt(nullptr), m_index(index), statement_data(capacity) { }
|
||||
@@ -204,257 +201,6 @@ void PreparedStatementBase::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 = NULL;
|
||||
delete[] (char*) m_bind[i].buffer;
|
||||
m_bind[i].buffer = NULL;
|
||||
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<char *>(param->buffer);
|
||||
param->buffer = new char[len];
|
||||
param->buffer_length = 0;
|
||||
param->is_null_value = 0;
|
||||
param->length = NULL; // 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<char *>(param->buffer);
|
||||
param->buffer = NULL;
|
||||
param->buffer_length = 0;
|
||||
param->is_null_value = 1;
|
||||
delete param->length;
|
||||
param->length = NULL;
|
||||
}
|
||||
|
||||
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<uint8>& 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<char *>(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;
|
||||
}
|
||||
|
||||
//- Execution
|
||||
PreparedStatementTask::PreparedStatementTask(PreparedStatementBase* stmt, bool async) :
|
||||
m_stmt(stmt), m_result(nullptr)
|
||||
|
||||
@@ -98,6 +98,8 @@ class TC_DATABASE_API PreparedStatementBase
|
||||
void setBinary(const uint8 index, const std::vector<uint8>& value);
|
||||
void setNull(const uint8 index);
|
||||
|
||||
uint32 GetIndex() const { return m_index; }
|
||||
|
||||
protected:
|
||||
void BindParameters(MySQLPreparedStatement* stmt);
|
||||
|
||||
@@ -125,53 +127,6 @@ private:
|
||||
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 PreparedStatementBase;
|
||||
|
||||
public:
|
||||
MySQLPreparedStatement(MYSQL_STMT* 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<uint8>& value, bool isString);
|
||||
|
||||
uint32 GetParameterCount() const { return m_paramCount; }
|
||||
|
||||
protected:
|
||||
MYSQL_STMT* GetSTMT() { return m_Mstmt; }
|
||||
MYSQL_BIND* GetBind() { return m_bind; }
|
||||
PreparedStatementBase* m_stmt;
|
||||
void ClearParameters();
|
||||
void AssertValidIndex(uint8 index);
|
||||
std::string getQueryString() const;
|
||||
|
||||
private:
|
||||
MYSQL_STMT* m_Mstmt;
|
||||
uint32 m_paramCount;
|
||||
std::vector<bool> 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
|
||||
{
|
||||
|
||||
@@ -20,10 +20,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 <winsock2.h>
|
||||
#endif
|
||||
#include <mysql.h>
|
||||
#include "MySQLHacks.h"
|
||||
#include "MySQLWorkaround.h"
|
||||
|
||||
static uint32 SizeForType(MYSQL_FIELD* field)
|
||||
{
|
||||
@@ -119,7 +117,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),
|
||||
@@ -132,7 +130,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),
|
||||
@@ -149,16 +147,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
|
||||
@@ -174,7 +172,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<MySQLField*>(mysql_fetch_fields(m_metadataResult));
|
||||
std::size_t rowSize = 0;
|
||||
for (uint32 i = 0; i < m_fieldCount; ++i)
|
||||
{
|
||||
|
||||
@@ -26,7 +26,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();
|
||||
@@ -43,8 +43,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;
|
||||
@@ -53,7 +53,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();
|
||||
@@ -70,9 +70,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();
|
||||
|
||||
@@ -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 <numeric>
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include <mysql_version.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/opensslv.h>
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user