diff options
Diffstat (limited to 'src/server/database/Database')
34 files changed, 1005 insertions, 841 deletions
diff --git a/src/server/database/Database/AdhocStatement.cpp b/src/server/database/Database/AdhocStatement.cpp index 644af01787b..90ddc1630f4 100644 --- a/src/server/database/Database/AdhocStatement.cpp +++ b/src/server/database/Database/AdhocStatement.cpp @@ -16,10 +16,14 @@ */ #include "AdhocStatement.h" +#include "Errors.h" #include "MySQLConnection.h" +#include "QueryResult.h" +#include <cstdlib> +#include <cstring> /*! Basic, ad-hoc queries. */ -BasicStatementTask::BasicStatementTask(const char* sql, bool async) : +BasicStatementTask::BasicStatementTask(char const* sql, bool async) : m_result(nullptr) { m_sql = strdup(sql); @@ -43,7 +47,7 @@ bool BasicStatementTask::Execute() if (!result || !result->GetRowCount() || !result->NextRow()) { delete result; - m_result->set_value(QueryResult(NULL)); + m_result->set_value(QueryResult(nullptr)); return false; } diff --git a/src/server/database/Database/AdhocStatement.h b/src/server/database/Database/AdhocStatement.h index e419908d175..e42e3474df3 100644 --- a/src/server/database/Database/AdhocStatement.h +++ b/src/server/database/Database/AdhocStatement.h @@ -18,24 +18,22 @@ #ifndef _ADHOCSTATEMENT_H #define _ADHOCSTATEMENT_H -#include <future> +#include "Define.h" +#include "DatabaseEnvFwd.h" #include "SQLOperation.h" -typedef std::future<QueryResult> QueryResultFuture; -typedef std::promise<QueryResult> QueryResultPromise; - /*! Raw, ad-hoc query. */ class TC_DATABASE_API BasicStatementTask : public SQLOperation { public: - BasicStatementTask(const char* sql, bool async = false); + BasicStatementTask(char const* sql, bool async = false); ~BasicStatementTask(); bool Execute() override; QueryResultFuture GetFuture() const { return m_result->get_future(); } private: - const char* m_sql; //- Raw query to be executed + char const* m_sql; //- Raw query to be executed bool m_has_result; QueryResultPromise* m_result; }; diff --git a/src/server/database/Database/DatabaseEnv.cpp b/src/server/database/Database/DatabaseEnv.cpp index 21b937d3c76..c7419f95de8 100644 --- a/src/server/database/Database/DatabaseEnv.cpp +++ b/src/server/database/Database/DatabaseEnv.cpp @@ -17,6 +17,6 @@ #include "DatabaseEnv.h" -WorldDatabaseWorkerPool WorldDatabase; -CharacterDatabaseWorkerPool CharacterDatabase; -LoginDatabaseWorkerPool LoginDatabase; +DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabase; +DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabase; +DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabase; diff --git a/src/server/database/Database/DatabaseEnv.h b/src/server/database/Database/DatabaseEnv.h index dde0d5f1847..301b94567ce 100644 --- a/src/server/database/Database/DatabaseEnv.h +++ b/src/server/database/Database/DatabaseEnv.h @@ -19,30 +19,24 @@ #ifndef DATABASEENV_H #define DATABASEENV_H -#include "Common.h" -#include "Errors.h" -#include "Log.h" - -#include "Field.h" -#include "QueryResult.h" - -#include "MySQLThreading.h" -#include "Transaction.h" - -#define _LIKE_ "LIKE" -#define _TABLE_SIM_ '`' -#define _CONCAT3_(A, B, C) "CONCAT( " A ", " B ", " C " )" -#define _OFFSET_ "LIMIT %d, 1" +#include "Define.h" +#include "DatabaseWorkerPool.h" #include "Implementation/LoginDatabase.h" #include "Implementation/CharacterDatabase.h" #include "Implementation/WorldDatabase.h" +#include "Field.h" +#include "PreparedStatement.h" +#include "QueryCallback.h" +#include "QueryResult.h" +#include "Transaction.h" + /// Accessor to the world database -TC_DATABASE_API extern WorldDatabaseWorkerPool WorldDatabase; +TC_DATABASE_API extern DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabase; /// Accessor to the character database -TC_DATABASE_API extern CharacterDatabaseWorkerPool CharacterDatabase; +TC_DATABASE_API extern DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabase; /// Accessor to the realm/login database -TC_DATABASE_API extern LoginDatabaseWorkerPool LoginDatabase; +TC_DATABASE_API extern DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabase; #endif diff --git a/src/server/database/Database/DatabaseEnvFwd.h b/src/server/database/Database/DatabaseEnvFwd.h new file mode 100644 index 00000000000..893dffaaace --- /dev/null +++ b/src/server/database/Database/DatabaseEnvFwd.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008-2017 TrinityCore <http://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 DatabaseEnvFwd_h__ +#define DatabaseEnvFwd_h__ + +#include <future> +#include <memory> + +class Field; + +class ResultSet; +typedef std::shared_ptr<ResultSet> QueryResult; +typedef std::future<QueryResult> QueryResultFuture; +typedef std::promise<QueryResult> QueryResultPromise; + +class PreparedStatement; + +class PreparedResultSet; +typedef std::shared_ptr<PreparedResultSet> PreparedQueryResult; +typedef std::future<PreparedQueryResult> PreparedQueryResultFuture; +typedef std::promise<PreparedQueryResult> PreparedQueryResultPromise; + +class QueryCallback; + +class Transaction; +typedef std::shared_ptr<Transaction> SQLTransaction; + +class SQLQueryHolder; +typedef std::future<SQLQueryHolder*> QueryResultHolderFuture; +typedef std::promise<SQLQueryHolder*> 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; + +#endif // DatabaseEnvFwd_h__ diff --git a/src/server/database/Database/DatabaseLoader.cpp b/src/server/database/Database/DatabaseLoader.cpp index 31e4b63f46c..6dd5003cc4b 100644 --- a/src/server/database/Database/DatabaseLoader.cpp +++ b/src/server/database/Database/DatabaseLoader.cpp @@ -16,8 +16,10 @@ */ #include "DatabaseLoader.h" -#include "DBUpdater.h" #include "Config.h" +#include "DatabaseEnv.h" +#include "DBUpdater.h" +#include "Log.h" #include <mysqld_error.h> @@ -178,8 +180,8 @@ bool DatabaseLoader::Process(std::queue<Predicate>& queue) } template TC_DATABASE_API -DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(LoginDatabaseWorkerPool&, std::string const&); +DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(DatabaseWorkerPool<LoginDatabaseConnection>&, std::string const&); template TC_DATABASE_API -DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(CharacterDatabaseWorkerPool&, std::string const&); +DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(DatabaseWorkerPool<CharacterDatabaseConnection>&, std::string const&); template TC_DATABASE_API -DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(WorldDatabaseWorkerPool&, std::string const&); +DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(DatabaseWorkerPool<WorldDatabaseConnection>&, std::string const&); diff --git a/src/server/database/Database/DatabaseLoader.h b/src/server/database/Database/DatabaseLoader.h index 472a6811f31..78125b9ac7e 100644 --- a/src/server/database/Database/DatabaseLoader.h +++ b/src/server/database/Database/DatabaseLoader.h @@ -18,12 +18,15 @@ #ifndef DatabaseLoader_h__ #define DatabaseLoader_h__ -#include "DatabaseWorkerPool.h" -#include "DBUpdater.h" +#include "Define.h" #include <functional> -#include <stack> #include <queue> +#include <stack> +#include <string> + +template <class T> +class DatabaseWorkerPool; // A helper class to initiate all database worker pools, // handles updating, delays preparing of statements and cleans up on failure. diff --git a/src/server/database/Database/DatabaseWorker.cpp b/src/server/database/Database/DatabaseWorker.cpp index 54980863861..c26562b4ff1 100644 --- a/src/server/database/Database/DatabaseWorker.cpp +++ b/src/server/database/Database/DatabaseWorker.cpp @@ -15,7 +15,6 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "DatabaseEnv.h" #include "DatabaseWorker.h" #include "SQLOperation.h" #include "ProducerConsumerQueue.h" diff --git a/src/server/database/Database/DatabaseWorker.h b/src/server/database/Database/DatabaseWorker.h index 38444706656..1476a2b9e99 100644 --- a/src/server/database/Database/DatabaseWorker.h +++ b/src/server/database/Database/DatabaseWorker.h @@ -18,8 +18,12 @@ #ifndef _WORKERTHREAD_H #define _WORKERTHREAD_H +#include "Define.h" +#include <atomic> #include <thread> -#include "ProducerConsumerQueue.h" + +template <typename T> +class ProducerConsumerQueue; class MySQLConnection; class SQLOperation; @@ -37,7 +41,7 @@ class TC_DATABASE_API DatabaseWorker void WorkerThread(); std::thread _workerThread; - std::atomic_bool _cancelationToken; + std::atomic<bool> _cancelationToken; DatabaseWorker(DatabaseWorker const& right) = delete; DatabaseWorker& operator=(DatabaseWorker const& right) = delete; diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp index fca4f666310..2d8c22e4d6f 100644 --- a/src/server/database/Database/DatabaseWorkerPool.cpp +++ b/src/server/database/Database/DatabaseWorkerPool.cpp @@ -16,12 +16,39 @@ */ #include "DatabaseWorkerPool.h" -#include "DatabaseEnv.h" +#include "AdhocStatement.h" +#include "Common.h" +#include "Errors.h" +#include "Implementation/LoginDatabase.h" +#include "Implementation/WorldDatabase.h" +#include "Implementation/CharacterDatabase.h" +#include "Log.h" +#include "PreparedStatement.h" +#include "ProducerConsumerQueue.h" #include "QueryCallback.h" +#include "QueryHolder.h" +#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 <mysqld_error.h> #define MIN_MYSQL_SERVER_VERSION 50100u #define MIN_MYSQL_CLIENT_VERSION 50100u +class PingOperation : public SQLOperation +{ + //! Operation for idle delaythreads + bool Execute() override + { + m_conn->Ping(); + return true; + } +}; + template <class T> DatabaseWorkerPool<T>::DatabaseWorkerPool() : _queue(new ProducerConsumerQueue<SQLOperation*>()), @@ -34,6 +61,12 @@ DatabaseWorkerPool<T>::DatabaseWorkerPool() } template <class T> +DatabaseWorkerPool<T>::~DatabaseWorkerPool() +{ + _queue->Cancel(); +} + +template <class T> void DatabaseWorkerPool<T>::SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads) { @@ -135,7 +168,7 @@ bool DatabaseWorkerPool<T>::PrepareStatements() } template <class T> -QueryResult DatabaseWorkerPool<T>::Query(const char* sql, T* connection /*= nullptr*/) +QueryResult DatabaseWorkerPool<T>::Query(char const* sql, T* connection /*= nullptr*/) { if (!connection) connection = GetFreeConnection(); @@ -145,7 +178,7 @@ QueryResult DatabaseWorkerPool<T>::Query(const char* sql, T* connection /*= null if (!result || !result->GetRowCount() || !result->NextRow()) { delete result; - return QueryResult(NULL); + return QueryResult(nullptr); } return QueryResult(result); @@ -164,14 +197,14 @@ PreparedQueryResult DatabaseWorkerPool<T>::Query(PreparedStatement* stmt) if (!ret || !ret->GetRowCount()) { delete ret; - return PreparedQueryResult(NULL); + return PreparedQueryResult(nullptr); } return PreparedQueryResult(ret); } template <class T> -QueryCallback DatabaseWorkerPool<T>::AsyncQuery(const char* sql) +QueryCallback DatabaseWorkerPool<T>::AsyncQuery(char const* sql) { BasicStatementTask* task = new BasicStatementTask(sql, true); // Store future result before enqueueing - task might get already processed and deleted before returning from this method @@ -201,6 +234,12 @@ QueryResultHolderFuture DatabaseWorkerPool<T>::DelayQueryHolder(SQLQueryHolder* } template <class T> +SQLTransaction DatabaseWorkerPool<T>::BeginTransaction() +{ + return std::make_shared<Transaction>(); +} + +template <class T> void DatabaseWorkerPool<T>::CommitTransaction(SQLTransaction transaction) { #ifdef TRINITY_DEBUG @@ -253,6 +292,12 @@ void DatabaseWorkerPool<T>::DirectCommitTransaction(SQLTransaction& transaction) } template <class T> +PreparedStatement* DatabaseWorkerPool<T>::GetPreparedStatement(PreparedStatementIndex index) +{ + return new PreparedStatement(index, _preparedStatementSize[index]); +} + +template <class T> void DatabaseWorkerPool<T>::EscapeString(std::string& str) { if (str.empty()) @@ -325,6 +370,22 @@ uint32 DatabaseWorkerPool<T>::OpenConnections(InternalIndex type, uint8 numConne } template <class T> +unsigned long DatabaseWorkerPool<T>::EscapeString(char* to, char const* from, unsigned long length) +{ + if (!to || !from || !length) + return 0; + + return mysql_real_escape_string( + _connections[IDX_SYNCH].front()->GetHandle(), to, from, length); +} + +template <class T> +void DatabaseWorkerPool<T>::Enqueue(SQLOperation* op) +{ + _queue->Push(op); +} + +template <class T> T* DatabaseWorkerPool<T>::GetFreeConnection() { uint8 i = 0; @@ -342,6 +403,69 @@ T* DatabaseWorkerPool<T>::GetFreeConnection() return connection; } +template <class T> +char const* DatabaseWorkerPool<T>::GetDatabaseName() const +{ + return _connectionInfo->database.c_str(); +} + +template <class T> +void DatabaseWorkerPool<T>::Execute(char const* sql) +{ + if (Trinity::IsFormatEmptyOrNull(sql)) + return; + + BasicStatementTask* task = new BasicStatementTask(sql); + Enqueue(task); +} + +template <class T> +void DatabaseWorkerPool<T>::Execute(PreparedStatement* stmt) +{ + PreparedStatementTask* task = new PreparedStatementTask(stmt); + Enqueue(task); +} + +template <class T> +void DatabaseWorkerPool<T>::DirectExecute(char const* sql) +{ + if (Trinity::IsFormatEmptyOrNull(sql)) + return; + + T* connection = GetFreeConnection(); + connection->Execute(sql); + connection->Unlock(); +} + +template <class T> +void DatabaseWorkerPool<T>::DirectExecute(PreparedStatement* stmt) +{ + T* connection = GetFreeConnection(); + connection->Execute(stmt); + connection->Unlock(); + + //! Delete proxy-class. Not needed anymore + delete stmt; +} + +template <class T> +void DatabaseWorkerPool<T>::ExecuteOrAppend(SQLTransaction& trans, char const* sql) +{ + if (!trans) + Execute(sql); + else + trans->Append(sql); +} + +template <class T> +void DatabaseWorkerPool<T>::ExecuteOrAppend(SQLTransaction& trans, PreparedStatement* stmt) +{ + if (!trans) + Execute(stmt); + else + trans->Append(stmt); +} + template class TC_DATABASE_API DatabaseWorkerPool<LoginDatabaseConnection>; template class TC_DATABASE_API DatabaseWorkerPool<WorldDatabaseConnection>; template class TC_DATABASE_API DatabaseWorkerPool<CharacterDatabaseConnection>; diff --git a/src/server/database/Database/DatabaseWorkerPool.h b/src/server/database/Database/DatabaseWorkerPool.h index b1be44adf32..7b4698a5bce 100644 --- a/src/server/database/Database/DatabaseWorkerPool.h +++ b/src/server/database/Database/DatabaseWorkerPool.h @@ -18,32 +18,18 @@ #ifndef _DATABASEWORKERPOOL_H #define _DATABASEWORKERPOOL_H -#include "Common.h" -#include "MySQLConnection.h" -#include "Transaction.h" -#include "DatabaseWorker.h" -#include "PreparedStatement.h" -#include "Log.h" -#include "QueryResult.h" -#include "QueryHolder.h" -#include "AdhocStatement.h" +#include "Define.h" +#include "DatabaseEnvFwd.h" #include "StringFormat.h" - -#include <mysqld_error.h> -#include <memory> #include <array> +#include <string> +#include <vector> -class QueryCallback; +template <typename T> +class ProducerConsumerQueue; -class PingOperation : public SQLOperation -{ - //! Operation for idle delaythreads - bool Execute() override - { - m_conn->Ping(); - return true; - } -}; +class SQLOperation; +struct MySQLConnectionInfo; template <class T> class DatabaseWorkerPool @@ -60,10 +46,7 @@ class DatabaseWorkerPool /* Activity state */ DatabaseWorkerPool(); - ~DatabaseWorkerPool() - { - _queue->Cancel(); - } + ~DatabaseWorkerPool(); void SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads); @@ -85,14 +68,7 @@ class DatabaseWorkerPool //! Enqueues a one-way SQL operation in string format that will be executed asynchronously. //! This method should only be used for queries that are only executed once, e.g during startup. - void Execute(const char* sql) - { - if (Trinity::IsFormatEmptyOrNull(sql)) - return; - - BasicStatementTask* task = new BasicStatementTask(sql); - Enqueue(task); - } + void Execute(char const* sql); //! Enqueues a one-way SQL operation in string format -with variable args- that will be executed asynchronously. //! This method should only be used for queries that are only executed once, e.g during startup. @@ -107,11 +83,7 @@ class DatabaseWorkerPool //! Enqueues a one-way SQL operation in prepared statement format that will be executed asynchronously. //! Statement must be prepared with CONNECTION_ASYNC flag. - void Execute(PreparedStatement* stmt) - { - PreparedStatementTask* task = new PreparedStatementTask(stmt); - Enqueue(task); - } + void Execute(PreparedStatement* stmt); /** Direct synchronous one-way statement methods. @@ -119,15 +91,7 @@ class DatabaseWorkerPool //! Directly executes a one-way SQL operation in string format, that will block the calling thread until finished. //! This method should only be used for queries that are only executed once, e.g during startup. - void DirectExecute(const char* sql) - { - if (Trinity::IsFormatEmptyOrNull(sql)) - return; - - T* connection = GetFreeConnection(); - connection->Execute(sql); - connection->Unlock(); - } + void DirectExecute(char const* sql); //! Directly executes a one-way SQL operation in string format -with variable args-, that will block the calling thread until finished. //! This method should only be used for queries that are only executed once, e.g during startup. @@ -142,15 +106,7 @@ class DatabaseWorkerPool //! Directly executes a one-way SQL operation in prepared statement format, that will block the calling thread until finished. //! Statement must be prepared with the CONNECTION_SYNCH flag. - void DirectExecute(PreparedStatement* stmt) - { - T* connection = GetFreeConnection(); - connection->Execute(stmt); - connection->Unlock(); - - //! Delete proxy-class. Not needed anymore - delete stmt; - } + void DirectExecute(PreparedStatement* stmt); /** Synchronous query (with resultset) methods. @@ -158,7 +114,7 @@ class DatabaseWorkerPool //! Directly executes an SQL query in string format that will block the calling thread until finished. //! Returns reference counted auto pointer, no need for manual memory management in upper level code. - QueryResult Query(const char* sql, T* connection = nullptr); + QueryResult Query(char const* sql, T* connection = nullptr); //! Directly executes an SQL query in string format -with variable args- that will block the calling thread until finished. //! Returns reference counted auto pointer, no need for manual memory management in upper level code. @@ -193,7 +149,7 @@ class DatabaseWorkerPool //! Enqueues a query in string format that will set the value of the QueryResultFuture return object as soon as the query is executed. //! The return value is then processed in ProcessQueryCallback methods. - QueryCallback AsyncQuery(const char* sql); + QueryCallback AsyncQuery(char const* sql); //! Enqueues a query in prepared format that will set the value of the PreparedQueryResultFuture return object as soon as the query is executed. //! The return value is then processed in ProcessQueryCallback methods. @@ -211,10 +167,7 @@ class DatabaseWorkerPool */ //! Begins an automanaged transaction pointer that will automatically rollback if not commited. (Autocommit=0) - SQLTransaction BeginTransaction() - { - return SQLTransaction(new Transaction); - } + SQLTransaction BeginTransaction(); //! Enqueues a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations //! were appended to the transaction will be respected during execution. @@ -224,25 +177,13 @@ class DatabaseWorkerPool //! were appended to the transaction will be respected during execution. void DirectCommitTransaction(SQLTransaction& transaction); - //! Method used to execute prepared statements in a diverse context. + //! Method used to execute ad-hoc statements in a diverse context. //! Will be wrapped in a transaction if valid object is present, otherwise executed standalone. - void ExecuteOrAppend(SQLTransaction& trans, PreparedStatement* stmt) - { - if (!trans) - Execute(stmt); - else - trans->Append(stmt); - } + void ExecuteOrAppend(SQLTransaction& trans, char const* sql); - //! Method used to execute ad-hoc statements in a diverse context. + //! Method used to execute prepared statements in a diverse context. //! Will be wrapped in a transaction if valid object is present, otherwise executed standalone. - void ExecuteOrAppend(SQLTransaction& trans, const char* sql) - { - if (!trans) - Execute(sql); - else - trans->Append(sql); - } + void ExecuteOrAppend(SQLTransaction& trans, PreparedStatement* stmt); /** Other @@ -253,10 +194,7 @@ class DatabaseWorkerPool //! Automanaged (internally) pointer to a prepared statement object for usage in upper level code. //! Pointer is deleted in this->DirectExecute(PreparedStatement*), this->Query(PreparedStatement*) or PreparedStatementTask::~PreparedStatementTask. //! This object is not tied to the prepared statement on the MySQL context yet until execution. - PreparedStatement* GetPreparedStatement(PreparedStatementIndex index) - { - return new PreparedStatement(index, _preparedStatementSize[index]); - } + PreparedStatement* GetPreparedStatement(PreparedStatementIndex index); //! Apply escape string'ing for current collation. (utf8) void EscapeString(std::string& str); @@ -267,28 +205,15 @@ class DatabaseWorkerPool private: uint32 OpenConnections(InternalIndex type, uint8 numConnections); - unsigned long EscapeString(char *to, const char *from, unsigned long length) - { - if (!to || !from || !length) - return 0; + unsigned long EscapeString(char* to, char const* from, unsigned long length); - return mysql_real_escape_string( - _connections[IDX_SYNCH].front()->GetHandle(), to, from, length); - } - - void Enqueue(SQLOperation* op) - { - _queue->Push(op); - } + void Enqueue(SQLOperation* op); //! Gets a free connection in the synchronous connection pool. //! Caller MUST call t->Unlock() after touching the MySQL context to prevent deadlocks. T* GetFreeConnection(); - char const* GetDatabaseName() const - { - return _connectionInfo->database.c_str(); - } + char const* GetDatabaseName() const; //! Queue shared by async worker threads. std::unique_ptr<ProducerConsumerQueue<SQLOperation*>> _queue; diff --git a/src/server/database/Database/Field.cpp b/src/server/database/Database/Field.cpp index 29326287e63..fee9ff6467d 100644 --- a/src/server/database/Database/Field.cpp +++ b/src/server/database/Database/Field.cpp @@ -16,11 +16,12 @@ */ #include "Field.h" +#include "Log.h" Field::Field() { - data.value = NULL; - data.type = MYSQL_TYPE_NULL; + data.value = nullptr; + data.type = DatabaseFieldTypes::Null; data.length = 0; data.raw = false; } @@ -30,7 +31,236 @@ Field::~Field() CleanUp(); } -void Field::SetByteValue(void* newValue, enum_field_types newType, uint32 length) +uint8 Field::GetUInt8() const +{ + if (!data.value) + return 0; + +#ifdef TRINITY_DEBUG + if (!IsType(DatabaseFieldTypes::Int8)) + { + TC_LOG_WARN("sql.sql", "Warning: GetUInt8() on non-tinyint field %s.%s (%s.%s) at index %u. Using type: %s.", + meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast<uint8*>(data.value); + return static_cast<uint8>(strtoul((char*)data.value, nullptr, 10)); +} + +int8 Field::GetInt8() const +{ + if (!data.value) + return 0; + +#ifdef TRINITY_DEBUG + if (!IsType(DatabaseFieldTypes::Int8)) + { + TC_LOG_WARN("sql.sql", "Warning: GetInt8() on non-tinyint field %s.%s (%s.%s) at index %u. Using type: %s.", + meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast<int8*>(data.value); + return static_cast<int8>(strtol((char*)data.value, nullptr, 10)); +} + +uint16 Field::GetUInt16() const +{ + if (!data.value) + return 0; + +#ifdef TRINITY_DEBUG + if (!IsType(DatabaseFieldTypes::Int16)) + { + TC_LOG_WARN("sql.sql", "Warning: GetUInt16() on non-smallint field %s.%s (%s.%s) at index %u. Using type: %s.", + meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast<uint16*>(data.value); + return static_cast<uint16>(strtoul((char*)data.value, nullptr, 10)); +} + +int16 Field::GetInt16() const +{ + if (!data.value) + return 0; + +#ifdef TRINITY_DEBUG + if (!IsType(DatabaseFieldTypes::Int16)) + { + TC_LOG_WARN("sql.sql", "Warning: GetInt16() on non-smallint field %s.%s (%s.%s) at index %u. Using type: %s.", + meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast<int16*>(data.value); + return static_cast<int16>(strtol((char*)data.value, nullptr, 10)); +} + +uint32 Field::GetUInt32() const +{ + if (!data.value) + return 0; + +#ifdef TRINITY_DEBUG + if (!IsType(DatabaseFieldTypes::Int32)) + { + TC_LOG_WARN("sql.sql", "Warning: GetUInt32() on non-(medium)int field %s.%s (%s.%s) at index %u. Using type: %s.", + meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast<uint32*>(data.value); + return static_cast<uint32>(strtoul((char*)data.value, nullptr, 10)); +} + +int32 Field::GetInt32() const +{ + if (!data.value) + return 0; + +#ifdef TRINITY_DEBUG + if (!IsType(DatabaseFieldTypes::Int32)) + { + TC_LOG_WARN("sql.sql", "Warning: GetInt32() on non-(medium)int field %s.%s (%s.%s) at index %u. Using type: %s.", + meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast<int32*>(data.value); + return static_cast<int32>(strtol((char*)data.value, nullptr, 10)); +} + +uint64 Field::GetUInt64() const +{ + if (!data.value) + return 0; + +#ifdef TRINITY_DEBUG + if (!IsType(DatabaseFieldTypes::Int64)) + { + TC_LOG_WARN("sql.sql", "Warning: GetUInt64() on non-bigint field %s.%s (%s.%s) at index %u. Using type: %s.", + meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast<uint64*>(data.value); + return static_cast<uint64>(strtoull((char*)data.value, nullptr, 10)); +} + +int64 Field::GetInt64() const +{ + if (!data.value) + return 0; + +#ifdef TRINITY_DEBUG + if (!IsType(DatabaseFieldTypes::Int64)) + { + TC_LOG_WARN("sql.sql", "Warning: GetInt64() on non-bigint field %s.%s (%s.%s) at index %u. Using type: %s.", + meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); + return 0; + } +#endif + + if (data.raw) + return *reinterpret_cast<int64*>(data.value); + return static_cast<int64>(strtoll((char*)data.value, nullptr, 10)); +} + +float Field::GetFloat() const +{ + if (!data.value) + return 0.0f; + +#ifdef TRINITY_DEBUG + if (!IsType(DatabaseFieldTypes::Float)) + { + TC_LOG_WARN("sql.sql", "Warning: GetFloat() on non-float field %s.%s (%s.%s) at index %u. Using type: %s.", + meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); + return 0.0f; + } +#endif + + if (data.raw) + return *reinterpret_cast<float*>(data.value); + return static_cast<float>(atof((char*)data.value)); +} + +double Field::GetDouble() const +{ + if (!data.value) + return 0.0f; + +#ifdef TRINITY_DEBUG + if (!IsType(DatabaseFieldTypes::Double) && !IsType(DatabaseFieldTypes::Decimal)) + { + TC_LOG_WARN("sql.sql", "Warning: GetDouble() on non-double/non-decimal field %s.%s (%s.%s) at index %u. Using type: %s.", + meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); + return 0.0f; + } +#endif + + if (data.raw && !IsType(DatabaseFieldTypes::Decimal)) + return *reinterpret_cast<double*>(data.value); + return static_cast<double>(atof((char*)data.value)); +} + +char const* Field::GetCString() const +{ + if (!data.value) + return nullptr; + +#ifdef TRINITY_DEBUG + if (IsNumeric() && data.raw) + { + TC_LOG_WARN("sql.sql", "Error: GetCString() on numeric field %s.%s (%s.%s) at index %u. Using type: %s.", + meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); + return nullptr; + } +#endif + return static_cast<char const*>(data.value); +} + +std::string Field::GetString() const +{ + if (!data.value) + return ""; + + char const* string = GetCString(); + if (!string) + return ""; + + return std::string(string, data.length); +} + +std::vector<uint8> Field::GetBinary() const +{ + std::vector<uint8> result; + if (!data.value || !data.length) + return result; + + result.resize(data.length); + memcpy(result.data(), data.value, data.length); + return result; +} + +void Field::SetByteValue(void* newValue, DatabaseFieldTypes newType, uint32 length) { // This value stores raw bytes that have to be explicitly cast later data.value = newValue; @@ -39,7 +269,7 @@ void Field::SetByteValue(void* newValue, enum_field_types newType, uint32 length data.raw = true; } -void Field::SetStructuredValue(char* newValue, enum_field_types newType, uint32 length) +void Field::SetStructuredValue(char* newValue, DatabaseFieldTypes newType, uint32 length) { if (data.value) CleanUp(); @@ -56,3 +286,70 @@ void Field::SetStructuredValue(char* newValue, enum_field_types newType, uint32 data.type = newType; data.raw = false; } + +bool Field::IsType(DatabaseFieldTypes type) const +{ + return data.type == type; +} + +bool Field::IsNumeric() const +{ + return (data.type == DatabaseFieldTypes::Int8 || + data.type == DatabaseFieldTypes::Int16 || + data.type == DatabaseFieldTypes::Int32 || + data.type == DatabaseFieldTypes::Int64 || + data.type == DatabaseFieldTypes::Float || + data.type == DatabaseFieldTypes::Double); +} + +#ifdef TRINITY_DEBUG + +#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> + +static char const* FieldTypeToString(enum_field_types type) +{ + switch (type) + { + case MYSQL_TYPE_BIT: return "BIT"; + case MYSQL_TYPE_BLOB: return "BLOB"; + case MYSQL_TYPE_DATE: return "DATE"; + case MYSQL_TYPE_DATETIME: return "DATETIME"; + case MYSQL_TYPE_NEWDECIMAL: return "NEWDECIMAL"; + case MYSQL_TYPE_DECIMAL: return "DECIMAL"; + case MYSQL_TYPE_DOUBLE: return "DOUBLE"; + case MYSQL_TYPE_ENUM: return "ENUM"; + case MYSQL_TYPE_FLOAT: return "FLOAT"; + case MYSQL_TYPE_GEOMETRY: return "GEOMETRY"; + case MYSQL_TYPE_INT24: return "INT24"; + case MYSQL_TYPE_LONG: return "LONG"; + case MYSQL_TYPE_LONGLONG: return "LONGLONG"; + case MYSQL_TYPE_LONG_BLOB: return "LONG_BLOB"; + case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB"; + case MYSQL_TYPE_NEWDATE: return "NEWDATE"; + case MYSQL_TYPE_NULL: return "NULL"; + case MYSQL_TYPE_SET: return "SET"; + case MYSQL_TYPE_SHORT: return "SHORT"; + case MYSQL_TYPE_STRING: return "STRING"; + case MYSQL_TYPE_TIME: return "TIME"; + case MYSQL_TYPE_TIMESTAMP: return "TIMESTAMP"; + case MYSQL_TYPE_TINY: return "TINY"; + case MYSQL_TYPE_TINY_BLOB: return "TINY_BLOB"; + case MYSQL_TYPE_VAR_STRING: return "VAR_STRING"; + case MYSQL_TYPE_YEAR: return "YEAR"; + default: return "-Unknown-"; + } +} + +void Field::SetMetadata(MYSQL_FIELD* field, uint32 fieldIndex) +{ + meta.TableName = field->org_table; + meta.TableAlias = field->table; + meta.Name = field->org_name; + meta.Alias = field->name; + meta.Type = FieldTypeToString(field->type); + meta.Index = fieldIndex; +} +#endif diff --git a/src/server/database/Database/Field.h b/src/server/database/Database/Field.h index 4daa447e481..f7f8397b761 100644 --- a/src/server/database/Database/Field.h +++ b/src/server/database/Database/Field.h @@ -18,10 +18,23 @@ #ifndef _FIELD_H #define _FIELD_H -#include "Common.h" -#include "Log.h" +#include "Define.h" +#include "DatabaseEnvFwd.h" +#include <vector> -#include <mysql.h> +enum class DatabaseFieldTypes : uint8 +{ + Null, + Int8, + Int16, + Int32, + Int64, + Float, + Double, + Decimal, + Date, + Binary +}; /** @class Field @@ -67,238 +80,23 @@ class TC_DATABASE_API Field return GetUInt8() == 1 ? true : false; } - uint8 GetUInt8() const - { - if (!data.value) - return 0; - - #ifdef TRINITY_DEBUG - if (!IsType(MYSQL_TYPE_TINY)) - { - TC_LOG_WARN("sql.sql", "Warning: GetUInt8() on non-tinyint field %s.%s (%s.%s) at index %u. Using type: %s.", - meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); - return 0; - } - #endif - - if (data.raw) - return *reinterpret_cast<uint8*>(data.value); - return static_cast<uint8>(strtoul((char*)data.value, nullptr, 10)); - } - - int8 GetInt8() const - { - if (!data.value) - return 0; - - #ifdef TRINITY_DEBUG - if (!IsType(MYSQL_TYPE_TINY)) - { - TC_LOG_WARN("sql.sql", "Warning: GetInt8() on non-tinyint field %s.%s (%s.%s) at index %u. Using type: %s.", - meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); - return 0; - } - #endif - - if (data.raw) - return *reinterpret_cast<int8*>(data.value); - return static_cast<int8>(strtol((char*)data.value, NULL, 10)); - } - - uint16 GetUInt16() const - { - if (!data.value) - return 0; - - #ifdef TRINITY_DEBUG - if (!IsType(MYSQL_TYPE_SHORT) && !IsType(MYSQL_TYPE_YEAR)) - { - TC_LOG_WARN("sql.sql", "Warning: GetUInt16() on non-smallint field %s.%s (%s.%s) at index %u. Using type: %s.", - meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); - return 0; - } - #endif - - if (data.raw) - return *reinterpret_cast<uint16*>(data.value); - return static_cast<uint16>(strtoul((char*)data.value, nullptr, 10)); - } - - int16 GetInt16() const - { - if (!data.value) - return 0; - - #ifdef TRINITY_DEBUG - if (!IsType(MYSQL_TYPE_SHORT) && !IsType(MYSQL_TYPE_YEAR)) - { - TC_LOG_WARN("sql.sql", "Warning: GetInt16() on non-smallint field %s.%s (%s.%s) at index %u. Using type: %s.", - meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); - return 0; - } - #endif - - if (data.raw) - return *reinterpret_cast<int16*>(data.value); - return static_cast<int16>(strtol((char*)data.value, NULL, 10)); - } - - uint32 GetUInt32() const - { - if (!data.value) - return 0; - - #ifdef TRINITY_DEBUG - if (!IsType(MYSQL_TYPE_INT24) && !IsType(MYSQL_TYPE_LONG)) - { - TC_LOG_WARN("sql.sql", "Warning: GetUInt32() on non-(medium)int field %s.%s (%s.%s) at index %u. Using type: %s.", - meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); - return 0; - } - #endif - - if (data.raw) - return *reinterpret_cast<uint32*>(data.value); - return static_cast<uint32>(strtoul((char*)data.value, nullptr, 10)); - } - - int32 GetInt32() const - { - if (!data.value) - return 0; - - #ifdef TRINITY_DEBUG - if (!IsType(MYSQL_TYPE_INT24) && !IsType(MYSQL_TYPE_LONG)) - { - TC_LOG_WARN("sql.sql", "Warning: GetInt32() on non-(medium)int field %s.%s (%s.%s) at index %u. Using type: %s.", - meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); - return 0; - } - #endif - - if (data.raw) - return *reinterpret_cast<int32*>(data.value); - return static_cast<int32>(strtol((char*)data.value, NULL, 10)); - } - - uint64 GetUInt64() const - { - if (!data.value) - return 0; - - #ifdef TRINITY_DEBUG - if (!IsType(MYSQL_TYPE_LONGLONG) && !IsType(MYSQL_TYPE_BIT)) - { - TC_LOG_WARN("sql.sql", "Warning: GetUInt64() on non-bigint field %s.%s (%s.%s) at index %u. Using type: %s.", - meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); - return 0; - } - #endif - - if (data.raw) - return *reinterpret_cast<uint64*>(data.value); - return static_cast<uint64>(strtoull((char*)data.value, nullptr, 10)); - } - - int64 GetInt64() const - { - if (!data.value) - return 0; - - #ifdef TRINITY_DEBUG - if (!IsType(MYSQL_TYPE_LONGLONG) && !IsType(MYSQL_TYPE_BIT)) - { - TC_LOG_WARN("sql.sql", "Warning: GetInt64() on non-bigint field %s.%s (%s.%s) at index %u. Using type: %s.", - meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); - return 0; - } - #endif - - if (data.raw) - return *reinterpret_cast<int64*>(data.value); - return static_cast<int64>(strtoll((char*)data.value, NULL, 10)); - } - - float GetFloat() const - { - if (!data.value) - return 0.0f; - - #ifdef TRINITY_DEBUG - if (!IsType(MYSQL_TYPE_FLOAT)) - { - TC_LOG_WARN("sql.sql", "Warning: GetFloat() on non-float field %s.%s (%s.%s) at index %u. Using type: %s.", - meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); - return 0.0f; - } - #endif - - if (data.raw) - return *reinterpret_cast<float*>(data.value); - return static_cast<float>(atof((char*)data.value)); - } - - double GetDouble() const - { - if (!data.value) - return 0.0f; - - #ifdef TRINITY_DEBUG - if (!IsType(MYSQL_TYPE_DOUBLE) && !IsType(MYSQL_TYPE_NEWDECIMAL)) - { - TC_LOG_WARN("sql.sql", "Warning: GetDouble() on non-double/non-decimal field %s.%s (%s.%s) at index %u. Using type: %s.", - meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); - return 0.0f; - } - #endif - - if (data.raw && !IsType(MYSQL_TYPE_NEWDECIMAL)) - return *reinterpret_cast<double*>(data.value); - return static_cast<double>(atof((char*)data.value)); - } - - char const* GetCString() const - { - if (!data.value) - return NULL; - - #ifdef TRINITY_DEBUG - if (IsNumeric()) - { - TC_LOG_WARN("sql.sql", "Error: GetCString() on numeric field %s.%s (%s.%s) at index %u. Using type: %s.", - meta.TableAlias, meta.Alias, meta.TableName, meta.Name, meta.Index, meta.Type); - return NULL; - } - #endif - return static_cast<char const*>(data.value); - } - - std::string GetString() const - { - if (!data.value) - return ""; - - char const* string = GetCString(); - if (!string) - return ""; - - return std::string(string, data.length); - } - - std::vector<uint8> GetBinary() const - { - std::vector<uint8> result; - if (!data.value || !data.length) - return result; - - result.resize(data.length); - memcpy(result.data(), data.value, data.length); - return result; - } + uint8 GetUInt8() const; + int8 GetInt8() const; + uint16 GetUInt16() const; + int16 GetInt16() const; + uint32 GetUInt32() const; + int32 GetInt32() const; + uint64 GetUInt64() const; + int64 GetInt64() const; + float GetFloat() const; + double GetDouble() const; + char const* GetCString() const; + std::string GetString() const; + std::vector<uint8> GetBinary() const; bool IsNull() const { - return data.value == NULL; + return data.value == nullptr; } struct Metadata @@ -315,138 +113,32 @@ class TC_DATABASE_API Field #pragma pack(push, 1) struct { - uint32 length; // Length (prepared strings only) - void* value; // Actual data in memory - enum_field_types type; // Field type - bool raw; // Raw bytes? (Prepared statement or ad hoc) + uint32 length; // Length (prepared strings only) + void* value; // Actual data in memory + DatabaseFieldTypes type; // Field type + bool raw; // Raw bytes? (Prepared statement or ad hoc) } data; #pragma pack(pop) - void SetByteValue(void* newValue, enum_field_types newType, uint32 length); - void SetStructuredValue(char* newValue, enum_field_types newType, uint32 length); + void SetByteValue(void* newValue, DatabaseFieldTypes newType, uint32 length); + void SetStructuredValue(char* newValue, DatabaseFieldTypes newType, uint32 length); void CleanUp() { // Field does not own the data if fetched with prepared statement if (!data.raw) delete[] ((char*)data.value); - data.value = NULL; - } - - static uint32 SizeForType(MYSQL_FIELD* field) - { - switch (field->type) - { - case MYSQL_TYPE_NULL: - return 0; - case MYSQL_TYPE_TINY: - return 1; - case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_SHORT: - return 2; - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_LONG: - case MYSQL_TYPE_FLOAT: - return 4; - case MYSQL_TYPE_DOUBLE: - case MYSQL_TYPE_LONGLONG: - case MYSQL_TYPE_BIT: - return 8; - - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_DATETIME: - return sizeof(MYSQL_TIME); - - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_VAR_STRING: - return field->max_length + 1; - - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_NEWDECIMAL: - return 64; - - case MYSQL_TYPE_GEOMETRY: - /* - Following types are not sent over the wire: - MYSQL_TYPE_ENUM: - MYSQL_TYPE_SET: - */ - default: - TC_LOG_WARN("sql.sql", "SQL::SizeForType(): invalid field type %u", uint32(field->type)); - return 0; - } + data.value = nullptr; } - bool IsType(enum_field_types type) const - { - return data.type == type; - } + bool IsType(DatabaseFieldTypes type) const; - bool IsNumeric() const - { - return (data.type == MYSQL_TYPE_TINY || - data.type == MYSQL_TYPE_SHORT || - data.type == MYSQL_TYPE_INT24 || - data.type == MYSQL_TYPE_LONG || - data.type == MYSQL_TYPE_FLOAT || - data.type == MYSQL_TYPE_DOUBLE || - data.type == MYSQL_TYPE_LONGLONG ); - } + bool IsNumeric() const; private: #ifdef TRINITY_DEBUG - static char const* FieldTypeToString(enum_field_types type) - { - switch (type) - { - case MYSQL_TYPE_BIT: return "BIT"; - case MYSQL_TYPE_BLOB: return "BLOB"; - case MYSQL_TYPE_DATE: return "DATE"; - case MYSQL_TYPE_DATETIME: return "DATETIME"; - case MYSQL_TYPE_NEWDECIMAL: return "NEWDECIMAL"; - case MYSQL_TYPE_DECIMAL: return "DECIMAL"; - case MYSQL_TYPE_DOUBLE: return "DOUBLE"; - case MYSQL_TYPE_ENUM: return "ENUM"; - case MYSQL_TYPE_FLOAT: return "FLOAT"; - case MYSQL_TYPE_GEOMETRY: return "GEOMETRY"; - case MYSQL_TYPE_INT24: return "INT24"; - case MYSQL_TYPE_LONG: return "LONG"; - case MYSQL_TYPE_LONGLONG: return "LONGLONG"; - case MYSQL_TYPE_LONG_BLOB: return "LONG_BLOB"; - case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB"; - case MYSQL_TYPE_NEWDATE: return "NEWDATE"; - case MYSQL_TYPE_NULL: return "NULL"; - case MYSQL_TYPE_SET: return "SET"; - case MYSQL_TYPE_SHORT: return "SHORT"; - case MYSQL_TYPE_STRING: return "STRING"; - case MYSQL_TYPE_TIME: return "TIME"; - case MYSQL_TYPE_TIMESTAMP: return "TIMESTAMP"; - case MYSQL_TYPE_TINY: return "TINY"; - case MYSQL_TYPE_TINY_BLOB: return "TINY_BLOB"; - case MYSQL_TYPE_VAR_STRING: return "VAR_STRING"; - case MYSQL_TYPE_YEAR: return "YEAR"; - default: return "-Unknown-"; - } - } - - void SetMetadata(MYSQL_FIELD* field, uint32 fieldIndex) - { - meta.TableName = field->org_table; - meta.TableAlias = field->table; - meta.Name = field->org_name; - meta.Alias = field->name; - meta.Type = FieldTypeToString(field->type); - meta.Index = fieldIndex; - } - + void SetMetadata(MYSQL_FIELD* 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 ca845ccd5a9..398973a7e85 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -16,6 +16,7 @@ */ #include "CharacterDatabase.h" +#include "PreparedStatement.h" void CharacterDatabaseConnection::DoPrepareStatements() { @@ -610,3 +611,15 @@ void CharacterDatabaseConnection::DoPrepareStatements() // DeserterTracker PrepareStatement(CHAR_INS_DESERTER_TRACK, "INSERT INTO battleground_deserters (guid, type, datetime) VALUES (?, ?, NOW())", CONNECTION_ASYNC); } + +CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) +{ +} + +CharacterDatabaseConnection::CharacterDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) +{ +} + +CharacterDatabaseConnection::~CharacterDatabaseConnection() +{ +} diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 28408886a8e..8f59059abc0 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -18,10 +18,9 @@ #ifndef _CHARACTERDATABASE_H #define _CHARACTERDATABASE_H -#include "DatabaseWorkerPool.h" #include "MySQLConnection.h" -enum CharacterDatabaseStatements +enum CharacterDatabaseStatements : uint32 { /* Naming standard for defines: {DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed} @@ -536,13 +535,12 @@ public: typedef CharacterDatabaseStatements Statements; //- Constructors for sync and async connections - CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { } - CharacterDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { } + CharacterDatabaseConnection(MySQLConnectionInfo& connInfo); + CharacterDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo); + ~CharacterDatabaseConnection(); //- Loads database type specific prepared statements void DoPrepareStatements() override; }; -typedef DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabaseWorkerPool; - #endif diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 360f0648254..3001e4c6df7 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -16,6 +16,7 @@ */ #include "LoginDatabase.h" +#include "PreparedStatement.h" void LoginDatabaseConnection::DoPrepareStatements() { @@ -63,7 +64,7 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_INS_REALM_CHARACTERS, "INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_SUM_REALM_CHARACTERS, "SELECT SUM(numchars) FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_ACCOUNT, "INSERT INTO account(username, sha_pass_hash, reg_mail, email, joindate) VALUES(?, ?, ?, ?, NOW())", CONNECTION_SYNCH); - PrepareStatement(LOGIN_INS_REALM_CHARACTERS_INIT, "INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist, account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL", CONNECTION_ASYNC); + PrepareStatement(LOGIN_INS_REALM_CHARACTERS_INIT, "INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist, account LEFT JOIN realmcharacters ON acctid = account.id WHERE acctid IS NULL", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_EXPANSION, "UPDATE account SET expansion = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_ACCOUNT_LOCK, "UPDATE account SET locked = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_ACCOUNT_LOCK_CONTRY, "UPDATE account SET lock_country = ? WHERE id = ?", CONNECTION_ASYNC); @@ -90,7 +91,7 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME, "SELECT 1 FROM account WHERE username = ? AND sha_pass_hash = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_PINFO, "SELECT a.username, aa.gmlevel, a.email, a.reg_mail, a.last_ip, DATE_FORMAT(a.last_login, '%Y-%m-%d %T'), a.mutetime, a.mutereason, a.muteby, a.failed_logins, a.locked, a.OS FROM account a LEFT JOIN account_access aa ON (a.id = aa.id AND (aa.RealmID = ? OR aa.RealmID = -1)) WHERE a.id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_PINFO_BANS, "SELECT unbandate, bandate = unbandate, bannedby, banreason FROM account_banned WHERE id = ? AND active ORDER BY bandate ASC LIMIT 1", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_GM_ACCOUNTS, "SELECT a.username, aa.gmlevel FROM account a, account_access aa WHERE a.id=aa.id AND aa.gmlevel >= ? AND (aa.realmid = -1 OR aa.realmid = ?)", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_GM_ACCOUNTS, "SELECT a.username, aa.gmlevel FROM account a, account_access aa WHERE a.id = aa.id AND aa.gmlevel >= ? AND (aa.realmid = -1 OR aa.realmid = ?)", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_INFO, "SELECT a.username, a.last_ip, aa.gmlevel, a.expansion FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS_GMLEVEL_TEST, "SELECT 1 FROM account_access WHERE id = ? AND gmlevel > ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS, "SELECT a.id, aa.gmlevel, aa.RealmID FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH); @@ -119,3 +120,15 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_ACCOUNT_MUTE_INFO, "SELECT mutedate, mutetime, mutereason, mutedby FROM account_muted WHERE guid = ? ORDER BY mutedate ASC", CONNECTION_SYNCH); PrepareStatement(LOGIN_DEL_ACCOUNT_MUTED, "DELETE FROM account_muted WHERE guid = ?", CONNECTION_ASYNC); } + +LoginDatabaseConnection::LoginDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) +{ +} + +LoginDatabaseConnection::LoginDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) +{ +} + +LoginDatabaseConnection::~LoginDatabaseConnection() +{ +} diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index dcb77394b42..f199d81f55c 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -18,10 +18,9 @@ #ifndef _LOGINDATABASE_H #define _LOGINDATABASE_H -#include "DatabaseWorkerPool.h" #include "MySQLConnection.h" -enum LoginDatabaseStatements +enum LoginDatabaseStatements : uint32 { /* Naming standard for defines: {DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed} @@ -123,13 +122,12 @@ public: typedef LoginDatabaseStatements Statements; //- Constructors for sync and async connections - LoginDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { } - LoginDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { } + LoginDatabaseConnection(MySQLConnectionInfo& connInfo); + LoginDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo); + ~LoginDatabaseConnection(); //- Loads database type specific prepared statements void DoPrepareStatements() override; }; -typedef DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabaseWorkerPool; - #endif diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index c4550144908..c64e7a9c2fc 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -16,6 +16,7 @@ */ #include "WorldDatabase.h" +#include "PreparedStatement.h" void WorldDatabaseConnection::DoPrepareStatements() { @@ -92,3 +93,15 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA, "UPDATE creature SET zoneId = ?, areaId = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA, "UPDATE gameobject SET zoneId = ?, areaId = ? WHERE guid = ?", CONNECTION_ASYNC); } + +WorldDatabaseConnection::WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) +{ +} + +WorldDatabaseConnection::WorldDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) +{ +} + +WorldDatabaseConnection::~WorldDatabaseConnection() +{ +} diff --git a/src/server/database/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h index 069f5fe0555..b68f53bb173 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.h +++ b/src/server/database/Database/Implementation/WorldDatabase.h @@ -18,10 +18,9 @@ #ifndef _WORLDDATABASE_H #define _WORLDDATABASE_H -#include "DatabaseWorkerPool.h" #include "MySQLConnection.h" -enum WorldDatabaseStatements +enum WorldDatabaseStatements : uint32 { /* Naming standard for defines: {DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed} @@ -109,13 +108,12 @@ public: typedef WorldDatabaseStatements Statements; //- Constructors for sync and async connections - WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { } - WorldDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { } + WorldDatabaseConnection(MySQLConnectionInfo& connInfo); + WorldDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo); + ~WorldDatabaseConnection(); //- Loads database type specific prepared statements void DoPrepareStatements() override; }; -typedef DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabaseWorkerPool; - #endif diff --git a/src/server/database/Database/MySQLConnection.cpp b/src/server/database/Database/MySQLConnection.cpp index 6e862ae6abe..75e6362f87f 100644 --- a/src/server/database/Database/MySQLConnection.cpp +++ b/src/server/database/Database/MySQLConnection.cpp @@ -15,29 +15,43 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ - +#include "MySQLConnection.h" #include "Common.h" - -#ifdef _WIN32 - #include <winsock2.h> +#include "DatabaseWorker.h" +#include "Log.h" +#include "PreparedStatement.h" +#include "QueryResult.h" +#include "Timer.h" +#include "Transaction.h" +#include "Util.h" +#include <errmsg.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 <errmsg.h> +#include <mysqld_error.h> -#include "MySQLConnection.h" -#include "QueryResult.h" -#include "SQLOperation.h" -#include "PreparedStatement.h" -#include "DatabaseWorker.h" -#include "Timer.h" -#include "Log.h" -#include "ProducerConsumerQueue.h" +MySQLConnectionInfo::MySQLConnectionInfo(std::string const& infoString) +{ + Tokenizer tokens(infoString, ';'); + + if (tokens.size() != 5) + return; + + uint8 i = 0; + + host.assign(tokens[i++]); + port_or_socket.assign(tokens[i++]); + user.assign(tokens[i++]); + password.assign(tokens[i++]); + database.assign(tokens[i++]); +} MySQLConnection::MySQLConnection(MySQLConnectionInfo& connInfo) : m_reconnecting(false), m_prepareError(false), -m_queue(NULL), -m_Mysql(NULL), +m_queue(nullptr), +m_Mysql(nullptr), m_connectionInfo(connInfo), m_connectionFlags(CONNECTION_SYNCH) { } @@ -45,7 +59,7 @@ MySQLConnection::MySQLConnection(ProducerConsumerQueue<SQLOperation*>* queue, My m_reconnecting(false), m_prepareError(false), m_queue(queue), -m_Mysql(NULL), +m_Mysql(nullptr), m_connectionInfo(connInfo), m_connectionFlags(CONNECTION_ASYNC) { @@ -74,7 +88,7 @@ void MySQLConnection::Close() uint32 MySQLConnection::Open() { MYSQL *mysqlInit; - mysqlInit = mysql_init(NULL); + mysqlInit = mysql_init(nullptr); if (!mysqlInit) { TC_LOG_ERROR("sql.sql", "Could not initialize Mysql connection to database `%s`", m_connectionInfo.database.c_str()); @@ -152,7 +166,7 @@ bool MySQLConnection::PrepareStatements() return !m_prepareError; } -bool MySQLConnection::Execute(const char* sql) +bool MySQLConnection::Execute(char const* sql) { if (!m_Mysql) return false; @@ -281,18 +295,18 @@ bool MySQLConnection::_Query(PreparedStatement* stmt, MYSQL_RES **pResult, uint6 return true; } -ResultSet* MySQLConnection::Query(const char* sql) +ResultSet* MySQLConnection::Query(char const* sql) { if (!sql) - return NULL; + return nullptr; - MYSQL_RES *result = NULL; - MYSQL_FIELD *fields = NULL; + MYSQL_RES *result = nullptr; + MYSQL_FIELD *fields = nullptr; uint64 rowCount = 0; uint32 fieldCount = 0; if (!_Query(sql, &result, &fields, &rowCount, &fieldCount)) - return NULL; + return nullptr; return new ResultSet(result, fields, rowCount, fieldCount); } @@ -355,14 +369,13 @@ void MySQLConnection::CommitTransaction() int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction) { - std::list<SQLElementData> const& queries = transaction->m_queries; + std::vector<SQLElementData> const& queries = transaction->m_queries; if (queries.empty()) return -1; BeginTransaction(); - std::list<SQLElementData>::const_iterator itr; - for (itr = queries.begin(); itr != queries.end(); ++itr) + for (auto itr = queries.begin(); itr != queries.end(); ++itr) { SQLElementData const& data = *itr; switch (itr->type) @@ -382,7 +395,7 @@ int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction) break; case SQL_ELEMENT_RAW: { - const char* sql = data.element.query; + char const* sql = data.element.query; ASSERT(sql); if (!Execute(sql)) { @@ -405,6 +418,26 @@ int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction) return 0; } +void MySQLConnection::Ping() +{ + mysql_ping(m_Mysql); +} + +uint32 MySQLConnection::GetLastError() +{ + return mysql_errno(m_Mysql); +} + +bool MySQLConnection::LockIfReady() +{ + return m_Mutex.try_lock(); +} + +void MySQLConnection::Unlock() +{ + m_Mutex.unlock(); +} + MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index) { ASSERT(index < m_stmts.size()); @@ -450,12 +483,12 @@ void MySQLConnection::PrepareStatement(uint32 index, std::string const& sql, Con PreparedResultSet* MySQLConnection::Query(PreparedStatement* stmt) { - MYSQL_RES *result = NULL; + MYSQL_RES *result = nullptr; uint64 rowCount = 0; uint32 fieldCount = 0; if (!_Query(stmt, &result, &rowCount, &fieldCount)) - return NULL; + return nullptr; if (mysql_more_results(m_Mysql)) { diff --git a/src/server/database/Database/MySQLConnection.h b/src/server/database/Database/MySQLConnection.h index 81b57261899..e8f42d49b34 100644 --- a/src/server/database/Database/MySQLConnection.h +++ b/src/server/database/Database/MySQLConnection.h @@ -15,18 +15,23 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "DatabaseWorkerPool.h" -#include "Transaction.h" -#include "Util.h" -#include "ProducerConsumerQueue.h" - #ifndef _MYSQLCONNECTION_H #define _MYSQLCONNECTION_H +#include "Define.h" +#include "DatabaseEnvFwd.h" +#include <map> +#include <memory> +#include <mutex> +#include <string> +#include <vector> + +template <typename T> +class ProducerConsumerQueue; + class DatabaseWorker; -class PreparedStatement; class MySQLPreparedStatement; -class PingOperation; +class SQLOperation; enum ConnectionFlags { @@ -37,21 +42,7 @@ enum ConnectionFlags struct TC_DATABASE_API MySQLConnectionInfo { - explicit MySQLConnectionInfo(std::string const& infoString) - { - Tokenizer tokens(infoString, ';'); - - if (tokens.size() != 5) - return; - - uint8 i = 0; - - host.assign(tokens[i++]); - port_or_socket.assign(tokens[i++]); - user.assign(tokens[i++]); - password.assign(tokens[i++]); - database.assign(tokens[i++]); - } + explicit MySQLConnectionInfo(std::string const& infoString); std::string user; std::string password; @@ -76,36 +67,29 @@ class TC_DATABASE_API MySQLConnection bool PrepareStatements(); public: - bool Execute(const char* sql); + bool Execute(char const* sql); bool Execute(PreparedStatement* stmt); - ResultSet* Query(const char* sql); + ResultSet* Query(char const* sql); PreparedResultSet* Query(PreparedStatement* stmt); - bool _Query(const char *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, MYSQL_RES** pResult, MYSQL_FIELD** pFields, uint64* pRowCount, uint32* pFieldCount); + bool _Query(PreparedStatement* stmt, MYSQL_RES** pResult, uint64* pRowCount, uint32* pFieldCount); void BeginTransaction(); void RollbackTransaction(); void CommitTransaction(); int ExecuteTransaction(SQLTransaction& transaction); - operator bool () const { return m_Mysql != NULL; } - void Ping() { mysql_ping(m_Mysql); } + void Ping(); - uint32 GetLastError() { return mysql_errno(m_Mysql); } + uint32 GetLastError(); protected: - bool LockIfReady() - { - /// Tries to acquire lock. If lock is acquired by another thread - /// the calling parent will just try another connection - return m_Mutex.try_lock(); - } - - void Unlock() - { - /// Called by parent databasepool. Will let other threads access this connection - m_Mutex.unlock(); - } + /// Tries to acquire lock. If lock is acquired by another thread + /// the calling parent will just try another connection + bool LockIfReady(); + + /// Called by parent databasepool. Will let other threads access this connection + void Unlock(); MYSQL* GetHandle() { return m_Mysql; } MySQLPreparedStatement* GetPreparedStatement(uint32 index); diff --git a/src/server/database/Database/MySQLThreading.cpp b/src/server/database/Database/MySQLThreading.cpp new file mode 100644 index 00000000000..3baa7877fe8 --- /dev/null +++ b/src/server/database/Database/MySQLThreading.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008-2017 TrinityCore <http://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 "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> + +void MySQL::Library_Init() +{ + mysql_library_init(-1, nullptr, nullptr); +} + +void MySQL::Library_End() +{ + mysql_library_end(); +} diff --git a/src/server/database/Database/MySQLThreading.h b/src/server/database/Database/MySQLThreading.h index 68a8c3ea7ed..4f8030f6555 100644 --- a/src/server/database/Database/MySQLThreading.h +++ b/src/server/database/Database/MySQLThreading.h @@ -18,20 +18,12 @@ #ifndef _MYSQLTHREADING_H #define _MYSQLTHREADING_H -#include "Log.h" +#include "Define.h" -class TC_DATABASE_API MySQL +namespace MySQL { - public: - static void Library_Init() - { - mysql_library_init(-1, NULL, NULL); - } - - static void Library_End() - { - mysql_library_end(); - } -}; + void TC_DATABASE_API Library_Init(); + void TC_DATABASE_API Library_End(); +} #endif diff --git a/src/server/database/Database/PreparedStatement.cpp b/src/server/database/Database/PreparedStatement.cpp index a46a022df48..137e9c05096 100644 --- a/src/server/database/Database/PreparedStatement.cpp +++ b/src/server/database/Database/PreparedStatement.cpp @@ -16,8 +16,15 @@ */ #include "PreparedStatement.h" +#include "Errors.h" #include "MySQLConnection.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> PreparedStatement::PreparedStatement(uint32 index, uint8 capacity) : m_stmt(nullptr), m_index(index), statement_data(capacity) { } @@ -214,9 +221,9 @@ void MySQLPreparedStatement::ClearParameters() for (uint32 i=0; i < m_paramCount; ++i) { delete m_bind[i].length; - m_bind[i].length = NULL; + m_bind[i].length = nullptr; delete[] (char*) m_bind[i].buffer; - m_bind[i].buffer = NULL; + m_bind[i].buffer = nullptr; m_paramsSet[i] = false; } } @@ -227,6 +234,19 @@ static bool ParamenterIndexAssertFail(uint32 stmtIndex, uint8 index, uint32 para 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<char*>(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) { @@ -236,6 +256,20 @@ void MySQLPreparedStatement::AssertValidIndex(uint8 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 = 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); @@ -246,7 +280,7 @@ void MySQLPreparedStatement::setUInt8(const uint8 index, const uint8 value) AssertValidIndex(index); m_paramsSet[index] = true; MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_TINY, &value, sizeof(uint8), true); + SetParameterValue(param, MYSQL_TYPE_TINY, &value, sizeof(uint8), true); } void MySQLPreparedStatement::setUInt16(const uint8 index, const uint16 value) @@ -254,7 +288,7 @@ void MySQLPreparedStatement::setUInt16(const uint8 index, const uint16 value) AssertValidIndex(index); m_paramsSet[index] = true; MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_SHORT, &value, sizeof(uint16), true); + SetParameterValue(param, MYSQL_TYPE_SHORT, &value, sizeof(uint16), true); } void MySQLPreparedStatement::setUInt32(const uint8 index, const uint32 value) @@ -262,7 +296,7 @@ void MySQLPreparedStatement::setUInt32(const uint8 index, const uint32 value) AssertValidIndex(index); m_paramsSet[index] = true; MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_LONG, &value, sizeof(uint32), true); + SetParameterValue(param, MYSQL_TYPE_LONG, &value, sizeof(uint32), true); } void MySQLPreparedStatement::setUInt64(const uint8 index, const uint64 value) @@ -270,7 +304,7 @@ void MySQLPreparedStatement::setUInt64(const uint8 index, const uint64 value) AssertValidIndex(index); m_paramsSet[index] = true; MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(uint64), true); + SetParameterValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(uint64), true); } void MySQLPreparedStatement::setInt8(const uint8 index, const int8 value) @@ -278,7 +312,7 @@ void MySQLPreparedStatement::setInt8(const uint8 index, const int8 value) AssertValidIndex(index); m_paramsSet[index] = true; MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_TINY, &value, sizeof(int8), false); + SetParameterValue(param, MYSQL_TYPE_TINY, &value, sizeof(int8), false); } void MySQLPreparedStatement::setInt16(const uint8 index, const int16 value) @@ -286,7 +320,7 @@ void MySQLPreparedStatement::setInt16(const uint8 index, const int16 value) AssertValidIndex(index); m_paramsSet[index] = true; MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_SHORT, &value, sizeof(int16), false); + SetParameterValue(param, MYSQL_TYPE_SHORT, &value, sizeof(int16), false); } void MySQLPreparedStatement::setInt32(const uint8 index, const int32 value) @@ -294,7 +328,7 @@ void MySQLPreparedStatement::setInt32(const uint8 index, const int32 value) AssertValidIndex(index); m_paramsSet[index] = true; MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_LONG, &value, sizeof(int32), false); + SetParameterValue(param, MYSQL_TYPE_LONG, &value, sizeof(int32), false); } void MySQLPreparedStatement::setInt64(const uint8 index, const int64 value) @@ -302,7 +336,7 @@ void MySQLPreparedStatement::setInt64(const uint8 index, const int64 value) AssertValidIndex(index); m_paramsSet[index] = true; MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(int64), false); + SetParameterValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(int64), false); } void MySQLPreparedStatement::setFloat(const uint8 index, const float value) @@ -310,7 +344,7 @@ void MySQLPreparedStatement::setFloat(const uint8 index, const float value) AssertValidIndex(index); m_paramsSet[index] = true; MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_FLOAT, &value, sizeof(float), (value > 0.0f)); + SetParameterValue(param, MYSQL_TYPE_FLOAT, &value, sizeof(float), (value > 0.0f)); } void MySQLPreparedStatement::setDouble(const uint8 index, const double value) @@ -318,7 +352,7 @@ void MySQLPreparedStatement::setDouble(const uint8 index, const double value) AssertValidIndex(index); m_paramsSet[index] = true; MYSQL_BIND* param = &m_bind[index]; - setValue(param, MYSQL_TYPE_DOUBLE, &value, sizeof(double), (value > 0.0f)); + SetParameterValue(param, MYSQL_TYPE_DOUBLE, &value, sizeof(double), (value > 0.0f)); } void MySQLPreparedStatement::setBinary(const uint8 index, const std::vector<uint8>& value, bool isString) @@ -328,7 +362,7 @@ void MySQLPreparedStatement::setBinary(const uint8 index, const std::vector<uint MYSQL_BIND* param = &m_bind[index]; uint32 len = uint32(value.size()); param->buffer_type = MYSQL_TYPE_BLOB; - delete [] static_cast<char *>(param->buffer); + delete [] static_cast<char*>(param->buffer); param->buffer = new char[len]; param->buffer_length = len; param->is_null_value = 0; @@ -343,33 +377,6 @@ void MySQLPreparedStatement::setBinary(const uint8 index, const std::vector<uint memcpy(param->buffer, value.data(), len); } -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::setValue(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); -} - std::string MySQLPreparedStatement::getQueryString() const { std::string queryString(m_queryString); @@ -458,7 +465,7 @@ bool PreparedStatementTask::Execute() if (!result || !result->GetRowCount()) { delete result; - m_result->set_value(PreparedQueryResult(NULL)); + m_result->set_value(PreparedQueryResult(nullptr)); return false; } m_result->set_value(PreparedQueryResult(result)); diff --git a/src/server/database/Database/PreparedStatement.h b/src/server/database/Database/PreparedStatement.h index 33ef04287cd..7d13e81c395 100644 --- a/src/server/database/Database/PreparedStatement.h +++ b/src/server/database/Database/PreparedStatement.h @@ -18,8 +18,10 @@ #ifndef _PREPAREDSTATEMENT_H #define _PREPAREDSTATEMENT_H -#include <future> +#include "Define.h" #include "SQLOperation.h" +#include <future> +#include <vector> #ifdef __APPLE__ #undef TYPE_BOOL @@ -81,6 +83,7 @@ class TC_DATABASE_API PreparedStatement PreparedStatement(uint32 index, uint8 capacity); ~PreparedStatement(); + 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); @@ -94,7 +97,6 @@ class TC_DATABASE_API PreparedStatement void setDouble(const uint8 index, const double value); void setString(const uint8 index, const std::string& value); void setBinary(const uint8 index, const std::vector<uint8>& value); - void setNull(const uint8 index); protected: void BindParameters(MySQLPreparedStatement* stmt); @@ -147,9 +149,6 @@ class TC_DATABASE_API MySQLPreparedStatement std::string getQueryString() const; private: - void setValue(MYSQL_BIND* param, enum_field_types type, const void* value, uint32 len, bool isUnsigned); - - private: MYSQL_STMT* m_Mstmt; uint32 m_paramCount; std::vector<bool> m_paramsSet; @@ -160,9 +159,6 @@ class TC_DATABASE_API MySQLPreparedStatement MySQLPreparedStatement& operator=(MySQLPreparedStatement const& right) = delete; }; -typedef std::future<PreparedQueryResult> PreparedQueryResultFuture; -typedef std::promise<PreparedQueryResult> PreparedQueryResultPromise; - //- Lower-level class, enqueuable operation class TC_DATABASE_API PreparedStatementTask : public SQLOperation { diff --git a/src/server/database/Database/QueryCallback.cpp b/src/server/database/Database/QueryCallback.cpp index 2d89e08a956..18f72975935 100644 --- a/src/server/database/Database/QueryCallback.cpp +++ b/src/server/database/Database/QueryCallback.cpp @@ -16,6 +16,7 @@ */ #include "QueryCallback.h" +#include "Errors.h" template<typename T, typename... Args> inline void Construct(T& t, Args&&... args) @@ -30,7 +31,7 @@ inline void Destroy(T& t) } template<typename T> -void ConstructActiveMember(T* obj) +inline void ConstructActiveMember(T* obj) { if (!obj->_isPrepared) Construct(obj->_string); @@ -39,7 +40,7 @@ void ConstructActiveMember(T* obj) } template<typename T> -void DestroyActiveMember(T* obj) +inline void DestroyActiveMember(T* obj) { if (!obj->_isPrepared) Destroy(obj->_string); @@ -48,7 +49,7 @@ void DestroyActiveMember(T* obj) } template<typename T> -void MoveFrom(T* to, T&& from) +inline void MoveFrom(T* to, T&& from) { ASSERT(to->_isPrepared == from._isPrepared); @@ -199,7 +200,7 @@ QueryCallback::Status QueryCallback::InvokeIfReady() { if (_string.valid() && _string.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - std::future<QueryResult> f(std::move(_string)); + QueryResultFuture f(std::move(_string)); std::function<void(QueryCallback&, QueryResult)> cb(std::move(callback._string)); cb(*this, f.get()); return checkStateAndReturnCompletion(); @@ -209,7 +210,7 @@ QueryCallback::Status QueryCallback::InvokeIfReady() { if (_prepared.valid() && _prepared.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - std::future<PreparedQueryResult> f(std::move(_prepared)); + PreparedQueryResultFuture f(std::move(_prepared)); std::function<void(QueryCallback&, PreparedQueryResult)> cb(std::move(callback._prepared)); cb(*this, f.get()); return checkStateAndReturnCompletion(); diff --git a/src/server/database/Database/QueryCallback.h b/src/server/database/Database/QueryCallback.h index f9c93000da7..ebc1538b5e9 100644 --- a/src/server/database/Database/QueryCallback.h +++ b/src/server/database/Database/QueryCallback.h @@ -18,14 +18,19 @@ #ifndef _QUERY_CALLBACK_H #define _QUERY_CALLBACK_H -#include "QueryResult.h" +#include "Define.h" +#include "DatabaseEnvFwd.h" +#include <functional> #include <future> +#include <list> +#include <queue> +#include <utility> class TC_DATABASE_API QueryCallback { public: - explicit QueryCallback(std::future<QueryResult>&& result); - explicit QueryCallback(std::future<PreparedQueryResult>&& result); + explicit QueryCallback(QueryResultFuture&& result); + explicit QueryCallback(PreparedQueryResultFuture&& result); QueryCallback(QueryCallback&& right); QueryCallback& operator=(QueryCallback&& right); ~QueryCallback(); @@ -58,8 +63,8 @@ private: union { - std::future<QueryResult> _string; - std::future<PreparedQueryResult> _prepared; + QueryResultFuture _string; + PreparedQueryResultFuture _prepared; }; bool _isPrepared; diff --git a/src/server/database/Database/QueryHolder.cpp b/src/server/database/Database/QueryHolder.cpp index 6a8bb701a1f..7143671db75 100644 --- a/src/server/database/Database/QueryHolder.cpp +++ b/src/server/database/Database/QueryHolder.cpp @@ -19,26 +19,7 @@ #include "QueryHolder.h" #include "PreparedStatement.h" #include "Log.h" - -bool SQLQueryHolder::SetQuery(size_t index, const char *sql) -{ - if (m_queries.size() <= index) - { - TC_LOG_ERROR("sql.sql", "Query index (%u) out of range (size: %u) for query: %s", uint32(index), (uint32)m_queries.size(), sql); - return false; - } - - /// not executed yet, just stored (it's not called a holder for nothing) - SQLElementData element; - element.type = SQL_ELEMENT_RAW; - element.element.query = strdup(sql); - - SQLResultSetUnion result; - result.qresult = NULL; - - m_queries[index] = SQLResultPair(element, result); - return true; -} +#include "QueryResult.h" bool SQLQueryHolder::SetPreparedQuery(size_t index, PreparedStatement* stmt) { @@ -48,59 +29,17 @@ bool SQLQueryHolder::SetPreparedQuery(size_t index, PreparedStatement* stmt) return false; } - /// not executed yet, just stored (it's not called a holder for nothing) - SQLElementData element; - element.type = SQL_ELEMENT_PREPARED; - element.element.stmt = stmt; - - SQLResultSetUnion result; - result.presult = NULL; - - m_queries[index] = SQLResultPair(element, result); + m_queries[index].first = stmt; return true; } -QueryResult SQLQueryHolder::GetResult(size_t index) -{ - // Don't call to this function if the index is of an ad-hoc statement - if (index < m_queries.size()) - { - ResultSet* result = m_queries[index].second.qresult; - if (!result || !result->GetRowCount() || !result->NextRow()) - return QueryResult(NULL); - - return QueryResult(result); - } - else - return QueryResult(NULL); -} - PreparedQueryResult SQLQueryHolder::GetPreparedResult(size_t index) { // Don't call to this function if the index is of a prepared statement if (index < m_queries.size()) - { - PreparedResultSet* result = m_queries[index].second.presult; - if (!result || !result->GetRowCount()) - return PreparedQueryResult(NULL); - - return PreparedQueryResult(result); - } + return m_queries[index].second; else - return PreparedQueryResult(NULL); -} - -void SQLQueryHolder::SetResult(size_t index, ResultSet* result) -{ - if (result && !result->GetRowCount()) - { - delete result; - result = NULL; - } - - /// store the result in the holder - if (index < m_queries.size()) - m_queries[index].second.qresult = result; + return PreparedQueryResult(nullptr); } void SQLQueryHolder::SetPreparedResult(size_t index, PreparedResultSet* result) @@ -108,12 +47,12 @@ void SQLQueryHolder::SetPreparedResult(size_t index, PreparedResultSet* result) if (result && !result->GetRowCount()) { delete result; - result = NULL; + result = nullptr; } /// store the result in the holder if (index < m_queries.size()) - m_queries[index].second.presult = result; + m_queries[index].second = PreparedQueryResult(result); } SQLQueryHolder::~SQLQueryHolder() @@ -122,18 +61,7 @@ SQLQueryHolder::~SQLQueryHolder() { /// if the result was never used, free the resources /// results used already (getresult called) are expected to be deleted - if (SQLElementData* data = &m_queries[i].first) - { - switch (data->type) - { - case SQL_ELEMENT_RAW: - free((void*)(const_cast<char*>(data->element.query))); - break; - case SQL_ELEMENT_PREPARED: - delete data->element.stmt; - break; - } - } + delete m_queries[i].first; } } @@ -156,33 +84,10 @@ bool SQLQueryHolderTask::Execute() if (!m_holder) return false; - /// we can do this, we are friends - std::vector<SQLQueryHolder::SQLResultPair> &queries = m_holder->m_queries; - - for (size_t i = 0; i < queries.size(); i++) - { - /// execute all queries in the holder and pass the results - if (SQLElementData* data = &queries[i].first) - { - switch (data->type) - { - case SQL_ELEMENT_RAW: - { - char const* sql = data->element.query; - if (sql) - m_holder->SetResult(i, m_conn->Query(sql)); - break; - } - case SQL_ELEMENT_PREPARED: - { - PreparedStatement* stmt = data->element.stmt; - if (stmt) - m_holder->SetPreparedResult(i, m_conn->Query(stmt)); - break; - } - } - } - } + /// execute all queries in the holder and pass the results + for (size_t i = 0; i < m_holder->m_queries.size(); ++i) + if (PreparedStatement* stmt = m_holder->m_queries[i].first) + m_holder->SetPreparedResult(i, m_conn->Query(stmt)); m_result.set_value(m_holder); return true; diff --git a/src/server/database/Database/QueryHolder.h b/src/server/database/Database/QueryHolder.h index c3ebb33068c..123765b01a5 100644 --- a/src/server/database/Database/QueryHolder.h +++ b/src/server/database/Database/QueryHolder.h @@ -18,34 +18,22 @@ #ifndef _QUERYHOLDER_H #define _QUERYHOLDER_H -#include <future> +#include "SQLOperation.h" class TC_DATABASE_API SQLQueryHolder { friend class SQLQueryHolderTask; private: - typedef std::pair<SQLElementData, SQLResultSetUnion> SQLResultPair; - std::vector<SQLResultPair> m_queries; + std::vector<std::pair<PreparedStatement*, PreparedQueryResult>> m_queries; public: SQLQueryHolder() { } virtual ~SQLQueryHolder(); - bool SetQuery(size_t index, const char* sql); - template<typename Format, typename... Args> - bool SetPQuery(size_t index, Format&& sql, Args&&... args) - { - return SetQuery(index, Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str()); - } bool SetPreparedQuery(size_t index, PreparedStatement* stmt); void SetSize(size_t size); - QueryResult GetResult(size_t index); PreparedQueryResult GetPreparedResult(size_t index); - void SetResult(size_t index, ResultSet* result); void SetPreparedResult(size_t index, PreparedResultSet* result); }; -typedef std::future<SQLQueryHolder*> QueryResultHolderFuture; -typedef std::promise<SQLQueryHolder*> QueryResultHolderPromise; - class TC_DATABASE_API SQLQueryHolderTask : public SQLOperation { private: diff --git a/src/server/database/Database/QueryResult.cpp b/src/server/database/Database/QueryResult.cpp index 7b6937e50b7..c2c05730ff1 100644 --- a/src/server/database/Database/QueryResult.cpp +++ b/src/server/database/Database/QueryResult.cpp @@ -16,8 +16,108 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "DatabaseEnv.h" +#include "QueryResult.h" +#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> + +static uint32 SizeForType(MYSQL_FIELD* field) +{ + switch (field->type) + { + case MYSQL_TYPE_NULL: + return 0; + case MYSQL_TYPE_TINY: + return 1; + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_SHORT: + return 2; + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_FLOAT: + return 4; + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_BIT: + return 8; + + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + return sizeof(MYSQL_TIME); + + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + return field->max_length + 1; + + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return 64; + + case MYSQL_TYPE_GEOMETRY: + /* + Following types are not sent over the wire: + MYSQL_TYPE_ENUM: + MYSQL_TYPE_SET: + */ + default: + TC_LOG_WARN("sql.sql", "SQL::SizeForType(): invalid field type %u", uint32(field->type)); + return 0; + } +} + +DatabaseFieldTypes MysqlTypeToFieldType(enum_field_types type) +{ + switch (type) + { + case MYSQL_TYPE_NULL: + return DatabaseFieldTypes::Null; + case MYSQL_TYPE_TINY: + return DatabaseFieldTypes::Int8; + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_SHORT: + return DatabaseFieldTypes::Int16; + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + return DatabaseFieldTypes::Int32; + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_BIT: + return DatabaseFieldTypes::Int64; + case MYSQL_TYPE_FLOAT: + return DatabaseFieldTypes::Float; + case MYSQL_TYPE_DOUBLE: + return DatabaseFieldTypes::Double; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return DatabaseFieldTypes::Decimal; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + return DatabaseFieldTypes::Date; + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + return DatabaseFieldTypes::Binary; + default: + TC_LOG_WARN("sql.sql", "MysqlTypeToFieldType(): invalid field type %u", uint32(type)); + break; + } + + return DatabaseFieldTypes::Null; +} ResultSet::ResultSet(MYSQL_RES *result, MYSQL_FIELD *fields, uint64 rowCount, uint32 fieldCount) : _rowCount(rowCount), @@ -36,11 +136,9 @@ PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES *result, uint64 m_rowCount(rowCount), m_rowPosition(0), m_fieldCount(fieldCount), -m_rBind(NULL), +m_rBind(nullptr), m_stmt(stmt), -m_metadataResult(result), -m_isNull(NULL), -m_length(NULL) +m_metadataResult(result) { if (!m_metadataResult) return; @@ -52,8 +150,12 @@ m_length(NULL) } m_rBind = new MYSQL_BIND[m_fieldCount]; - m_isNull = new my_bool[m_fieldCount]; - m_length = new unsigned long[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]; + 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); @@ -76,14 +178,14 @@ m_length(NULL) std::size_t rowSize = 0; for (uint32 i = 0; i < m_fieldCount; ++i) { - uint32 size = Field::SizeForType(&field[i]); + uint32 size = SizeForType(&field[i]); rowSize += size; m_rBind[i].buffer_type = field[i].type; m_rBind[i].buffer_length = size; m_rBind[i].length = &m_length[i]; m_rBind[i].is_null = &m_isNull[i]; - m_rBind[i].error = NULL; + m_rBind[i].error = nullptr; m_rBind[i].is_unsigned = field[i].flags & UNSIGNED_FLAG; } @@ -137,7 +239,7 @@ m_length(NULL) m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue( buffer, - m_rBind[fIndex].buffer_type, + MysqlTypeToFieldType(m_rBind[fIndex].buffer_type), fetched_length); // move buffer pointer to next part @@ -147,7 +249,7 @@ m_length(NULL) { m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue( nullptr, - m_rBind[fIndex].buffer_type, + MysqlTypeToFieldType(m_rBind[fIndex].buffer_type), *m_rBind[fIndex].length); } @@ -196,7 +298,7 @@ bool ResultSet::NextRow() } for (uint32 i = 0; i < _fieldCount; i++) - _currentRow[i].SetStructuredValue(row[i], _fields[i].type, lengths[i]); + _currentRow[i].SetStructuredValue(row[i], MysqlTypeToFieldType(_fields[i].type), lengths[i]); return true; } @@ -227,16 +329,35 @@ void ResultSet::CleanUp() if (_currentRow) { delete [] _currentRow; - _currentRow = NULL; + _currentRow = nullptr; } if (_result) { mysql_free_result(_result); - _result = NULL; + _result = nullptr; } } +Field const& ResultSet::operator[](std::size_t index) const +{ + ASSERT(index < _fieldCount); + return _currentRow[index]; +} + +Field* PreparedResultSet::Fetch() const +{ + ASSERT(m_rowPosition < m_rowCount); + return const_cast<Field*>(&m_rows[uint32(m_rowPosition) * m_fieldCount]); +} + +Field const& PreparedResultSet::operator[](std::size_t index) const +{ + ASSERT(m_rowPosition < m_rowCount); + ASSERT(index < m_fieldCount); + return m_rows[uint32(m_rowPosition) * m_fieldCount + index]; +} + void PreparedResultSet::CleanUp() { if (m_metadataResult) diff --git a/src/server/database/Database/QueryResult.h b/src/server/database/Database/QueryResult.h index 2df5d3a4f70..dcfe18765fc 100644 --- a/src/server/database/Database/QueryResult.h +++ b/src/server/database/Database/QueryResult.h @@ -19,13 +19,9 @@ #ifndef QUERYRESULT_H #define QUERYRESULT_H -#include <memory> -#include "Field.h" - -#ifdef _WIN32 - #include <winsock2.h> -#endif -#include <mysql.h> +#include "Define.h" +#include "DatabaseEnvFwd.h" +#include <vector> class TC_DATABASE_API ResultSet { @@ -38,11 +34,7 @@ class TC_DATABASE_API ResultSet uint32 GetFieldCount() const { return _fieldCount; } Field* Fetch() const { return _currentRow; } - const Field & operator [] (uint32 index) const - { - ASSERT(index < _fieldCount); - return _currentRow[index]; - } + Field const& operator[](std::size_t index) const; protected: uint64 _rowCount; @@ -58,8 +50,6 @@ class TC_DATABASE_API ResultSet ResultSet& operator=(ResultSet const& right) = delete; }; -typedef std::shared_ptr<ResultSet> QueryResult; - class TC_DATABASE_API PreparedResultSet { public: @@ -70,18 +60,8 @@ class TC_DATABASE_API PreparedResultSet uint64 GetRowCount() const { return m_rowCount; } uint32 GetFieldCount() const { return m_fieldCount; } - Field* Fetch() const - { - ASSERT(m_rowPosition < m_rowCount); - return const_cast<Field*>(&m_rows[uint32(m_rowPosition) * m_fieldCount]); - } - - Field const& operator[](uint32 index) const - { - ASSERT(m_rowPosition < m_rowCount); - ASSERT(index < m_fieldCount); - return m_rows[uint32(m_rowPosition) * m_fieldCount + index]; - } + Field* Fetch() const; + Field const& operator[](std::size_t index) const; protected: std::vector<Field> m_rows; @@ -94,9 +74,6 @@ class TC_DATABASE_API PreparedResultSet MYSQL_STMT* m_stmt; MYSQL_RES* m_metadataResult; ///< Field metadata, returned by mysql_stmt_result_metadata - my_bool* m_isNull; - unsigned long* m_length; - void CleanUp(); bool _NextRow(); @@ -104,7 +81,4 @@ class TC_DATABASE_API PreparedResultSet PreparedResultSet& operator=(PreparedResultSet const& right) = delete; }; -typedef std::shared_ptr<PreparedResultSet> PreparedQueryResult; - #endif - diff --git a/src/server/database/Database/SQLOperation.h b/src/server/database/Database/SQLOperation.h index 1e3000600c5..4e7e4c8eba5 100644 --- a/src/server/database/Database/SQLOperation.h +++ b/src/server/database/Database/SQLOperation.h @@ -18,16 +18,14 @@ #ifndef _SQLOPERATION_H #define _SQLOPERATION_H -#include "QueryResult.h" - -//- Forward declare (don't include header to prevent circular includes) -class PreparedStatement; +#include "Define.h" +#include "DatabaseEnvFwd.h" //- Union that holds element data union SQLElementUnion { PreparedStatement* stmt; - const char* query; + char const* query; }; //- Type specifier of our element data @@ -56,7 +54,7 @@ class MySQLConnection; class TC_DATABASE_API SQLOperation { public: - SQLOperation(): m_conn(NULL) { } + SQLOperation(): m_conn(nullptr) { } virtual ~SQLOperation() { } virtual int call() diff --git a/src/server/database/Database/Transaction.cpp b/src/server/database/Database/Transaction.cpp index 52f7062c78a..740e0455881 100644 --- a/src/server/database/Database/Transaction.cpp +++ b/src/server/database/Database/Transaction.cpp @@ -15,14 +15,15 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "DatabaseEnv.h" #include "Transaction.h" +#include "MySQLConnection.h" +#include "PreparedStatement.h" #include <mysqld_error.h> std::mutex TransactionTask::_deadlockLock; //- Append a raw ad-hoc query to the transaction -void Transaction::Append(const char* sql) +void Transaction::Append(char const* sql) { SQLElementData data; data.type = SQL_ELEMENT_RAW; @@ -45,9 +46,8 @@ void Transaction::Cleanup() if (_cleanedUp) return; - while (!m_queries.empty()) + for (SQLElementData const& data : m_queries) { - SQLElementData const &data = m_queries.front(); switch (data.type) { case SQL_ELEMENT_PREPARED: @@ -57,10 +57,9 @@ void Transaction::Cleanup() free((void*)(data.element.query)); break; } - - m_queries.pop_front(); } + m_queries.clear(); _cleanedUp = true; } diff --git a/src/server/database/Database/Transaction.h b/src/server/database/Database/Transaction.h index 6e6d68302f5..33e19531889 100644 --- a/src/server/database/Database/Transaction.h +++ b/src/server/database/Database/Transaction.h @@ -18,11 +18,12 @@ #ifndef _TRANSACTION_H #define _TRANSACTION_H +#include "Define.h" +#include "DatabaseEnvFwd.h" #include "SQLOperation.h" #include "StringFormat.h" - -//- Forward declare (don't include header to prevent circular includes) -class PreparedStatement; +#include <mutex> +#include <vector> /*! Transactions, high level class. */ class TC_DATABASE_API Transaction @@ -38,24 +39,23 @@ class TC_DATABASE_API Transaction ~Transaction() { Cleanup(); } void Append(PreparedStatement* statement); - void Append(const char* sql); + void Append(char const* sql); template<typename Format, typename... Args> void PAppend(Format&& sql, Args&&... args) { Append(Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str()); } - size_t GetSize() const { return m_queries.size(); } + std::size_t GetSize() const { return m_queries.size(); } protected: void Cleanup(); - std::list<SQLElementData> m_queries; + std::vector<SQLElementData> m_queries; private: bool _cleanedUp; }; -typedef std::shared_ptr<Transaction> SQLTransaction; /*! Low level class*/ class TC_DATABASE_API TransactionTask : public SQLOperation |