Big update.

This commit is contained in:
UltraNix
2022-03-12 22:28:00 +01:00
parent 6006eeeb01
commit 12d41d1314
2064 changed files with 427245 additions and 268481 deletions

View File

@@ -19,23 +19,21 @@
#include "Errors.h"
#include "MySQLConnection.h"
#include "QueryResult.h"
#include <cstdlib>
#include <cstring>
/*! Basic, ad-hoc queries. */
BasicStatementTask::BasicStatementTask(char const* sql, bool async) :
m_result(nullptr)
BasicStatementTask::BasicStatementTask(std::string_view sql, bool async) : m_result(nullptr)
{
m_sql = strdup(sql);
m_sql = std::string(sql);
m_has_result = async; // If the operation is async, then there's a result
if (async)
m_result = new QueryResultPromise();
}
BasicStatementTask::~BasicStatementTask()
{
free((void*)m_sql);
if (m_has_result && m_result != nullptr)
m_sql.clear();
if (m_has_result && m_result)
delete m_result;
}

View File

@@ -26,14 +26,14 @@
class AC_DATABASE_API BasicStatementTask : public SQLOperation
{
public:
BasicStatementTask(char const* sql, bool async = false);
BasicStatementTask(std::string_view sql, bool async = false);
~BasicStatementTask();
bool Execute() override;
QueryResultFuture GetFuture() const { return m_result->get_future(); }
private:
char const* m_sql; //- Raw query to be executed
std::string m_sql; //- Raw query to be executed
bool m_has_result;
QueryResultPromise* m_result;
};

View File

@@ -21,6 +21,6 @@ DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabase;
DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabase;
DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabase;
#ifdef PLAYERBOTS
DatabaseWorkerPool<PlayerbotDatabaseConnection> PlayerbotDatabase;
#ifdef MOD_PLAYERBOTS
DatabaseWorkerPool<PlayerbotsDatabaseConnection> PlayerbotsDatabase;
#endif

View File

@@ -25,8 +25,8 @@
#include "Implementation/LoginDatabase.h"
#include "Implementation/WorldDatabase.h"
#ifdef PLAYERBOTS
#include "PlayerbotDatabase.h"
#ifdef MOD_PLAYERBOTS
#include "Implementation/PlayerbotsDatabase.h"
#endif
#include "Field.h"
@@ -42,9 +42,9 @@ AC_DATABASE_API extern DatabaseWorkerPool<CharacterDatabaseConnection> Character
/// Accessor to the realm/login database
AC_DATABASE_API extern DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabase;
#ifdef PLAYERBOTS
/// Accessor to the playerbot database
AC_DATABASE_API extern DatabaseWorkerPool<PlayerbotDatabaseConnection> PlayerbotDatabase;
#ifdef MOD_PLAYERBOTS
/// Accessor to the playerbots database
AC_DATABASE_API extern DatabaseWorkerPool<PlayerbotsDatabaseConnection> PlayerbotsDatabase;
#endif
#endif

View File

@@ -33,8 +33,8 @@ class CharacterDatabaseConnection;
class LoginDatabaseConnection;
class WorldDatabaseConnection;
#ifdef PLAYERBOTS
class PlayerbotDatabaseConnection;
#ifdef MOD_PLAYERBOTS
class PlayerbotsDatabaseConnection;
#endif
class PreparedStatementBase;
@@ -46,8 +46,8 @@ using CharacterDatabasePreparedStatement = PreparedStatement<CharacterDatabaseCo
using LoginDatabasePreparedStatement = PreparedStatement<LoginDatabaseConnection>;
using WorldDatabasePreparedStatement = PreparedStatement<WorldDatabaseConnection>;
#ifdef PLAYERBOTS
using PlayerbotDatabasePreparedStatement = PreparedStatement<PlayerbotDatabaseConnection>;
#ifdef MOD_PLAYERBOTS
using PlayerbotsDatabasePreparedStatement = PreparedStatement<PlayerbotsDatabaseConnection>;
#endif
class PreparedResultSet;
@@ -79,8 +79,8 @@ using CharacterDatabaseTransaction = SQLTransaction<CharacterDatabaseConnection>
using LoginDatabaseTransaction = SQLTransaction<LoginDatabaseConnection>;
using WorldDatabaseTransaction = SQLTransaction<WorldDatabaseConnection>;
#ifdef PLAYERBOTS
using PlayerbotDatabaseTransaction = SQLTransaction<PlayerbotDatabaseConnection>;
#ifdef MOD_PLAYERBOTS
using PlayerbotsDatabaseTransaction = SQLTransaction<PlayerbotsDatabaseConnection>;
#endif
class SQLQueryHolderBase;
@@ -94,8 +94,8 @@ using CharacterDatabaseQueryHolder = SQLQueryHolder<CharacterDatabaseConnection>
using LoginDatabaseQueryHolder = SQLQueryHolder<LoginDatabaseConnection>;
using WorldDatabaseQueryHolder = SQLQueryHolder<WorldDatabaseConnection>;
#ifdef PLAYERBOTS
using PlayerbotDatabaseQueryHolder = SQLQueryHolder<PlayerbotDatabaseConnection>;
#ifdef MOD_PLAYERBOTS
using PlayerbotsDatabaseQueryHolder = SQLQueryHolder<PlayerbotsDatabaseConnection>;
#endif
class SQLQueryHolderCallback;

View File

@@ -41,15 +41,15 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
std::string const dbString = sConfigMgr->GetOption<std::string>(name + "DatabaseInfo", "");
if (dbString.empty())
{
LOG_ERROR(_logger, "Database %s not specified in configuration file!", name.c_str());
LOG_ERROR(_logger, "Database {} not specified in configuration file!", name);
return false;
}
uint8 const asyncThreads = sConfigMgr->GetOption<uint8>(name + "Database.WorkerThreads", 1);
if (asyncThreads < 1 || asyncThreads > 32)
{
LOG_ERROR(_logger, "%s database: invalid number of worker threads specified. "
"Please pick a value between 1 and 32.", name.c_str());
LOG_ERROR(_logger, "{} database: invalid number of worker threads specified. "
"Please pick a value between 1 and 32.", name);
return false;
}
@@ -68,7 +68,7 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
while (reconnectCount < attempts)
{
LOG_INFO(_logger, "> Retrying after %u seconds", static_cast<uint32>(reconnectSeconds.count()));
LOG_WARN(_logger, "> Retrying after {} seconds", static_cast<uint32>(reconnectSeconds.count()));
std::this_thread::sleep_for(reconnectSeconds);
error = pool.Open();
@@ -96,8 +96,8 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
// If the error wasn't handled quit
if (error)
{
LOG_ERROR(_logger, "DatabasePool %s NOT opened. There were errors opening the MySQL connections. "
"Check your log file for specific errors", name.c_str());
LOG_ERROR(_logger, "DatabasePool {} NOT opened. There were errors opening the MySQL connections. "
"Check your log file for specific errors", name);
return false;
}
@@ -118,7 +118,7 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
{
if (!DBUpdater<T>::Populate(pool))
{
LOG_ERROR(_logger, "Could not populate the %s database, see log for details.", name.c_str());
LOG_ERROR(_logger, "Could not populate the {} database, see log for details.", name);
return false;
}
@@ -129,7 +129,7 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
{
if (!DBUpdater<T>::Update(pool, _modulesList))
{
LOG_ERROR(_logger, "Could not update the %s database, see log for details.", name.c_str());
LOG_ERROR(_logger, "Could not update the {} database, see log for details.", name);
return false;
}
@@ -141,7 +141,7 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
{
if (!pool.PrepareStatements())
{
LOG_ERROR(_logger, "Could not prepare statements of the %s database, see log for details.", name.c_str());
LOG_ERROR(_logger, "Could not prepare statements of the {} database, see log for details.", name);
return false;
}
@@ -153,7 +153,22 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
bool DatabaseLoader::Load()
{
return OpenDatabases() && PopulateDatabases() && UpdateDatabases() && PrepareStatements();
if (!_updateFlags)
LOG_INFO("sql.updates", "Automatic database updates are disabled for all databases!");
if (!OpenDatabases())
return false;
if (!PopulateDatabases())
return false;
if (!UpdateDatabases())
return false;
if (!PrepareStatements())
return false;
return true;
}
bool DatabaseLoader::OpenDatabases()
@@ -205,7 +220,7 @@ DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(Databas
template AC_DATABASE_API
DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(DatabaseWorkerPool<WorldDatabaseConnection>&, std::string const&);
#ifdef PLAYERBOTS
#ifdef MOD_PLAYERBOTS
template AC_DATABASE_API
DatabaseLoader& DatabaseLoader::AddDatabase<PlayerbotDatabaseConnection>(DatabaseWorkerPool<PlayerbotDatabaseConnection>&, std::string const&);
DatabaseLoader& DatabaseLoader::AddDatabase<PlayerbotsDatabaseConnection>(DatabaseWorkerPool<PlayerbotsDatabaseConnection>&, std::string const&);
#endif

View File

@@ -48,10 +48,9 @@ public:
DATABASE_LOGIN = 1,
DATABASE_CHARACTER = 2,
DATABASE_WORLD = 4,
#ifdef PLAYERBOTS
DATABASE_PLAYERBOT = 8,
DATABASE_MASK_ALL = DATABASE_LOGIN | DATABASE_CHARACTER | DATABASE_WORLD | DATABASE_PLAYERBOT
#ifdef MOD_PLAYERBOTS
DATABASE_PLAYERBOTS = 8,
DATABASE_MASK_ALL = DATABASE_LOGIN | DATABASE_CHARACTER | DATABASE_WORLD | DATABASE_PLAYERBOTS
#else
DATABASE_MASK_ALL = DATABASE_LOGIN | DATABASE_CHARACTER | DATABASE_WORLD
#endif

View File

@@ -17,12 +17,10 @@
#include "DatabaseWorkerPool.h"
#include "AdhocStatement.h"
#include "Common.h"
#include "CharacterDatabase.h"
#include "Errors.h"
#include "Implementation/CharacterDatabase.h"
#include "Implementation/LoginDatabase.h"
#include "Implementation/WorldDatabase.h"
#include "Log.h"
#include "LoginDatabase.h"
#include "MySQLPreparedStatement.h"
#include "MySQLWorkaround.h"
#include "PCQueue.h"
@@ -32,19 +30,21 @@
#include "QueryResult.h"
#include "SQLOperation.h"
#include "Transaction.h"
#include "WorldDatabase.h"
#include <mysqld_error.h>
#include <limits>
#ifdef ACORE_DEBUG
#include <boost/stacktrace.hpp>
#include <sstream>
#endif
#ifdef PLAYERBOTS
#include "PlayerbotDatabase.h"
#ifdef MOD_PLAYERBOTS
#include "Implementation/PlayerbotsDatabase.h"
#endif
#if MARIADB_VERSION_ID >= 100600
#define MIN_MYSQL_SERVER_VERSION 100200u
#define MIN_MYSQL_SERVER_VERSION 100500u
#define MIN_MYSQL_CLIENT_VERSION 30203u
#else
#define MIN_MYSQL_SERVER_VERSION 50700u
@@ -62,9 +62,10 @@ class PingOperation : public SQLOperation
};
template <class T>
DatabaseWorkerPool<T>::DatabaseWorkerPool()
: _queue(new ProducerConsumerQueue<SQLOperation*>()),
_async_threads(0), _synch_threads(0)
DatabaseWorkerPool<T>::DatabaseWorkerPool() :
_queue(new ProducerConsumerQueue<SQLOperation*>()),
_async_threads(0),
_synch_threads(0)
{
WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
@@ -76,8 +77,8 @@ DatabaseWorkerPool<T>::DatabaseWorkerPool()
bool isSameClientDB = true; // Client version 3.2.3?
#endif
WPFatal(isSupportClientDB, "AzerothCore does not support MySQL versions below 5.7 and MariaDB 10.2\nSearch the wiki for ACE00043 in Common Errors (https://www.azerothcore.org/wiki/common-errors).");
WPFatal(isSameClientDB, "Used MySQL library version (%s id %lu) does not match the version id used to compile AzerothCore (id %u).\nSearch the wiki for ACE00046 in Common Errors (https://www.azerothcore.org/wiki/common-errors).",
WPFatal(isSupportClientDB, "AzerothCore does not support MySQL versions below 5.7 and MariaDB 10.5\nSearch the wiki for ACE00043 in Common Errors (https://www.azerothcore.org/wiki/common-errors).");
WPFatal(isSameClientDB, "Used MySQL library version ({} id {}) does not match the version id used to compile AzerothCore (id {}).\nSearch the wiki for ACE00046 in Common Errors (https://www.azerothcore.org/wiki/common-errors).",
mysql_get_client_info(), mysql_get_client_version(), MYSQL_VERSION_ID);
}
@@ -88,8 +89,7 @@ DatabaseWorkerPool<T>::~DatabaseWorkerPool()
}
template <class T>
void DatabaseWorkerPool<T>::SetConnectionInfo(std::string const& infoString,
uint8 const asyncThreads, uint8 const synchThreads)
void DatabaseWorkerPool<T>::SetConnectionInfo(std::string_view infoString, uint8 const asyncThreads, uint8 const synchThreads)
{
_connectionInfo = std::make_unique<MySQLConnectionInfo>(infoString);
@@ -102,8 +102,7 @@ uint32 DatabaseWorkerPool<T>::Open()
{
WPFatal(_connectionInfo.get(), "Connection info was not set!");
LOG_INFO("sql.driver", "Opening DatabasePool '%s'. "
"Asynchronous connections: %u, synchronous connections: %u.",
LOG_INFO("sql.driver", "Opening DatabasePool '{}'. Asynchronous connections: {}, synchronous connections: {}.",
GetDatabaseName(), _async_threads, _synch_threads);
uint32 error = OpenConnections(IDX_ASYNC, _async_threads);
@@ -115,9 +114,8 @@ uint32 DatabaseWorkerPool<T>::Open()
if (!error)
{
LOG_INFO("sql.driver", "DatabasePool '%s' opened successfully. " SZFMTD
" total connections running.", GetDatabaseName(),
(_connections[IDX_SYNCH].size() + _connections[IDX_ASYNC].size()));
LOG_INFO("sql.driver", "DatabasePool '{}' opened successfully. {} total connections running.",
GetDatabaseName(), (_connections[IDX_SYNCH].size() + _connections[IDX_ASYNC].size()));
}
LOG_INFO("sql.driver", " ");
@@ -128,13 +126,12 @@ uint32 DatabaseWorkerPool<T>::Open()
template <class T>
void DatabaseWorkerPool<T>::Close()
{
LOG_INFO("sql.driver", "Closing down DatabasePool '%s'.", GetDatabaseName());
LOG_INFO("sql.driver", "Closing down DatabasePool '{}'.", GetDatabaseName());
//! Closes the actualy MySQL connection.
_connections[IDX_ASYNC].clear();
LOG_INFO("sql.driver", "Asynchronous connections on DatabasePool '%s' terminated. "
"Proceeding with synchronous connections.",
LOG_INFO("sql.driver", "Asynchronous connections on DatabasePool '{}' terminated. Proceeding with synchronous connections.",
GetDatabaseName());
//! Shut down the synchronous connections
@@ -143,15 +140,15 @@ void DatabaseWorkerPool<T>::Close()
//! meaning there can be no concurrent access at this point.
_connections[IDX_SYNCH].clear();
LOG_INFO("sql.driver", "All connections on DatabasePool '%s' closed.", GetDatabaseName());
LOG_INFO("sql.driver", "All connections on DatabasePool '{}' closed.", GetDatabaseName());
}
template <class T>
bool DatabaseWorkerPool<T>::PrepareStatements()
{
for (auto& connections : _connections)
for (auto const& connections : _connections)
{
for (auto& connection : connections)
for (auto const& connection : connections)
{
connection->LockIfReady();
if (!connection->PrepareStatements())
@@ -178,7 +175,7 @@ bool DatabaseWorkerPool<T>::PrepareStatements()
{
uint32 const paramCount = stmt->GetParameterCount();
// TC only supports uint8 indices.
// WH only supports uint8 indices.
ASSERT(paramCount < std::numeric_limits<uint8>::max());
_preparedStatementSize[i] = static_cast<uint8>(paramCount);
@@ -191,13 +188,13 @@ bool DatabaseWorkerPool<T>::PrepareStatements()
}
template <class T>
QueryResult DatabaseWorkerPool<T>::Query(char const* sql, T* connection /*= nullptr*/)
QueryResult DatabaseWorkerPool<T>::Query(std::string_view sql)
{
if (!connection)
connection = GetFreeConnection();
auto connection = GetFreeConnection();
ResultSet* result = connection->Query(sql);
connection->Unlock();
if (!result || !result->GetRowCount() || !result->NextRow())
{
delete result;
@@ -227,7 +224,7 @@ PreparedQueryResult DatabaseWorkerPool<T>::Query(PreparedStatement<T>* stmt)
}
template <class T>
QueryCallback DatabaseWorkerPool<T>::AsyncQuery(char const* sql)
QueryCallback DatabaseWorkerPool<T>::AsyncQuery(std::string_view sql)
{
BasicStatementTask* task = new BasicStatementTask(sql, true);
// Store future result before enqueueing - task might get already processed and deleted before returning from this method
@@ -316,6 +313,7 @@ void DatabaseWorkerPool<T>::DirectCommitTransaction(SQLTransaction<T>& transacti
{
T* connection = GetFreeConnection();
int errorCode = connection->ExecuteTransaction(transaction);
if (!errorCode)
{
connection->Unlock(); // OK, operation succesful
@@ -328,6 +326,7 @@ void DatabaseWorkerPool<T>::DirectCommitTransaction(SQLTransaction<T>& transacti
{
//todo: handle multiple sync threads deadlocking in a similar way as async threads
uint8 loopBreaker = 5;
for (uint8 i = 0; i < loopBreaker; ++i)
{
if (!connection->ExecuteTransaction(transaction))
@@ -376,6 +375,7 @@ void DatabaseWorkerPool<T>::KeepAlive()
//! If one or more worker threads are busy, the ping operations will not be split evenly, but this doesn't matter
//! as the sole purpose is to prevent connections from idling.
auto const count = _connections[IDX_ASYNC].size();
for (uint8 i = 0; i < count; ++i)
Enqueue(new PingOperation);
}
@@ -386,7 +386,8 @@ uint32 DatabaseWorkerPool<T>::OpenConnections(InternalIndex type, uint8 numConne
for (uint8 i = 0; i < numConnections; ++i)
{
// Create the connection
auto connection = [&] {
auto connection = [&]
{
switch (type)
{
case IDX_ASYNC:
@@ -406,7 +407,7 @@ uint32 DatabaseWorkerPool<T>::OpenConnections(InternalIndex type, uint8 numConne
}
else if (connection->GetServerVersion() < MIN_MYSQL_SERVER_VERSION)
{
LOG_ERROR("sql.driver", "AzerothCore does not support MySQL versions below 5.7");
LOG_ERROR("sql.driver", "AzerothCore does not support MySQL versions below 5.7 or MariaDB versions below 10.5");
return 1;
}
else
@@ -448,7 +449,7 @@ T* DatabaseWorkerPool<T>::GetFreeConnection()
{
std::ostringstream ss;
ss << boost::stacktrace::stacktrace();
LOG_WARN("sql.performances", "Sync query at:\n%s", ss.str().c_str());
LOG_WARN("sql.performances", "Sync query at:\n{}", ss.str());
}
#endif
@@ -469,15 +470,15 @@ T* DatabaseWorkerPool<T>::GetFreeConnection()
}
template <class T>
char const* DatabaseWorkerPool<T>::GetDatabaseName() const
std::string_view DatabaseWorkerPool<T>::GetDatabaseName() const
{
return _connectionInfo->database.c_str();
return std::string_view{ _connectionInfo->database };
}
template <class T>
void DatabaseWorkerPool<T>::Execute(char const* sql)
void DatabaseWorkerPool<T>::Execute(std::string_view sql)
{
if (Acore::IsFormatEmptyOrNull(sql))
if (sql.empty())
return;
BasicStatementTask* task = new BasicStatementTask(sql);
@@ -492,9 +493,9 @@ void DatabaseWorkerPool<T>::Execute(PreparedStatement<T>* stmt)
}
template <class T>
void DatabaseWorkerPool<T>::DirectExecute(char const* sql)
void DatabaseWorkerPool<T>::DirectExecute(std::string_view sql)
{
if (Acore::IsFormatEmptyOrNull(sql))
if (sql.empty())
return;
T* connection = GetFreeConnection();
@@ -514,7 +515,7 @@ void DatabaseWorkerPool<T>::DirectExecute(PreparedStatement<T>* stmt)
}
template <class T>
void DatabaseWorkerPool<T>::ExecuteOrAppend(SQLTransaction<T>& trans, char const* sql)
void DatabaseWorkerPool<T>::ExecuteOrAppend(SQLTransaction<T>& trans, std::string_view sql)
{
if (!trans)
Execute(sql);
@@ -535,6 +536,6 @@ template class AC_DATABASE_API DatabaseWorkerPool<LoginDatabaseConnection>;
template class AC_DATABASE_API DatabaseWorkerPool<WorldDatabaseConnection>;
template class AC_DATABASE_API DatabaseWorkerPool<CharacterDatabaseConnection>;
#ifdef PLAYERBOTS
template class AC_DATABASE_API DatabaseWorkerPool<PlayerbotDatabaseConnection>;
#ifdef MOD_PLAYERBOTS
template class AC_DATABASE_API DatabaseWorkerPool<PlayerbotsDatabaseConnection>;
#endif

View File

@@ -22,7 +22,6 @@
#include "Define.h"
#include "StringFormat.h"
#include <array>
#include <string>
#include <vector>
template <typename T>
@@ -45,19 +44,17 @@ private:
public:
/* Activity state */
DatabaseWorkerPool();
~DatabaseWorkerPool();
void SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads);
void SetConnectionInfo(std::string_view infoString, uint8 const asyncThreads, uint8 const synchThreads);
uint32 Open();
void Close();
//! Prepares all prepared statements
bool PrepareStatements();
inline MySQLConnectionInfo const* GetConnectionInfo() const
[[nodiscard]] inline MySQLConnectionInfo const* GetConnectionInfo() const
{
return _connectionInfo.get();
}
@@ -68,17 +65,17 @@ public:
//! 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(char const* sql);
void Execute(std::string_view 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.
template<typename Format, typename... Args>
void PExecute(Format&& sql, Args&&... args)
template<typename... Args>
void Execute(std::string_view sql, Args&&... args)
{
if (Acore::IsFormatEmptyOrNull(sql))
if (sql.empty())
return;
Execute(Acore::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
Execute(Acore::StringFormatFmt(sql, std::forward<Args>(args)...));
}
//! Enqueues a one-way SQL operation in prepared statement format that will be executed asynchronously.
@@ -91,17 +88,17 @@ public:
//! 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(char const* sql);
void DirectExecute(std::string_view 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.
template<typename Format, typename... Args>
void DirectPExecute(Format&& sql, Args&&... args)
template<typename... Args>
void DirectExecute(std::string_view sql, Args&&... args)
{
if (Acore::IsFormatEmptyOrNull(sql))
if (sql.empty())
return;
DirectExecute(Acore::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
DirectExecute(Acore::StringFormatFmt(sql, std::forward<Args>(args)...));
}
//! Directly executes a one-way SQL operation in prepared statement format, that will block the calling thread until finished.
@@ -114,28 +111,17 @@ public:
//! 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(char const* sql, T* connection = nullptr);
QueryResult Query(std::string_view sql);
//! 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.
template<typename Format, typename... Args>
QueryResult PQuery(Format&& sql, T* conn, Args&&... args)
template<typename... Args>
QueryResult Query(std::string_view sql, Args&&... args)
{
if (Acore::IsFormatEmptyOrNull(sql))
if (sql.empty())
return QueryResult(nullptr);
return Query(Acore::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str(), conn);
}
//! 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.
template<typename Format, typename... Args>
QueryResult PQuery(Format&& sql, Args&&... args)
{
if (Acore::IsFormatEmptyOrNull(sql))
return QueryResult(nullptr);
return Query(Acore::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
return Query(Acore::StringFormatFmt(sql, std::forward<Args>(args)...));
}
//! Directly executes an SQL query in prepared format that will block the calling thread until finished.
@@ -149,7 +135,7 @@ public:
//! 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(char const* sql);
QueryCallback AsyncQuery(std::string_view 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.
@@ -183,7 +169,7 @@ public:
//! 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<T>& trans, char const* sql);
void ExecuteOrAppend(SQLTransaction<T>& trans, std::string_view sql);
//! Method used to execute prepared statements in a diverse context.
//! Will be wrapped in a transaction if valid object is present, otherwise executed standalone.
@@ -213,7 +199,7 @@ public:
#endif
}
size_t QueueSize() const;
[[nodiscard]] size_t QueueSize() const;
private:
uint32 OpenConnections(InternalIndex type, uint8 numConnections);
@@ -226,7 +212,7 @@ private:
//! Caller MUST call t->Unlock() after touching the MySQL context to prevent deadlocks.
T* GetFreeConnection();
char const* GetDatabaseName() const;
[[nodiscard]] std::string_view GetDatabaseName() const;
//! Queue shared by async worker threads.
std::unique_ptr<ProducerConsumerQueue<SQLOperation*>> _queue;

View File

@@ -19,6 +19,8 @@
#include "Errors.h"
#include "Log.h"
#include "MySQLHacks.h"
#include "StringConvert.h"
#include "Types.h"
Field::Field()
{
@@ -28,241 +30,123 @@ Field::Field()
meta = nullptr;
}
Field::~Field() = default;
uint8 Field::GetUInt8() const
namespace
{
if (!data.value)
return 0;
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int8))
template<typename T>
constexpr T GetDefaultValue()
{
LogWrongType(__FUNCTION__);
return 0;
if constexpr (std::is_same_v<T, bool>)
return false;
else if constexpr (std::is_integral_v<T>)
return 0;
else if constexpr (std::is_floating_point_v<T>)
return 1.0f;
else if constexpr (std::is_same_v<T, std::vector<uint8>> || std::is_same_v<std::string_view, T>)
return {};
else
return "";
}
#endif
if (data.raw)
return *reinterpret_cast<uint8 const*>(data.value);
return static_cast<uint8>(strtoul(data.value, nullptr, 10));
}
int8 Field::GetInt8() const
{
if (!data.value)
return 0;
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int8))
template<typename T>
inline bool IsCorrectFieldType(DatabaseFieldTypes type)
{
LogWrongType(__FUNCTION__);
return 0;
// Int8
if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, int8> || std::is_same_v<T, uint8>)
{
if (type == DatabaseFieldTypes::Int8)
return true;
}
// In16
if constexpr (std::is_same_v<T, uint16> || std::is_same_v<T, int16>)
{
if (type == DatabaseFieldTypes::Int16)
return true;
}
// Int32
if constexpr (std::is_same_v<T, uint32> || std::is_same_v<T, int32>)
{
if (type == DatabaseFieldTypes::Int32)
return true;
}
// Int64
if constexpr (std::is_same_v<T, uint64> || std::is_same_v<T, int64>)
{
if (type == DatabaseFieldTypes::Int64)
return true;
}
// float
if constexpr (std::is_same_v<T, float>)
{
if (type == DatabaseFieldTypes::Float)
return true;
}
// dobule
if constexpr (std::is_same_v<T, double>)
{
if (type == DatabaseFieldTypes::Double || type == DatabaseFieldTypes::Decimal)
return true;
}
// Binary
if constexpr (std::is_same_v<T, Binary>)
{
if (type == DatabaseFieldTypes::Binary)
return true;
}
return false;
}
#endif
if (data.raw)
return *reinterpret_cast<int8 const*>(data.value);
return static_cast<int8>(strtol(data.value, nullptr, 10));
}
uint16 Field::GetUInt16() const
{
if (!data.value)
return 0;
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int16))
inline Optional<std::string_view> GetCleanAliasName(std::string_view alias)
{
LogWrongType(__FUNCTION__);
return 0;
if (alias.empty())
return {};
auto pos = alias.find_first_of('(');
if (pos == std::string_view::npos)
return {};
alias.remove_suffix(alias.length() - pos);
return { alias };
}
#endif
if (data.raw)
return *reinterpret_cast<uint16 const*>(data.value);
return static_cast<uint16>(strtoul(data.value, nullptr, 10));
}
int16 Field::GetInt16() const
{
if (!data.value)
return 0;
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int16))
template<typename T>
inline bool IsCorrectAlias(DatabaseFieldTypes type, std::string_view alias)
{
LogWrongType(__FUNCTION__);
return 0;
if constexpr (std::is_same_v<T, double>)
{
if ((StringEqualI(alias, "sum") || StringEqualI(alias, "avg")) && type == DatabaseFieldTypes::Decimal)
return true;
return false;
}
if constexpr (std::is_same_v<T, uint64>)
{
if (StringEqualI(alias, "count") && type == DatabaseFieldTypes::Int64)
return true;
return false;
}
if ((StringEqualI(alias, "min") || StringEqualI(alias, "max")) && IsCorrectFieldType<T>(type))
{
return true;
}
return false;
}
#endif
if (data.raw)
return *reinterpret_cast<int16 const*>(data.value);
return static_cast<int16>(strtol(data.value, nullptr, 10));
}
uint32 Field::GetUInt32() const
{
if (!data.value)
return 0;
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int32))
{
LogWrongType(__FUNCTION__);
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<uint32 const*>(data.value);
return static_cast<uint32>(strtoul(data.value, nullptr, 10));
}
int32 Field::GetInt32() const
{
if (!data.value)
return 0;
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int32))
{
LogWrongType(__FUNCTION__);
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<int32 const*>(data.value);
return static_cast<int32>(strtol(data.value, nullptr, 10));
}
uint64 Field::GetUInt64() const
{
if (!data.value)
return 0;
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int64))
{
LogWrongType(__FUNCTION__);
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<uint64 const*>(data.value);
return static_cast<uint64>(strtoull(data.value, nullptr, 10));
}
int64 Field::GetInt64() const
{
if (!data.value)
return 0;
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int64))
{
LogWrongType(__FUNCTION__);
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<int64 const*>(data.value);
return static_cast<int64>(strtoll(data.value, nullptr, 10));
}
float Field::GetFloat() const
{
if (!data.value)
return 0.0f;
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Float))
{
LogWrongType(__FUNCTION__);
return 0.0f;
}
#endif
if (data.raw)
return *reinterpret_cast<float const*>(data.value);
return static_cast<float>(atof(data.value));
}
double Field::GetDouble() const
{
if (!data.value)
return 0.0f;
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Double) && !IsType(DatabaseFieldTypes::Decimal))
{
LogWrongType(__FUNCTION__);
return 0.0f;
}
#endif
if (data.raw && !IsType(DatabaseFieldTypes::Decimal))
return *reinterpret_cast<double const*>(data.value);
return static_cast<double>(atof(data.value));
}
char const* Field::GetCString() const
{
if (!data.value)
return nullptr;
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (IsNumeric() && data.raw)
{
LogWrongType(__FUNCTION__);
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::string_view Field::GetStringView() const
{
if (!data.value)
return {};
char const* const string = GetCString();
if (!string)
return {};
return { 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::GetBinarySizeChecked(uint8* buf, size_t length) const
{
ASSERT(data.value && (data.length == length), "Expected %zu-byte binary blob, got %sdata (%u bytes) instead", length, data.value ? "" : "no ", data.length);
ASSERT(data.value && (data.length == length), "Expected {}-byte binary blob, got {}data ({} bytes) instead", length, data.value ? "" : "no ", data.length);
memcpy(buf, data.value, length);
}
@@ -297,13 +181,159 @@ bool Field::IsNumeric() const
meta->Type == DatabaseFieldTypes::Double);
}
void Field::LogWrongType(char const* getter) const
void Field::LogWrongType(std::string_view getter, std::string_view typeName) const
{
LOG_WARN("sql.sql", "Warning: %s on %s field %s.%s (%s.%s) at index %u.",
getter, meta->TypeName, meta->TableAlias, meta->Alias, meta->TableName, meta->Name, meta->Index);
LOG_WARN("sql.sql", "Warning: {}<{}> on {} field {}.{} ({}.{}) at index {}.",
getter, typeName, meta->TypeName, meta->TableAlias, meta->Alias, meta->TableName, meta->Name, meta->Index);
}
void Field::SetMetadata(QueryResultFieldMetadata const* fieldMeta)
{
meta = fieldMeta;
}
template<typename T>
T Field::GetData() const
{
static_assert(std::is_arithmetic_v<T>, "Unsurropt type for Field::GetData()");
if (!data.value)
return GetDefaultValue<T>();
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsCorrectFieldType<T>(meta->Type))
{
LogWrongType(__FUNCTION__, typeid(T).name());
//return GetDefaultValue<T>();
}
#endif
Optional<T> result = {};
if (data.raw)
result = *reinterpret_cast<T const*>(data.value);
else
result = Acore::StringTo<T>(data.value);
// Correct double fields... this undefined behavior :/
if constexpr (std::is_same_v<T, double>)
{
if (data.raw && !IsType(DatabaseFieldTypes::Decimal))
result = *reinterpret_cast<double const*>(data.value);
else
result = Acore::StringTo<float>(data.value);
}
// Check -1 for *_dbc db tables
if constexpr (std::is_same_v<T, uint32>)
{
std::string_view tableName{ meta->TableName };
if (!tableName.empty() && tableName.size() > 4)
{
auto signedResult = Acore::StringTo<int32>(data.value);
if (signedResult && !result && tableName.substr(tableName.length() - 4) == "_dbc")
{
LOG_DEBUG("sql.sql", "> Found incorrect value '{}' for type '{}' in _dbc table.", data.value, typeid(T).name());
LOG_DEBUG("sql.sql", "> Table name '{}'. Field name '{}'. Try return int32 value", meta->TableName, meta->Name);
return GetData<int32>();
}
}
}
if (auto alias = GetCleanAliasName(meta->Alias))
{
if ((StringEqualI(*alias, "min") || StringEqualI(*alias, "max")) && !IsCorrectAlias<T>(meta->Type, *alias))
{
LogWrongType(__FUNCTION__, typeid(T).name());
}
if ((StringEqualI(*alias, "sum") || StringEqualI(*alias, "avg")) && !IsCorrectAlias<T>(meta->Type, *alias))
{
LogWrongType(__FUNCTION__, typeid(T).name());
LOG_WARN("sql.sql", "> Please use GetData<double>()");
return GetData<double>();
}
if (StringEqualI(*alias, "count") && !IsCorrectAlias<T>(meta->Type, *alias))
{
LogWrongType(__FUNCTION__, typeid(T).name());
LOG_WARN("sql.sql", "> Please use GetData<uint64>()");
return GetData<uint64>();
}
}
if (!result)
{
LOG_FATAL("sql.sql", "> Incorrect value '{}' for type '{}'. Value is raw ? '{}'", data.value, typeid(T).name(), data.raw);
LOG_FATAL("sql.sql", "> Table name '{}'. Field name '{}'", meta->TableName, meta->Name);
//ABORT();
return GetDefaultValue<T>();
}
return *result;
}
template bool Field::GetData() const;
template uint8 Field::GetData() const;
template uint16 Field::GetData() const;
template uint32 Field::GetData() const;
template uint64 Field::GetData() const;
template int8 Field::GetData() const;
template int16 Field::GetData() const;
template int32 Field::GetData() const;
template int64 Field::GetData() const;
template float Field::GetData() const;
template double Field::GetData() const;
std::string Field::GetDataString() const
{
if (!data.value)
return "";
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (IsNumeric() && data.raw)
{
LogWrongType(__FUNCTION__, "std::string");
return "";
}
#endif
return { data.value, data.length };
}
std::string_view Field::GetDataStringView() const
{
if (!data.value)
return {};
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (IsNumeric() && data.raw)
{
LogWrongType(__FUNCTION__, "std::string_view");
return {};
}
#endif
return { data.value, data.length };
}
Binary Field::GetDataBinary() const
{
Binary result = {};
if (!data.value || !data.length)
return result;
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsCorrectFieldType<Binary>(meta->Type))
{
LogWrongType(__FUNCTION__, "Binary");
return {};
}
#endif
result.resize(data.length);
memcpy(result.data(), data.value, data.length);
return result;
}

View File

@@ -20,11 +20,26 @@
#include "DatabaseEnvFwd.h"
#include "Define.h"
#include "Duration.h"
#include <array>
#include <string>
#include <string_view>
#include <vector>
namespace Acore::Types
{
template <typename T>
using is_chrono_v = std::enable_if_t<std::is_same_v<Milliseconds, T>
|| std::is_same_v<Seconds, T>
|| std::is_same_v<Minutes, T>
|| std::is_same_v<Hours, T>
|| std::is_same_v<Days, T>
|| std::is_same_v<Weeks, T>
|| std::is_same_v<Years, T>
|| std::is_same_v<Months, T>, T>;
}
using Binary = std::vector<uint8>;
enum class DatabaseFieldTypes : uint8
{
Null,
@@ -41,11 +56,11 @@ enum class DatabaseFieldTypes : uint8
struct QueryResultFieldMetadata
{
char const* TableName = nullptr;
char const* TableAlias = nullptr;
char const* Name = nullptr;
char const* Alias = nullptr;
char const* TypeName = nullptr;
std::string TableName{};
std::string TableAlias{};
std::string Name{};
std::string Alias{};
std::string TypeName{};
uint32 Index = 0;
DatabaseFieldTypes Type = DatabaseFieldTypes::Null;
};
@@ -57,20 +72,20 @@ struct QueryResultFieldMetadata
Guideline on field type matching:
| MySQL type | method to use |
|------------------------|----------------------------------------|
| TINYINT | GetBool, GetInt8, GetUInt8 |
| SMALLINT | GetInt16, GetUInt16 |
| MEDIUMINT, INT | GetInt32, GetUInt32 |
| BIGINT | GetInt64, GetUInt64 |
| FLOAT | GetFloat |
| DOUBLE, DECIMAL | GetDouble |
| CHAR, VARCHAR, | GetCString, GetString |
| TINYTEXT, MEDIUMTEXT, | GetCString, GetString |
| TEXT, LONGTEXT | GetCString, GetString |
| TINYBLOB, MEDIUMBLOB, | GetBinary, GetString |
| BLOB, LONGBLOB | GetBinary, GetString |
| BINARY, VARBINARY | GetBinary |
| MySQL type | method to use |
|------------------------|-----------------------------------------|
| TINYINT | Get<bool>, Get<int8>, Get<uint8> |
| SMALLINT | Get<int16>, Get<uint16> |
| MEDIUMINT, INT | Get<int32>, Get<uint32> |
| BIGINT | Get<int64>, Get<uint64> |
| FLOAT | Get<float> |
| DOUBLE, DECIMAL | Get<double> |
| CHAR, VARCHAR, | Get<std::string>, Get<std::string_view> |
| TINYTEXT, MEDIUMTEXT, | Get<std::string>, Get<std::string_view> |
| TEXT, LONGTEXT | Get<std::string>, Get<std::string_view> |
| TINYBLOB, MEDIUMBLOB, | Get<Binary>, Get<std::string> |
| BLOB, LONGBLOB | Get<Binary>, Get<std::string> |
| BINARY, VARBINARY | Get<Binary> |
Return types of aggregate functions:
@@ -87,39 +102,49 @@ friend class PreparedResultSet;
public:
Field();
~Field();
~Field() = default;
bool GetBool() const // Wrapper, actually gets integer
[[nodiscard]] inline bool IsNull() const
{
return GetUInt8() == 1 ? true : false;
return data.value == nullptr;
}
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::string_view GetStringView() const;
std::vector<uint8> GetBinary() const;
template <size_t S>
std::array<uint8, S> GetBinary() const
template<typename T>
inline std::enable_if_t<std::is_arithmetic_v<T>, T> Get() const
{
std::array<uint8, S> buf;
return GetData<T>();
}
template<typename T>
inline std::enable_if_t<std::is_same_v<std::string, T>, T> Get() const
{
return GetDataString();
}
template<typename T>
inline std::enable_if_t<std::is_same_v<std::string_view, T>, T> Get() const
{
return GetDataStringView();
}
template<typename T>
inline std::enable_if_t<std::is_same_v<Binary, T>, T> Get() const
{
return GetDataBinary();
}
template <typename T, size_t S>
inline std::enable_if_t<std::is_same_v<Binary, T>, std::array<uint8, S>> Get() const
{
std::array<uint8, S> buf = {};
GetBinarySizeChecked(buf.data(), S);
return buf;
}
bool IsNull() const
template<typename T>
inline Acore::Types::is_chrono_v<T> Get(bool convertToUin32 = true) const
{
return data.value == nullptr;
return convertToUin32 ? T(GetData<uint32>()) : T(GetData<uint64>());
}
DatabaseFieldTypes GetType() { return meta->Type; }
@@ -134,12 +159,19 @@ protected:
void SetByteValue(char const* newValue, uint32 length);
void SetStructuredValue(char const* newValue, uint32 length);
bool IsType(DatabaseFieldTypes type) const;
bool IsNumeric() const;
[[nodiscard]] bool IsType(DatabaseFieldTypes type) const;
[[nodiscard]] bool IsNumeric() const;
private:
template<typename T>
T GetData() const;
std::string GetDataString() const;
std::string_view GetDataStringView() const;
Binary GetDataBinary() const;
QueryResultFieldMetadata const* meta;
void LogWrongType(char const* getter) const;
void LogWrongType(std::string_view getter, std::string_view typeName) const;
void SetMetadata(QueryResultFieldMetadata const* fieldMeta);
void GetBinarySizeChecked(uint8* buf, size_t size) const;
};

View File

@@ -43,12 +43,12 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, "
"gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.extra_flags "
"FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY c.order, c.guid", CONNECTION_ASYNC);
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, "
"c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, "
"cb.guid, c.extra_flags, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? "
"LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY c.order, c.guid", CONNECTION_ASYNC);
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT guid, name, at_login FROM characters WHERE guid = ? AND account = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level FROM characters WHERE guid = ?", CONNECTION_SYNCH);
@@ -85,7 +85,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_INS_CHARACTER_DAILYQUESTSTATUS, "INSERT INTO character_queststatus_daily (guid, quest, time) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHARACTER_WEEKLYQUESTSTATUS, "INSERT INTO character_queststatus_weekly (guid, quest) VALUES (?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHARACTER_MONTHLYQUESTSTATUS, "INSERT INTO character_queststatus_monthly (guid, quest) VALUES (?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHARACTER_SEASONALQUESTSTATUS, "INSERT INTO character_queststatus_seasonal (guid, quest, event) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHARACTER_SEASONALQUESTSTATUS, "INSERT IGNORE INTO character_queststatus_seasonal (guid, quest, event) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_REPUTATION, "SELECT faction, standing, flags FROM character_reputation WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_INVENTORY, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, bag, slot, "
"item, itemEntry FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot", CONNECTION_ASYNC);
@@ -119,12 +119,12 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_INS_AUCTION, "INSERT INTO auctionhouse (id, houseid, itemguid, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_AUCTION, "DELETE FROM auctionhouse WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_AUCTION_BID, "UPDATE auctionhouse SET buyguid = ?, lastbid = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_MAIL, "INSERT INTO mail(id, messageType, stationery, mailTemplateId, sender, receiver, subject, body, has_items, expire_time, deliver_time, money, cod, checked, auctionId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_MAIL, "INSERT INTO mail(id, messageType, stationery, mailTemplateId, sender, receiver, subject, body, has_items, expire_time, deliver_time, money, cod, checked) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_MAIL_BY_ID, "DELETE FROM mail WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_MAIL_ITEM, "INSERT INTO mail_items(mail_id, item_guid, receiver) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_MAIL_ITEM, "DELETE FROM mail_items WHERE item_guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_INVALID_MAIL_ITEM, "DELETE FROM mail_items WHERE item_guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_EXPIRED_MAIL, "SELECT id, messageType, sender, receiver, has_items, expire_time, stationery, checked, mailTemplateId, auctionId FROM mail WHERE expire_time < ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_EXPIRED_MAIL, "SELECT id, messageType, sender, receiver, has_items, expire_time, stationery, checked, mailTemplateId FROM mail WHERE expire_time < ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_EXPIRED_MAIL_ITEMS, "SELECT item_guid, itemEntry, mail_id FROM mail_items mi INNER JOIN item_instance ii ON ii.guid = mi.item_guid LEFT JOIN mail mm ON mi.mail_id = mm.id WHERE mm.id IS NOT NULL AND mm.expire_time < ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_UPD_MAIL_RETURNED, "UPDATE mail SET sender = ?, receiver = ?, expire_time = ?, deliver_time = ?, cod = 0, checked = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_MAIL_ITEM_RECEIVER, "UPDATE mail_items SET receiver = ? WHERE item_guid = ?", CONNECTION_ASYNC);
@@ -155,6 +155,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
// 0: uint32, 1: string, 2: uint32, 3: string, 4: string, 5: uint64, 6-10: uint32, 11: uint64
PrepareStatement(CHAR_INS_GUILD, "INSERT INTO guild (guildid, name, leaderguid, info, motd, createdate, EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor, BankMoney) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_GUILD, "DELETE FROM guild WHERE guildid = ?", CONNECTION_ASYNC); // 0: uint32
// 0: string, 1: uint32
PrepareStatement(CHAR_UPD_GUILD_NAME, "UPDATE guild SET name = ? WHERE guildid = ?", CONNECTION_ASYNC);
// 0: uint32, 1: uint32, 2: uint8, 4: string, 5: string
PrepareStatement(CHAR_INS_GUILD_MEMBER, "INSERT INTO guild_member (guildid, guid, `rank`, pnote, offnote) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_GUILD_MEMBER, "DELETE FROM guild_member WHERE guid = ?", CONNECTION_ASYNC); // 0: uint32
@@ -415,7 +417,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHAR_SOCIAL, "SELECT DISTINCT guid FROM character_social WHERE friend = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_OLD_CHARS, "SELECT guid, deleteInfos_Account FROM characters WHERE deleteDate IS NOT NULL AND deleteDate < ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_ARENA_TEAM_ID_BY_PLAYER_GUID, "SELECT arena_team_member.arenateamid FROM arena_team_member JOIN arena_team ON arena_team_member.arenateamid = arena_team.arenateamid WHERE guid = ? AND type = ? LIMIT 1", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_MAIL, "SELECT id, messageType, sender, receiver, subject, body, expire_time, deliver_time, money, cod, checked, stationery, mailTemplateId, auctionId FROM mail WHERE receiver = ? AND deliver_time <= ? ORDER BY id DESC", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_MAIL, "SELECT id, messageType, sender, receiver, subject, body, expire_time, deliver_time, money, cod, checked, stationery, mailTemplateId FROM mail WHERE receiver = ? AND deliver_time <= ? ORDER BY id DESC", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_NEXT_MAIL_DELIVERYTIME, "SELECT MIN(deliver_time) FROM mail WHERE receiver = ? AND deliver_time > ? AND (checked & 1) = 0 LIMIT 1", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_CHAR_AURA_FROZEN, "DELETE FROM character_aura WHERE spell = 9454 AND guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_INVENTORY_COUNT_ITEM, "SELECT COUNT(itemEntry) FROM character_inventory ci INNER JOIN item_instance ii ON ii.guid = ci.item WHERE itemEntry = ?", CONNECTION_SYNCH);
@@ -540,16 +542,11 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_CALENDAR_INVITE, "DELETE FROM calendar_invites WHERE id = ?", CONNECTION_ASYNC);
// Pet
PrepareStatement(CHAR_SEL_PET_SLOTS, "SELECT owner, slot FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_SLOTS_DETAIL, "SELECT owner, id, entry, level, name FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_ENTRY, "SELECT entry, slot FROM character_pet WHERE owner = ? AND id = ? AND slot >= ? AND slot <= ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_SLOT_BY_ID, "SELECT slot, entry FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_SPELL_LIST, "SELECT DISTINCT pet_spell.spell FROM pet_spell, character_pet WHERE character_pet.owner = ? AND character_pet.id = pet_spell.guid AND character_pet.id <> ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_PET, "SELECT id FROM character_pet WHERE owner = ? AND id <> ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_PETS, "SELECT id FROM character_pet WHERE owner = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_PET_IDS, "SELECT id FROM character_pet WHERE owner = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER, "DELETE FROM character_pet_declinedname WHERE owner = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME, "DELETE FROM character_pet_declinedname WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_ADD_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_AURA, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges FROM pet_aura WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, category, time FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
@@ -561,15 +558,9 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_INS_PET_SPELL, "INSERT INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
"base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND entry = ? AND (slot = ? OR slot > ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_PET_BY_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?) ", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_PETS, "SELECT id, entry, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_PET_BY_OWNER, "DELETE FROM character_pet WHERE owner = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_PET_NAME, "UPDATE character_pet SET name = ?, renamed = 1 WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ? AND id <> ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_PET_BY_ID, "DELETE FROM character_pet WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_PET_BY_SLOT, "DELETE FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?)", CONNECTION_ASYNC);
@@ -593,10 +584,21 @@ void CharacterDatabaseConnection::DoPrepareStatements()
// Recovery Item
PrepareStatement(CHAR_INS_RECOVERY_ITEM, "INSERT INTO recovery_item (Guid, ItemEntry, Count) VALUES (?, ?, ?)", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_RECOVERY_ITEM, "SELECT id, itemEntry, Count, Guid FROM recovery_item WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_RECOVERY_ITEM_LIST, "SELECT id, itemEntry, Count FROM recovery_item WHERE Guid = ? ORDER BY id DESC", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_RECOVERY_ITEM, "DELETE FROM recovery_item WHERE Guid = ? AND ItemEntry = ? AND Count = ? ORDER BY Id DESC LIMIT 1", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_RECOVERY_ITEM_BY_RECOVERY_ID, "DELETE FROM recovery_item WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_HONORPOINTS, "SELECT totalHonorPoints FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_ARENAPOINTS, "SELECT arenaPoints FROM characters WHERE guid = ?", CONNECTION_SYNCH);
// Character names
PrepareStatement(CHAR_INS_RESERVED_PLAYER_NAME, "INSERT IGNORE INTO reserved_name (name) VALUES (?)", CONNECTION_ASYNC);
// Character settings
PrepareStatement(CHAR_SEL_CHAR_SETTINGS, "SELECT source, data FROM character_settings WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_REP_CHAR_SETTINGS, "REPLACE INTO character_settings (guid, source, data) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_SETTINGS, "DELETE FROM character_settings WHERE guid = ?", CONNECTION_ASYNC);
}
CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)

View File

@@ -137,6 +137,7 @@ enum CharacterDatabaseStatements : uint32
CHAR_INS_GUILD,
CHAR_DEL_GUILD,
CHAR_UPD_GUILD_NAME,
CHAR_INS_GUILD_MEMBER,
CHAR_DEL_GUILD_MEMBER,
CHAR_DEL_GUILD_MEMBERS,
@@ -468,23 +469,12 @@ enum CharacterDatabaseStatements : uint32
CHAR_DEL_PET_SPELLS,
CHAR_DEL_CHAR_PET_BY_OWNER,
CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER,
CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT,
CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS,
CHAR_SEL_PET_SLOTS,
CHAR_SEL_PET_SLOTS_DETAIL,
CHAR_SEL_PET_ENTRY,
CHAR_SEL_PET_SLOT_BY_ID,
CHAR_SEL_PET_SPELL_LIST,
CHAR_SEL_CHAR_PET,
CHAR_SEL_CHAR_PETS,
CHAR_SEL_CHAR_PET_BY_ENTRY,
CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2,
CHAR_SEL_CHAR_PET_BY_SLOT,
CHAR_SEL_CHAR_PET_IDS,
CHAR_DEL_CHAR_PET_DECLINEDNAME,
CHAR_ADD_CHAR_PET_DECLINEDNAME,
CHAR_SEL_PET_DECLINED_NAME,
CHAR_UPD_CHAR_PET_NAME,
CHAR_UDP_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID,
CHAR_UDP_CHAR_PET_SLOT_BY_SLOT,
CHAR_UPD_CHAR_PET_SLOT_BY_ID,
CHAR_DEL_CHAR_PET_BY_ID,
CHAR_DEL_CHAR_PET_BY_SLOT,
@@ -509,10 +499,20 @@ enum CharacterDatabaseStatements : uint32
CHAR_UPD_QUEST_TRACK_ABANDON_TIME,
CHAR_INS_RECOVERY_ITEM,
CHAR_SEL_RECOVERY_ITEM,
CHAR_SEL_RECOVERY_ITEM_LIST,
CHAR_DEL_RECOVERY_ITEM,
CHAR_DEL_RECOVERY_ITEM_BY_RECOVERY_ID,
CHAR_SEL_HONORPOINTS,
CHAR_SEL_ARENAPOINTS,
CHAR_INS_RESERVED_PLAYER_NAME,
CHAR_SEL_CHAR_SETTINGS,
CHAR_REP_CHAR_SETTINGS,
CHAR_DEL_CHAR_SETTINGS,
MAX_CHARACTERDATABASE_STATEMENTS
};
@@ -524,7 +524,7 @@ public:
//- Constructors for sync and async connections
CharacterDatabaseConnection(MySQLConnectionInfo& connInfo);
CharacterDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo);
~CharacterDatabaseConnection();
~CharacterDatabaseConnection() override;
//- Loads database type specific prepared statements
void DoPrepareStatements() override;

View File

@@ -128,7 +128,7 @@ public:
//- Constructors for sync and async connections
LoginDatabaseConnection(MySQLConnectionInfo& connInfo);
LoginDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo);
~LoginDatabaseConnection();
~LoginDatabaseConnection() override;
//- Loads database type specific prepared statements
void DoPrepareStatements() override;

View File

@@ -0,0 +1,111 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef MOD_PLAYERBOTS
#include "PlayerbotsDatabase.h"
#include "MySQLPreparedStatement.h"
void PlayerbotsDatabaseConnection::DoPrepareStatements()
{
if (!m_reconnecting)
m_stmts.resize(MAX_PLAYERBOTS_STATEMENTS);
PrepareStatement(PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER, "SELECT DISTINCT name FROM playerbots_custom_strategy WHERE owner = ?", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER_AND_NAME, "SELECT idx, action_line FROM playerbots_custom_strategy WHERE owner = ? AND name = ? ORDER BY idx", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER_AND_NAME_AND_IDX, "SELECT action_line FROM playerbots_custom_strategy WHERE owner = ? AND name = ? AND idx = ?", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_DEL_CUSTOM_STRATEGY, "DELETE FROM playerbots_custom_strategy WHERE name = ? AND owner = ? AND idx = ?", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_UPD_CUSTOM_STRATEGY, "UPDATE playerbots_custom_strategy SET action_line = ? WHERE name = ? AND owner = ? AND idx = ?", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_INS_CUSTOM_STRATEGY, "INSERT INTO playerbots_custom_strategy (name, owner, idx, action_line) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_SEL_DB_STORE, "SELECT `key`,`value` FROM `playerbots_db_store` WHERE `guid` = ?", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_DEL_DB_STORE, "DELETE FROM `playerbots_db_store` WHERE `guid` = ?", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_INS_DB_STORE, "INSERT INTO `playerbots_db_store` (`guid`, `key`, `value`) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_SEL_ENCHANTS, "SELECT class, spec, spellid, slotid FROM playerbots_enchants", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_EQUIP_CACHE, "SELECT clazz, lvl, slot, quality, item FROM playerbots_equip_cache", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_INS_EQUIP_CACHE, "INSERT INTO playerbots_equip_cache (clazz, lvl, slot, quality, item) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_SEL_GUILD_TASKS_BY_VALUE, "SELECT `value`, `time`, validIn FROM playerbots_guild_tasks WHERE `value` = ? AND guildid = ? AND `type` = ?", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER, "SELECT `value`, `time`, validIn, guildid FROM playerbots_guild_tasks WHERE owner = ? AND `type` = ?", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER_AND_TYPE, "SELECT `value`, `time`, validIn FROM playerbots_guild_tasks WHERE owner = ? AND guildid = ? AND `type` = ?", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER_DISTINCT, "SELECT DISTINCT guildid FROM playerbots_guild_tasks WHERE owner = ?", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER_ORDERED, "SELECT `value`, `time`, validIn, guildid FROM playerbots_guild_tasks WHERE owner = ? AND type = ? ORDER BY guildid", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_DEL_GUILD_TASKS, "DELETE FROM playerbots_guild_tasks WHERE owner = ? AND guildid = ? AND `type` = ?", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_INS_GUILD_TASKS, "INSERT INTO playerbots_guild_tasks (owner, guildid, `time`, validIn, `type`, `value`) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_SEL_RANDOM_BOTS_VALUE, "SELECT value FROM playerbots_random_bots WHERE event = ?", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BOT, "SELECT `bot` FROM playerbots_random_bots WHERE event = ?", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_EVENT, "SELECT bot FROM playerbots_random_bots WHERE owner = ? AND event = ?", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_BOT, "SELECT `event`, `value`, `time`, validIn, `data` FROM playerbots_random_bots WHERE owner = ? AND bot = ?", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BY_EVENT_AND_VALUE, "SELECT bot FROM playerbots_random_bots WHERE event = ? AND value = ?", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_INS_RANDOM_BOTS, "INSERT INTO playerbots_random_bots (owner, bot, `time`, validIn, event, `value`, `data`) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_DEL_RANDOM_BOTS, "DELETE FROM playerbots_random_bots", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_DEL_RANDOM_BOTS_BY_OWNER, "DELETE FROM playerbots_random_bots WHERE owner = ? AND bot = ?", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_DEL_RANDOM_BOTS_BY_OWNER_AND_EVENT, "DELETE FROM playerbots_random_bots WHERE owner = ? AND bot = ? AND event = ?", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_UPD_RANDOM_BOTS, "UPDATE playerbots_random_bots SET validIn = ? WHERE event = ? AND bot = ?", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_SEL_RARITY_CACHE, "SELECT item, rarity FROM playerbots_rarity_cache", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_INS_RARITY_CACHE, "INSERT INTO playerbots_rarity_cache (item, rarity) VALUES (?, ?)", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_SEL_RNDITEM_CACHE, "SELECT lvl, type, item FROM playerbots_rnditem_cache", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_INS_RNDITEM_CACHE, "INSERT INTO playerbots_rnditem_cache (lvl, type, item) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_SEL_SPEECH, "SELECT name, text, type FROM playerbots_speech", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_SPEECH_PROBABILITY, "SELECT name, probability FROM playerbots_speech_probability", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_TELE_CACHE, "SELECT map_id, x, y, z, level FROM playerbots_tele_cache", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_INS_TELE_CACHE, "INSERT INTO playerbots_tele_cache (level, map_id, x, y, z) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_SEL_TRAVELNODE, "SELECT id, name, map_id, x, y, z, linked FROM playerbots_travelnode", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_INS_TRAVELNODE, "INSERT INTO `playerbots_travelnode` (`id`, `name`, `map_id`, `x`, `y`, `z`, `linked`) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_DEL_TRAVELNODE, "DELETE FROM playerbots_travelnode", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_SEL_TRAVELNODE_LINK, "SELECT node_id, to_node_id,type,object,distance,swim_distance, extra_cost,calculated, max_creature_0,max_creature_1,max_creature_2 FROM playerbots_travelnode_link", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_INS_TRAVELNODE_LINK, "INSERT INTO `playerbots_travelnode_link` (`node_id`, `to_node_id`,`type`,`object`,`distance`,`swim_distance`, `extra_cost`,`calculated`, `max_creature_0`,`max_creature_1`,`max_creature_2`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_DEL_TRAVELNODE_LINK, "DELETE FROM playerbots_travelnode_link", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_SEL_TRAVELNODE_PATH, "SELECT node_id, to_node_id, nr, map_id, x, y, z FROM playerbots_travelnode_path order by node_id, to_node_id, nr", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_INS_TRAVELNODE_PATH, "INSERT INTO `playerbots_travelnode_path` (`node_id`, `to_node_id`, `nr`, `map_id`, `x`, `y`, `z`) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_DEL_TRAVELNODE_PATH, "DELETE FROM playerbots_travelnode_path", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_SEL_TEXT, "SELECT `key`,`text` FROM playerbots_text", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_WEIGHTSCALES, "SELECT id, name, class FROM playerbots_weightscales", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_SEL_WEIGHTSCALE_DATA, "SELECT id, field, val FROM playerbots_weightscale_data", CONNECTION_SYNCH);
PrepareStatement(PLAYERBOTS_INS_EQUIP_CACHE_NEW, "INSERT INTO playerbots_item_info_cache (id, quality, slot, source, sourceId, team, faction, factionRepRank, minLevel, "
"scale_1, scale_2, scale_3, scale_4, scale_5, scale_6, scale_7, scale_8, scale_9, scale_10, scale_11, scale_12, scale_13, scale_14, scale_15, "
"scale_16, scale_17, scale_18, scale_19, scale_20, scale_21, scale_22, scale_23, scale_24, scale_25, scale_26, scale_27, scale_28, scale_29, scale_30, scale_31, scale_32) "
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(PLAYERBOTS_DEL_EQUIP_CACHE_NEW, "DELETE FROM playerbots_item_info_cache WHERE id = ?", CONNECTION_ASYNC);
}
PlayerbotsDatabaseConnection::PlayerbotsDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
{
}
PlayerbotsDatabaseConnection::PlayerbotsDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo)
{
}
PlayerbotsDatabaseConnection::~PlayerbotsDatabaseConnection()
{
}
#endif

View File

@@ -0,0 +1,119 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef MOD_PLAYERBOTS
#ifndef _PlayerbotsDatabase_H
#define _PlayerbotsDatabase_H
#include "MySQLConnection.h"
enum PlayerbotsDatabaseStatements : uint32
{
/* Naming standard for defines:
{DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed}
When updating more than one field, consider looking at the calling function
name for a suiting suffix.
*/
PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER,
PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER_AND_NAME,
PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER_AND_NAME_AND_IDX,
PLAYERBOTS_DEL_CUSTOM_STRATEGY,
PLAYERBOTS_UPD_CUSTOM_STRATEGY,
PLAYERBOTS_INS_CUSTOM_STRATEGY,
PLAYERBOTS_SEL_DB_STORE,
PLAYERBOTS_DEL_DB_STORE,
PLAYERBOTS_INS_DB_STORE,
PLAYERBOTS_SEL_ENCHANTS,
PLAYERBOTS_SEL_EQUIP_CACHE,
PLAYERBOTS_INS_EQUIP_CACHE,
PLAYERBOTS_SEL_GUILD_TASKS_BY_VALUE,
PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER,
PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER_AND_TYPE,
PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER_DISTINCT,
PLAYERBOTS_SEL_GUILD_TASKS_BY_OWNER_ORDERED,
PLAYERBOTS_DEL_GUILD_TASKS,
PLAYERBOTS_INS_GUILD_TASKS,
PLAYERBOTS_SEL_RANDOM_BOTS_VALUE,
PLAYERBOTS_SEL_RANDOM_BOTS_BOT,
PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_EVENT,
PLAYERBOTS_SEL_RANDOM_BOTS_BY_OWNER_AND_BOT,
PLAYERBOTS_SEL_RANDOM_BOTS_BY_EVENT_AND_VALUE,
PLAYERBOTS_INS_RANDOM_BOTS,
PLAYERBOTS_DEL_RANDOM_BOTS,
PLAYERBOTS_DEL_RANDOM_BOTS_BY_OWNER,
PLAYERBOTS_DEL_RANDOM_BOTS_BY_OWNER_AND_EVENT,
PLAYERBOTS_UPD_RANDOM_BOTS,
PLAYERBOTS_SEL_RARITY_CACHE,
PLAYERBOTS_INS_RARITY_CACHE,
PLAYERBOTS_SEL_RNDITEM_CACHE,
PLAYERBOTS_INS_RNDITEM_CACHE,
PLAYERBOTS_SEL_SPEECH,
PLAYERBOTS_SEL_SPEECH_PROBABILITY,
PLAYERBOTS_SEL_TELE_CACHE,
PLAYERBOTS_INS_TELE_CACHE,
PLAYERBOTS_SEL_TEXT,
PLAYERBOTS_SEL_TRAVELNODE,
PLAYERBOTS_INS_TRAVELNODE,
PLAYERBOTS_DEL_TRAVELNODE,
PLAYERBOTS_SEL_TRAVELNODE_LINK,
PLAYERBOTS_INS_TRAVELNODE_LINK,
PLAYERBOTS_DEL_TRAVELNODE_LINK,
PLAYERBOTS_SEL_TRAVELNODE_PATH,
PLAYERBOTS_INS_TRAVELNODE_PATH,
PLAYERBOTS_DEL_TRAVELNODE_PATH,
PLAYERBOTS_SEL_WEIGHTSCALES,
PLAYERBOTS_SEL_WEIGHTSCALE_DATA,
PLAYERBOTS_INS_EQUIP_CACHE_NEW,
PLAYERBOTS_DEL_EQUIP_CACHE_NEW,
MAX_PLAYERBOTS_STATEMENTS
};
class AC_DATABASE_API PlayerbotsDatabaseConnection : public MySQLConnection
{
public:
typedef PlayerbotsDatabaseStatements Statements;
//- Constructors for sync and async connections
PlayerbotsDatabaseConnection(MySQLConnectionInfo& connInfo);
PlayerbotsDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo);
~PlayerbotsDatabaseConnection();
//- Loads database type specific prepared statements
void DoPrepareStatements() override;
};
#endif
#endif

View File

@@ -77,13 +77,13 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, security, help FROM command", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, detection_range, scale, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, InhabitType, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, scale, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id1 = ? OR id2 = ? OR id3 = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM creature WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH);
PrepareStatement(WORLD_INS_CREATURE, "INSERT INTO creature (guid, id , map, spawnMask, phaseMask, modelid, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, currentwaypoint, curhealth, curmana, MovementType, npcflag, unit_flags, dynamicflags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(WORLD_SEL_CREATURE_NEAREST, "SELECT guid, id1, id2, id3, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM creature WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH);
PrepareStatement(WORLD_INS_CREATURE, "INSERT INTO creature (guid, id1, id2, id3, map, spawnMask, phaseMask, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, currentwaypoint, curhealth, curmana, MovementType, npcflag, unit_flags, dynamicflags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(WORLD_DEL_GAME_EVENT_CREATURE, "DELETE FROM game_event_creature WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_DEL_GAME_EVENT_MODEL_EQUIP, "DELETE FROM game_event_model_equip WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_INS_GAMEOBJECT, "INSERT INTO gameobject (guid, id, map, spawnMask, phaseMask, position_x, position_y, position_z, orientation, rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);

View File

@@ -112,7 +112,7 @@ public:
//- Constructors for sync and async connections
WorldDatabaseConnection(MySQLConnectionInfo& connInfo);
WorldDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo);
~WorldDatabaseConnection();
~WorldDatabaseConnection() override;
//- Loads database type specific prepared statements
void DoPrepareStatements() override;

View File

@@ -16,7 +16,6 @@
*/
#include "MySQLConnection.h"
#include "Common.h"
#include "DatabaseWorker.h"
#include "Log.h"
#include "MySQLHacks.h"
@@ -24,6 +23,7 @@
#include "MySQLWorkaround.h"
#include "PreparedStatement.h"
#include "QueryResult.h"
#include "StringConvert.h"
#include "Timer.h"
#include "Tokenize.h"
#include "Transaction.h"
@@ -31,38 +31,38 @@
#include <errmsg.h>
#include <mysqld_error.h>
MySQLConnectionInfo::MySQLConnectionInfo(std::string const& infoString)
MySQLConnectionInfo::MySQLConnectionInfo(std::string_view infoString)
{
std::vector<std::string_view> tokens = Acore::Tokenize(infoString, ';', true);
if (tokens.size() != 5 && tokens.size() != 6)
return;
host.assign(tokens[0]);
port_or_socket.assign(tokens[1]);
user.assign(tokens[2]);
password.assign(tokens[3]);
database.assign(tokens[4]);
host.assign(tokens.at(0));
port_or_socket.assign(tokens.at(1));
user.assign(tokens.at(2));
password.assign(tokens.at(3));
database.assign(tokens.at(4));
if (tokens.size() == 6)
ssl.assign(tokens[5]);
ssl.assign(tokens.at(5));
}
MySQLConnection::MySQLConnection(MySQLConnectionInfo& connInfo) :
m_reconnecting(false),
m_prepareError(false),
m_queue(nullptr),
m_Mysql(nullptr),
m_connectionInfo(connInfo),
m_connectionFlags(CONNECTION_SYNCH) { }
m_reconnecting(false),
m_prepareError(false),
m_queue(nullptr),
m_Mysql(nullptr),
m_connectionInfo(connInfo),
m_connectionFlags(CONNECTION_SYNCH) { }
MySQLConnection::MySQLConnection(ProducerConsumerQueue<SQLOperation*>* queue, MySQLConnectionInfo& connInfo) :
m_reconnecting(false),
m_prepareError(false),
m_queue(queue),
m_Mysql(nullptr),
m_connectionInfo(connInfo),
m_connectionFlags(CONNECTION_ASYNC)
m_reconnecting(false),
m_prepareError(false),
m_queue(queue),
m_Mysql(nullptr),
m_connectionInfo(connInfo),
m_connectionFlags(CONNECTION_ASYNC)
{
m_worker = std::make_unique<DatabaseWorker>(m_queue, this);
}
@@ -76,7 +76,6 @@ void MySQLConnection::Close()
{
// Stop the worker thread before the statements are cleared
m_worker.reset();
m_stmts.clear();
if (m_Mysql)
@@ -88,21 +87,19 @@ void MySQLConnection::Close()
uint32 MySQLConnection::Open()
{
MYSQL *mysqlInit;
mysqlInit = mysql_init(nullptr);
MYSQL* mysqlInit = mysql_init(nullptr);
if (!mysqlInit)
{
LOG_ERROR("sql.sql", "Could not initialize Mysql connection to database `%s`", m_connectionInfo.database.c_str());
LOG_ERROR("sql.sql", "Could not initialize Mysql connection to database `{}`", m_connectionInfo.database);
return CR_UNKNOWN_ERROR;
}
int port;
uint32 port;
char const* unix_socket;
//unsigned int timeout = 10;
mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8");
//mysql_options(mysqlInit, MYSQL_OPT_READ_TIMEOUT, (char const*)&timeout);
#ifdef _WIN32
#ifdef _WIN32
if (m_connectionInfo.host == ".") // named pipe use option (Windows)
{
unsigned int opt = MYSQL_PROTOCOL_PIPE;
@@ -112,10 +109,10 @@ uint32 MySQLConnection::Open()
}
else // generic case
{
port = atoi(m_connectionInfo.port_or_socket.c_str());
port = *Acore::StringTo<uint32>(m_connectionInfo.port_or_socket);
unix_socket = 0;
}
#else
#else
if (m_connectionInfo.host == ".") // socket use option (Unix/Linux)
{
unsigned int opt = MYSQL_PROTOCOL_SOCKET;
@@ -126,10 +123,10 @@ uint32 MySQLConnection::Open()
}
else // generic case
{
port = atoi(m_connectionInfo.port_or_socket.c_str());
port = *Acore::StringTo<uint32>(m_connectionInfo.port_or_socket);
unix_socket = nullptr;
}
#endif
#endif
if (m_connectionInfo.ssl != "")
{
@@ -139,6 +136,7 @@ uint32 MySQLConnection::Open()
{
opt_use_ssl = SSL_MODE_REQUIRED;
}
mysql_options(mysqlInit, MYSQL_OPT_SSL_MODE, (char const*)&opt_use_ssl);
#else
MySQLBool opt_use_ssl = MySQLBool(0);
@@ -146,6 +144,7 @@ uint32 MySQLConnection::Open()
{
opt_use_ssl = MySQLBool(1);
}
mysql_options(mysqlInit, MYSQL_OPT_SSL_ENFORCE, (char const*)&opt_use_ssl);
#endif
}
@@ -157,24 +156,21 @@ uint32 MySQLConnection::Open()
{
if (!m_reconnecting)
{
LOG_INFO("sql.sql", "MySQL client library: %s", mysql_get_client_info());
LOG_INFO("sql.sql", "MySQL server ver: %s ", mysql_get_server_info(m_Mysql));
// MySQL version above 5.1 IS required in both client and server and there is no known issue with different versions above 5.1
// if (mysql_get_server_version(m_Mysql) != mysql_get_client_version())
// LOG_INFO("sql.sql", "[WARNING] MySQL client/server version mismatch; may conflict with behaviour of prepared statements.");
LOG_INFO("sql.sql", "MySQL client library: {}", mysql_get_client_info());
LOG_INFO("sql.sql", "MySQL server ver: {} ", mysql_get_server_info(m_Mysql));
}
LOG_INFO("sql.sql", "Connected to MySQL database at %s", m_connectionInfo.host.c_str());
LOG_INFO("sql.sql", "Connected to MySQL database at {}", m_connectionInfo.host);
mysql_autocommit(m_Mysql, 1);
// set connection properties to UTF8 to properly handle locales for different
// server configs - core sends data in UTF8, so MySQL must expect UTF8 too
mysql_set_character_set(m_Mysql, "utf8");
mysql_set_character_set(m_Mysql, "utf8mb4");
return 0;
}
else
{
LOG_ERROR("sql.sql", "Could not connect to MySQL database at %s: %s", m_connectionInfo.host.c_str(), mysql_error(mysqlInit));
LOG_ERROR("sql.sql", "Could not connect to MySQL database at {}: {}", m_connectionInfo.host, mysql_error(mysqlInit));
uint32 errorCode = mysql_errno(mysqlInit);
mysql_close(mysqlInit);
return errorCode;
@@ -187,7 +183,7 @@ bool MySQLConnection::PrepareStatements()
return !m_prepareError;
}
bool MySQLConnection::Execute(char const* sql)
bool MySQLConnection::Execute(std::string_view sql)
{
if (!m_Mysql)
return false;
@@ -195,12 +191,12 @@ bool MySQLConnection::Execute(char const* sql)
{
uint32 _s = getMSTime();
if (mysql_query(m_Mysql, sql))
if (mysql_query(m_Mysql, std::string(sql).c_str()))
{
uint32 lErrno = mysql_errno(m_Mysql);
LOG_INFO("sql.sql", "SQL: %s", sql);
LOG_ERROR("sql.sql", "[%u] %s", lErrno, mysql_error(m_Mysql));
LOG_INFO("sql.sql", "SQL: {}", sql);
LOG_ERROR("sql.sql", "[{}] {}", lErrno, mysql_error(m_Mysql));
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
return Execute(sql); // Try again
@@ -208,7 +204,7 @@ bool MySQLConnection::Execute(char const* sql)
return false;
}
else
LOG_DEBUG("sql.sql", "[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql);
LOG_DEBUG("sql.sql", "[{} ms] SQL: {}", getMSTimeDiff(_s, getMSTime()), sql);
}
return true;
@@ -222,7 +218,7 @@ bool MySQLConnection::Execute(PreparedStatementBase* stmt)
uint32 index = stmt->GetIndex();
MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index);
ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query
ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query
m_mStmt->BindParameters(stmt);
@@ -234,7 +230,7 @@ bool MySQLConnection::Execute(PreparedStatementBase* stmt)
if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
{
uint32 lErrno = mysql_errno(m_Mysql);
LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString().c_str(), lErrno, mysql_stmt_error(msql_STMT));
LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT));
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
return Execute(stmt); // Try again
@@ -246,7 +242,7 @@ bool MySQLConnection::Execute(PreparedStatementBase* stmt)
if (mysql_stmt_execute(msql_STMT))
{
uint32 lErrno = mysql_errno(m_Mysql);
LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString().c_str(), lErrno, mysql_stmt_error(msql_STMT));
LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT));
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
return Execute(stmt); // Try again
@@ -255,7 +251,7 @@ bool MySQLConnection::Execute(PreparedStatementBase* stmt)
return false;
}
LOG_DEBUG("sql.sql", "[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString().c_str());
LOG_DEBUG("sql.sql", "[{} ms] SQL(p): {}", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString());
m_mStmt->ClearParameters();
return true;
@@ -282,7 +278,7 @@ bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLPreparedStatement
if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
{
uint32 lErrno = mysql_errno(m_Mysql);
LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString().c_str(), lErrno, mysql_stmt_error(msql_STMT));
LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT));
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
return _Query(stmt, mysqlStmt, pResult, pRowCount, pFieldCount); // Try again
@@ -294,8 +290,7 @@ bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLPreparedStatement
if (mysql_stmt_execute(msql_STMT))
{
uint32 lErrno = mysql_errno(m_Mysql);
LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s",
m_mStmt->getQueryString().c_str(), lErrno, mysql_stmt_error(msql_STMT));
LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT));
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
return _Query(stmt, mysqlStmt, pResult, pRowCount, pFieldCount); // Try again
@@ -304,7 +299,7 @@ bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLPreparedStatement
return false;
}
LOG_DEBUG("sql.sql", "[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString().c_str());
LOG_DEBUG("sql.sql", "[{} ms] SQL(p): {}", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString());
m_mStmt->ClearParameters();
@@ -315,9 +310,9 @@ bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLPreparedStatement
return true;
}
ResultSet* MySQLConnection::Query(char const* sql)
ResultSet* MySQLConnection::Query(std::string_view sql)
{
if (!sql)
if (sql.empty())
return nullptr;
MySQLResult* result = nullptr;
@@ -331,7 +326,7 @@ ResultSet* MySQLConnection::Query(char const* sql)
return new ResultSet(result, fields, rowCount, fieldCount);
}
bool MySQLConnection::_Query(const char* sql, MySQLResult** pResult, MySQLField** pFields, uint64* pRowCount, uint32* pFieldCount)
bool MySQLConnection::_Query(std::string_view sql, MySQLResult** pResult, MySQLField** pFields, uint64* pRowCount, uint32* pFieldCount)
{
if (!m_Mysql)
return false;
@@ -339,26 +334,26 @@ bool MySQLConnection::_Query(const char* sql, MySQLResult** pResult, MySQLField*
{
uint32 _s = getMSTime();
if (mysql_query(m_Mysql, sql))
if (mysql_query(m_Mysql, std::string(sql).c_str()))
{
uint32 lErrno = mysql_errno(m_Mysql);
LOG_INFO("sql.sql", "SQL: %s", sql);
LOG_ERROR("sql.sql", "[%u] %s", lErrno, mysql_error(m_Mysql));
LOG_INFO("sql.sql", "SQL: {}", sql);
LOG_ERROR("sql.sql", "[{}] {}", lErrno, mysql_error(m_Mysql));
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
return _Query(sql, pResult, pFields, pRowCount, pFieldCount); // We try again
return false;
}
else
LOG_DEBUG("sql.sql", "[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql);
LOG_DEBUG("sql.sql", "[{} ms] SQL: {}", getMSTimeDiff(_s, getMSTime()), sql);
*pResult = reinterpret_cast<MySQLResult*>(mysql_store_result(m_Mysql));
*pRowCount = mysql_affected_rows(m_Mysql);
*pFieldCount = mysql_field_count(m_Mysql);
}
if (!*pResult )
if (!*pResult)
return false;
if (!*pRowCount)
@@ -395,18 +390,29 @@ int MySQLConnection::ExecuteTransaction(std::shared_ptr<TransactionBase> transac
BeginTransaction();
for (auto itr = queries.begin(); itr != queries.end(); ++itr)
for (auto const& data : queries)
{
SQLElementData const& data = *itr;
switch (itr->type)
switch (data.type)
{
case SQL_ELEMENT_PREPARED:
{
PreparedStatementBase* stmt = data.element.stmt;
PreparedStatementBase* stmt = nullptr;
try
{
stmt = std::get<PreparedStatementBase*>(data.element);
}
catch (const std::bad_variant_access& ex)
{
LOG_FATAL("sql.sql", "> PreparedStatementBase not found in SQLElementData. {}", ex.what());
ABORT();
}
ASSERT(stmt);
if (!Execute(stmt))
{
LOG_WARN("sql.sql", "Transaction aborted. %u queries not executed.", (uint32)queries.size());
LOG_WARN("sql.sql", "Transaction aborted. {} queries not executed.", queries.size());
int errorCode = GetLastError();
RollbackTransaction();
return errorCode;
@@ -415,12 +421,24 @@ int MySQLConnection::ExecuteTransaction(std::shared_ptr<TransactionBase> transac
break;
case SQL_ELEMENT_RAW:
{
char const* sql = data.element.query;
ASSERT(sql);
std::string sql{};
try
{
sql = std::get<std::string>(data.element);
}
catch (const std::bad_variant_access& ex)
{
LOG_FATAL("sql.sql", "> std::string not found in SQLElementData. {}", ex.what());
ABORT();
}
ASSERT(!sql.empty());
if (!Execute(sql))
{
LOG_WARN("sql.sql", "Transaction aborted. %u queries not executed.", (uint32)queries.size());
int errorCode = GetLastError();
LOG_WARN("sql.sql", "Transaction aborted. {} queries not executed.", queries.size());
uint32 errorCode = GetLastError();
RollbackTransaction();
return errorCode;
}
@@ -470,17 +488,19 @@ uint32 MySQLConnection::GetServerVersion() const
MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index)
{
ASSERT(index < m_stmts.size(), "Tried to access invalid prepared statement index %u (max index " SZFMTD ") on database `%s`, connection type: %s",
index, m_stmts.size(), m_connectionInfo.database.c_str(), (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
ASSERT(index < m_stmts.size(), "Tried to access invalid prepared statement index {} (max index {}) on database `{}`, connection type: {}",
index, m_stmts.size(), m_connectionInfo.database, (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
MySQLPreparedStatement* ret = m_stmts[index].get();
if (!ret)
LOG_ERROR("sql.sql", "Could not fetch prepared statement %u on database `%s`, connection type: %s.",
index, m_connectionInfo.database.c_str(), (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
LOG_ERROR("sql.sql", "Could not fetch prepared statement {} on database `{}`, connection type: {}.",
index, m_connectionInfo.database, (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
return ret;
}
void MySQLConnection::PrepareStatement(uint32 index, std::string const& sql, ConnectionFlags flags)
void MySQLConnection::PrepareStatement(uint32 index, std::string_view sql, ConnectionFlags flags)
{
// Check if specified query should be prepared on this connection
// i.e. don't prepare async statements on synchronous connections
@@ -494,16 +514,16 @@ void MySQLConnection::PrepareStatement(uint32 index, std::string const& sql, Con
MYSQL_STMT* stmt = mysql_stmt_init(m_Mysql);
if (!stmt)
{
LOG_ERROR("sql.sql", "In mysql_stmt_init() id: %u, sql: \"%s\"", index, sql.c_str());
LOG_ERROR("sql.sql", "%s", mysql_error(m_Mysql));
LOG_ERROR("sql.sql", "In mysql_stmt_init() id: {}, sql: \"{}\"", index, sql);
LOG_ERROR("sql.sql", "{}", mysql_error(m_Mysql));
m_prepareError = true;
}
else
{
if (mysql_stmt_prepare(stmt, sql.c_str(), static_cast<unsigned long>(sql.size())))
if (mysql_stmt_prepare(stmt, std::string(sql).c_str(), static_cast<unsigned long>(sql.size())))
{
LOG_ERROR("sql.sql", "In mysql_stmt_prepare() id: %u, sql: \"%s\"", index, sql.c_str());
LOG_ERROR("sql.sql", "%s", mysql_stmt_error(stmt));
LOG_ERROR("sql.sql", "In mysql_stmt_prepare() id: {}, sql: \"{}\"", index, sql);
LOG_ERROR("sql.sql", "{}", mysql_stmt_error(stmt));
mysql_stmt_close(stmt);
m_prepareError = true;
}
@@ -526,6 +546,7 @@ PreparedResultSet* MySQLConnection::Query(PreparedStatementBase* stmt)
{
mysql_next_result(m_Mysql);
}
return new PreparedResultSet(mysqlStmt->GetSTMT(), result, rowCount, fieldCount);
}
@@ -559,12 +580,12 @@ bool MySQLConnection::_HandleMySQLErrno(uint32 errNo, uint8 attempts /*= 5*/)
if (!this->PrepareStatements())
{
LOG_FATAL("sql.sql", "Could not re-prepare statements!");
std::this_thread::sleep_for(std::chrono::seconds(10));
std::this_thread::sleep_for(10s);
std::abort();
}
LOG_INFO("sql.sql", "Successfully reconnected to %s @%s:%s (%s).",
m_connectionInfo.database.c_str(), m_connectionInfo.host.c_str(), m_connectionInfo.port_or_socket.c_str(),
LOG_INFO("sql.sql", "Successfully reconnected to {} @{}:{} ({}).",
m_connectionInfo.database, m_connectionInfo.host, m_connectionInfo.port_or_socket,
(m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
m_reconnecting = false;
@@ -575,24 +596,24 @@ bool MySQLConnection::_HandleMySQLErrno(uint32 errNo, uint8 attempts /*= 5*/)
{
// Shut down the server when the mysql server isn't
// reachable for some time
LOG_FATAL("sql.sql", "Failed to reconnect to the MySQL server, "
"terminating the server to prevent data corruption!");
LOG_FATAL("sql.sql", "Failed to reconnect to the MySQL server, terminating the server to prevent data corruption!");
// We could also initiate a shutdown through using std::raise(SIGTERM)
std::this_thread::sleep_for(std::chrono::seconds(10));
std::this_thread::sleep_for(10s);
std::abort();
}
else
{
// It's possible this attempted reconnect throws 2006 at us.
// To prevent crazy recursive calls, sleep here.
std::this_thread::sleep_for(std::chrono::seconds(3)); // Sleep 3 seconds
std::this_thread::sleep_for(3s); // Sleep 3 seconds
return _HandleMySQLErrno(lErrno, attempts); // Call self (recursive)
}
}
case ER_LOCK_DEADLOCK:
return false; // Implemented in TransactionTask::Execute and DatabaseWorkerPool<T>::DirectCommitTransaction
return false; // Implemented in TransactionTask::Execute and DatabaseWorkerPool<T>::DirectCommitTransaction
// Query related errors - skip query
case ER_WRONG_VALUE_COUNT:
case ER_DUP_ENTRY:
@@ -602,16 +623,16 @@ bool MySQLConnection::_HandleMySQLErrno(uint32 errNo, uint8 attempts /*= 5*/)
case ER_BAD_FIELD_ERROR:
case ER_NO_SUCH_TABLE:
LOG_ERROR("sql.sql", "Your database structure is not up to date. Please make sure you've executed all queries in the sql/updates folders.");
std::this_thread::sleep_for(std::chrono::seconds(10));
std::this_thread::sleep_for(10s);
std::abort();
return false;
case ER_PARSE_ERROR:
LOG_ERROR("sql.sql", "Error while parsing SQL. Core fix required.");
std::this_thread::sleep_for(std::chrono::seconds(10));
std::this_thread::sleep_for(10s);
std::abort();
return false;
default:
LOG_ERROR("sql.sql", "Unhandled MySQL errno %u. Unexpected behaviour possible.", errNo);
LOG_ERROR("sql.sql", "Unhandled MySQL errno {}. Unexpected behaviour possible.", errNo);
return false;
}
}

View File

@@ -42,7 +42,7 @@ enum ConnectionFlags
struct AC_DATABASE_API MySQLConnectionInfo
{
explicit MySQLConnectionInfo(std::string const& infoString);
explicit MySQLConnectionInfo(std::string_view infoString);
std::string user;
std::string password;
@@ -54,7 +54,9 @@ struct AC_DATABASE_API MySQLConnectionInfo
class AC_DATABASE_API MySQLConnection
{
template <class T> friend class DatabaseWorkerPool;
template <class T>
friend class DatabaseWorkerPool;
friend class PingOperation;
public:
@@ -67,11 +69,11 @@ public:
bool PrepareStatements();
bool Execute(char const* sql);
bool Execute(std::string_view sql);
bool Execute(PreparedStatementBase* stmt);
ResultSet* Query(char const* sql);
ResultSet* Query(std::string_view sql);
PreparedResultSet* Query(PreparedStatementBase* stmt);
bool _Query(char const* sql, MySQLResult** pResult, MySQLField** pFields, uint64* pRowCount, uint32* pFieldCount);
bool _Query(std::string_view sql, MySQLResult** pResult, MySQLField** pFields, uint64* pRowCount, uint32* pFieldCount);
bool _Query(PreparedStatementBase* stmt, MySQLPreparedStatement** mysqlStmt, MySQLResult** pResult, uint64* pRowCount, uint32* pFieldCount);
void BeginTransaction();
@@ -91,27 +93,27 @@ protected:
/// Called by parent databasepool. Will let other threads access this connection
void Unlock();
uint32 GetServerVersion() const;
[[nodiscard]] uint32 GetServerVersion() const;
MySQLPreparedStatement* GetPreparedStatement(uint32 index);
void PrepareStatement(uint32 index, std::string const& sql, ConnectionFlags flags);
void PrepareStatement(uint32 index, std::string_view sql, ConnectionFlags flags);
virtual void DoPrepareStatements() = 0;
typedef std::vector<std::unique_ptr<MySQLPreparedStatement>> PreparedStatementContainer;
PreparedStatementContainer m_stmts; //! PreparedStatements storage
bool m_reconnecting; //! Are we reconnecting?
bool m_prepareError; //! Was there any error while preparing statements?
PreparedStatementContainer m_stmts; //! PreparedStatements storage
bool m_reconnecting; //! Are we reconnecting?
bool m_prepareError; //! Was there any error while preparing statements?
private:
bool _HandleMySQLErrno(uint32 errNo, uint8 attempts = 5);
ProducerConsumerQueue<SQLOperation*>* m_queue; //! Queue shared with other asynchronous connections.
std::unique_ptr<DatabaseWorker> m_worker; //! Core worker task.
MySQLHandle* m_Mysql; //! MySQL Handle.
MySQLConnectionInfo& m_connectionInfo; //! Connection info (used for logging)
ConnectionFlags m_connectionFlags; //! Connection flags (for preparing relevant statements)
std::mutex m_Mutex;
MySQLHandle* m_Mysql; //! MySQL Handle.
MySQLConnectionInfo& m_connectionInfo; //! Connection info (used for logging)
ConnectionFlags m_connectionFlags; //! Connection flags (for preparing relevant statements)
std::mutex m_Mutex;
MySQLConnection(MySQLConnection const& right) = delete;
MySQLConnection& operator=(MySQLConnection const& right) = delete;

View File

@@ -35,8 +35,11 @@ template<> struct MySQLType<int64> : std::integral_constant<enum_field_types, MY
template<> struct MySQLType<float> : std::integral_constant<enum_field_types, MYSQL_TYPE_FLOAT> { };
template<> struct MySQLType<double> : std::integral_constant<enum_field_types, MYSQL_TYPE_DOUBLE> { };
MySQLPreparedStatement::MySQLPreparedStatement(MySQLStmt* stmt, std::string queryString) :
m_stmt(nullptr), m_Mstmt(stmt), m_bind(nullptr), m_queryString(std::move(queryString))
MySQLPreparedStatement::MySQLPreparedStatement(MySQLStmt* stmt, std::string_view queryString) :
m_stmt(nullptr),
m_Mstmt(stmt),
m_bind(nullptr),
m_queryString(std::string(queryString))
{
/// Initialize variable parameters
m_paramCount = mysql_stmt_param_count(stmt);
@@ -57,6 +60,7 @@ MySQLPreparedStatement::~MySQLPreparedStatement()
delete[] m_Mstmt->bind->length;
delete[] m_Mstmt->bind->is_null;
}
mysql_stmt_close(m_Mstmt);
delete[] m_bind;
}
@@ -72,11 +76,13 @@ void MySQLPreparedStatement::BindParameters(PreparedStatementBase* stmt)
{
SetParameter(pos, param);
}, data.data);
++pos;
}
#ifdef _DEBUG
if (pos < m_paramCount)
LOG_WARN("sql.sql", "[WARNING]: BindParameters() for statement %u did not bind all allocated parameters", stmt->GetIndex());
LOG_WARN("sql.sql", "[WARNING]: BindParameters() for statement {} did not bind all allocated parameters", stmt->GetIndex());
#endif
}
@@ -86,7 +92,7 @@ void MySQLPreparedStatement::ClearParameters()
{
delete m_bind[i].length;
m_bind[i].length = nullptr;
delete[] (char*) m_bind[i].buffer;
delete[] (char*)m_bind[i].buffer;
m_bind[i].buffer = nullptr;
m_paramsSet[i] = false;
}
@@ -94,7 +100,9 @@ void MySQLPreparedStatement::ClearParameters()
static bool ParamenterIndexAssertFail(uint32 stmtIndex, uint8 index, uint32 paramCount)
{
LOG_ERROR("sql.driver", "Attempted to bind parameter %u%s on a PreparedStatement %u (statement has only %u parameters)", uint32(index) + 1, (index == 1 ? "st" : (index == 2 ? "nd" : (index == 3 ? "rd" : "nd"))), stmtIndex, paramCount);
LOG_ERROR("sql.driver", "Attempted to bind parameter {}{} on a PreparedStatement {} (statement has only {} parameters)",
uint32(index) + 1, (index == 1 ? "st" : (index == 2 ? "nd" : (index == 3 ? "rd" : "nd"))), stmtIndex, paramCount);
return false;
}
@@ -104,10 +112,33 @@ void MySQLPreparedStatement::AssertValidIndex(uint8 index)
ASSERT(index < m_paramCount || ParamenterIndexAssertFail(m_stmt->GetIndex(), index, m_paramCount));
if (m_paramsSet[index])
LOG_ERROR("sql.sql", "[ERROR] Prepared Statement (id: %u) trying to bind value on already bound index (%u).", m_stmt->GetIndex(), index);
LOG_ERROR("sql.sql", "[ERROR] Prepared Statement (id: {}) trying to bind value on already bound index ({}).", m_stmt->GetIndex(), index);
}
void MySQLPreparedStatement::SetParameter(uint8 index, std::nullptr_t)
template<typename T>
void MySQLPreparedStatement::SetParameter(const uint8 index, T value)
{
AssertValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
uint32 len = uint32(sizeof(T));
param->buffer_type = MySQLType<T>::value;
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 = std::is_unsigned_v<T>;
memcpy(param->buffer, &value, len);
}
void MySQLPreparedStatement::SetParameter(const uint8 index, bool value)
{
SetParameter(index, uint8(value ? 1 : 0));
}
void MySQLPreparedStatement::SetParameter(const uint8 index, std::nullptr_t /*value*/)
{
AssertValidIndex(index);
m_paramsSet[index] = true;
@@ -121,29 +152,6 @@ void MySQLPreparedStatement::SetParameter(uint8 index, std::nullptr_t)
param->length = nullptr;
}
void MySQLPreparedStatement::SetParameter(uint8 index, bool value)
{
SetParameter(index, uint8(value ? 1 : 0));
}
template<typename T>
void MySQLPreparedStatement::SetParameter(uint8 index, T value)
{
AssertValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
uint32 len = uint32(sizeof(T));
param->buffer_type = MySQLType<T>::value;
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 = std::is_unsigned_v<T>;
memcpy(param->buffer, &value, len);
}
void MySQLPreparedStatement::SetParameter(uint8 index, std::string const& value)
{
AssertValidIndex(index);
@@ -151,7 +159,7 @@ void MySQLPreparedStatement::SetParameter(uint8 index, std::string const& value)
MYSQL_BIND* param = &m_bind[index];
uint32 len = uint32(value.size());
param->buffer_type = MYSQL_TYPE_VAR_STRING;
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;
@@ -168,7 +176,7 @@ void MySQLPreparedStatement::SetParameter(uint8 index, std::vector<uint8> const&
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;
@@ -183,6 +191,7 @@ std::string MySQLPreparedStatement::getQueryString() const
std::string queryString(m_queryString);
size_t pos = 0;
for (PreparedStatementData const& data : m_stmt->GetParameters())
{
pos = queryString.find('?', pos);

View File

@@ -36,7 +36,7 @@ friend class MySQLConnection;
friend class PreparedStatementBase;
public:
MySQLPreparedStatement(MySQLStmt* stmt, std::string queryString);
MySQLPreparedStatement(MySQLStmt* stmt, std::string_view queryString);
~MySQLPreparedStatement();
void BindParameters(PreparedStatementBase* stmt);
@@ -44,18 +44,19 @@ public:
uint32 GetParameterCount() const { return m_paramCount; }
protected:
void SetParameter(uint8 index, std::nullptr_t);
void SetParameter(uint8 index, bool value);
void SetParameter(const uint8 index, bool value);
void SetParameter(const uint8 index, std::nullptr_t /*value*/);
void SetParameter(const uint8 index, std::string const& value);
void SetParameter(const uint8 index, std::vector<uint8> const& value);
template<typename T>
void SetParameter(uint8 index, T value);
void SetParameter(uint8 index, std::string const& value);
void SetParameter(uint8 index, std::vector<uint8> const& value);
void SetParameter(const uint8 index, T value);
MySQLStmt* GetSTMT() { return m_Mstmt; }
MySQLBind* GetBind() { return m_bind; }
PreparedStatementBase* m_stmt;
void ClearParameters();
void AssertValidIndex(uint8 index);
void AssertValidIndex(const uint8 index);
std::string getQueryString() const;
private:
@@ -63,7 +64,7 @@ private:
uint32 m_paramCount;
std::vector<bool> m_paramsSet;
MySQLBind* m_bind;
std::string const m_queryString;
std::string m_queryString{};
MySQLPreparedStatement(MySQLPreparedStatement const& right) = delete;
MySQLPreparedStatement& operator=(MySQLPreparedStatement const& right) = delete;

View File

@@ -24,106 +24,52 @@
#include "QueryResult.h"
PreparedStatementBase::PreparedStatementBase(uint32 index, uint8 capacity) :
m_index(index), statement_data(capacity) { }
m_index(index),
statement_data(capacity) { }
PreparedStatementBase::~PreparedStatementBase() { }
//- Bind to buffer
void PreparedStatementBase::setBool(const uint8 index, const bool value)
template<typename T>
Acore::Types::is_non_string_view_v<T> PreparedStatementBase::SetValidData(const uint8 index, T const& value)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
statement_data[index].data.emplace<T>(value);
}
void PreparedStatementBase::setUInt8(const uint8 index, const uint8 value)
// Non template functions
void PreparedStatementBase::SetValidData(const uint8 index)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
statement_data[index].data.emplace<std::nullptr_t>(nullptr);
}
void PreparedStatementBase::setUInt16(const uint8 index, const uint16 value)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatementBase::setUInt32(const uint8 index, const uint32 value)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatementBase::setUInt64(const uint8 index, const uint64 value)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatementBase::setInt8(const uint8 index, const int8 value)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatementBase::setInt16(const uint8 index, const int16 value)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatementBase::setInt32(const uint8 index, const int32 value)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatementBase::setInt64(const uint8 index, const int64 value)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatementBase::setFloat(const uint8 index, const float value)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatementBase::setDouble(const uint8 index, const double value)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatementBase::setString(const uint8 index, const std::string& value)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatementBase::setStringView(const uint8 index, const std::string_view value)
void PreparedStatementBase::SetValidData(const uint8 index, std::string_view value)
{
ASSERT(index < statement_data.size());
statement_data[index].data.emplace<std::string>(value);
}
void PreparedStatementBase::setBinary(const uint8 index, const std::vector<uint8>& value)
{
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatementBase::setNull(const uint8 index)
{
ASSERT(index < statement_data.size());
statement_data[index].data = nullptr;
}
template void PreparedStatementBase::SetValidData(const uint8 index, uint8 const& value);
template void PreparedStatementBase::SetValidData(const uint8 index, int8 const& value);
template void PreparedStatementBase::SetValidData(const uint8 index, uint16 const& value);
template void PreparedStatementBase::SetValidData(const uint8 index, int16 const& value);
template void PreparedStatementBase::SetValidData(const uint8 index, uint32 const& value);
template void PreparedStatementBase::SetValidData(const uint8 index, int32 const& value);
template void PreparedStatementBase::SetValidData(const uint8 index, uint64 const& value);
template void PreparedStatementBase::SetValidData(const uint8 index, int64 const& value);
template void PreparedStatementBase::SetValidData(const uint8 index, bool const& value);
template void PreparedStatementBase::SetValidData(const uint8 index, float const& value);
template void PreparedStatementBase::SetValidData(const uint8 index, std::string const& value);
template void PreparedStatementBase::SetValidData(const uint8 index, std::vector<uint8> const& value);
//- Execution
PreparedStatementTask::PreparedStatementTask(PreparedStatementBase* stmt, bool async) :
m_stmt(stmt), m_result(nullptr)
m_stmt(stmt),
m_result(nullptr)
{
m_has_result = async; // If it's async, then there's a result
if (async)
m_result = new PreparedQueryResultPromise();
}
@@ -131,7 +77,8 @@ m_stmt(stmt), m_result(nullptr)
PreparedStatementTask::~PreparedStatementTask()
{
delete m_stmt;
if (m_has_result && m_result != nullptr)
if (m_has_result && m_result)
delete m_result;
}
@@ -146,6 +93,7 @@ bool PreparedStatementTask::Execute()
m_result->set_value(PreparedQueryResult(nullptr));
return false;
}
m_result->set_value(PreparedQueryResult(result));
return true;
}
@@ -156,45 +104,29 @@ bool PreparedStatementTask::Execute()
template<typename T>
std::string PreparedStatementData::ToString(T value)
{
return fmt::format("{}", value);
return Acore::StringFormatFmt("{}", value);
}
std::string PreparedStatementData::ToString(bool value)
{
return ToString<uint32>(value);
}
std::string PreparedStatementData::ToString(uint8 value)
{
return ToString<uint32>(value);
}
template std::string PreparedStatementData::ToString<uint16>(uint16);
template std::string PreparedStatementData::ToString<uint32>(uint32);
template std::string PreparedStatementData::ToString<uint64>(uint64);
std::string PreparedStatementData::ToString(int8 value)
{
return ToString<int32>(value);
}
template std::string PreparedStatementData::ToString<int16>(int16);
template std::string PreparedStatementData::ToString<int32>(int32);
template std::string PreparedStatementData::ToString<int64>(int64);
template std::string PreparedStatementData::ToString<float>(float);
template std::string PreparedStatementData::ToString<double>(double);
std::string PreparedStatementData::ToString(std::string const& value)
{
return fmt::format("'{}'", value);
}
std::string PreparedStatementData::ToString(std::vector<uint8> const& /*value*/)
template<>
std::string PreparedStatementData::ToString(std::vector<uint8> /*value*/)
{
return "BINARY";
}
std::string PreparedStatementData::ToString(std::nullptr_t)
template std::string PreparedStatementData::ToString(uint8);
template std::string PreparedStatementData::ToString(uint16);
template std::string PreparedStatementData::ToString(uint32);
template std::string PreparedStatementData::ToString(uint64);
template std::string PreparedStatementData::ToString(int8);
template std::string PreparedStatementData::ToString(int16);
template std::string PreparedStatementData::ToString(int32);
template std::string PreparedStatementData::ToString(int64);
template std::string PreparedStatementData::ToString(std::string);
template std::string PreparedStatementData::ToString(float);
template std::string PreparedStatementData::ToString(double);
template std::string PreparedStatementData::ToString(bool);
std::string PreparedStatementData::ToString(std::nullptr_t /*value*/)
{
return "NULL";
}

View File

@@ -19,11 +19,26 @@
#define _PREPAREDSTATEMENT_H
#include "Define.h"
#include "Duration.h"
#include "Optional.h"
#include "SQLOperation.h"
#include <future>
#include <tuple>
#include <variant>
#include <vector>
namespace Acore::Types
{
template <typename T>
using is_default = std::enable_if_t<std::is_arithmetic_v<T> || std::is_same_v<std::vector<uint8>, T>>;
template <typename T>
using is_enum_v = std::enable_if_t<std::is_enum_v<T>>;
template <typename T>
using is_non_string_view_v = std::enable_if_t<!std::is_base_of_v<std::string_view, T>>;
}
struct PreparedStatementData
{
std::variant<
@@ -46,12 +61,7 @@ struct PreparedStatementData
template<typename T>
static std::string ToString(T value);
static std::string ToString(bool value);
static std::string ToString(uint8 value);
static std::string ToString(int8 value);
static std::string ToString(std::string const& value);
static std::string ToString(std::vector<uint8> const& value);
static std::string ToString(std::nullptr_t);
static std::string ToString(std::nullptr_t /*value*/);
};
//- Upper-level class that is used in code
@@ -63,32 +73,77 @@ public:
explicit PreparedStatementBase(uint32 index, uint8 capacity);
virtual ~PreparedStatementBase();
void setNull(const uint8 index);
void setBool(const uint8 index, const bool value);
void setUInt8(const uint8 index, const uint8 value);
void setUInt16(const uint8 index, const uint16 value);
void setUInt32(const uint8 index, const uint32 value);
void setUInt64(const uint8 index, const uint64 value);
void setInt8(const uint8 index, const int8 value);
void setInt16(const uint8 index, const int16 value);
void setInt32(const uint8 index, const int32 value);
void setInt64(const uint8 index, const int64 value);
void setFloat(const uint8 index, const float value);
void setDouble(const uint8 index, const double value);
void setString(const uint8 index, const std::string& value);
void setStringView(const uint8 index, const std::string_view value);
void setBinary(const uint8 index, const std::vector<uint8>& value);
template <size_t Size>
void setBinary(const uint8 index, std::array<uint8, Size> const& value)
// Set numerlic and default binary
template<typename T>
inline Acore::Types::is_default<T> SetData(const uint8 index, T value)
{
std::vector<uint8> vec(value.begin(), value.end());
setBinary(index, vec);
SetValidData(index, value);
}
uint32 GetIndex() const { return m_index; }
std::vector<PreparedStatementData> const& GetParameters() const { return statement_data; }
// Set enums
template<typename T>
inline Acore::Types::is_enum_v<T> SetData(const uint8 index, T value)
{
SetValidData(index, std::underlying_type_t<T>(value));
}
// Set string_view
inline void SetData(const uint8 index, std::string_view value)
{
SetValidData(index, value);
}
// Set nullptr
inline void SetData(const uint8 index, std::nullptr_t = nullptr)
{
SetValidData(index);
}
// Set non default binary
template<std::size_t Size>
inline void SetData(const uint8 index, std::array<uint8, Size> const& value)
{
std::vector<uint8> vec(value.begin(), value.end());
SetValidData(index, vec);
}
// Set duration
template<class _Rep, class _Period>
inline void SetData(const uint8 index, std::chrono::duration<_Rep, _Period> const& value, bool convertToUin32 = true)
{
SetValidData(index, convertToUin32 ? static_cast<uint32>(value.count()) : value.count());
}
// Set all
template <typename... Args>
inline void SetArguments(Args&&... args)
{
SetDataTuple(std::make_tuple(std::forward<Args>(args)...));
}
[[nodiscard]] uint32 GetIndex() const { return m_index; }
[[nodiscard]] std::vector<PreparedStatementData> const& GetParameters() const { return statement_data; }
protected:
template<typename T>
Acore::Types::is_non_string_view_v<T> SetValidData(const uint8 index, T const& value);
void SetValidData(const uint8 index);
void SetValidData(const uint8 index, std::string_view value);
template<typename... Ts>
inline void SetDataTuple(std::tuple<Ts...> const& argsList)
{
std::apply
(
[this](Ts const&... arguments)
{
uint8 index{ 0 };
((SetData(index, arguments), index++), ...);
}, argsList
);
}
uint32 m_index;
//- Buffer of parameters, not tied to MySQL in any way yet
@@ -116,7 +171,7 @@ class AC_DATABASE_API PreparedStatementTask : public SQLOperation
{
public:
PreparedStatementTask(PreparedStatementBase* stmt, bool async = false);
~PreparedStatementTask();
~PreparedStatementTask() override;
bool Execute() override;
PreparedQueryResultFuture GetFuture() { return m_result->get_future(); }

View File

@@ -16,6 +16,7 @@
*/
#include "QueryCallback.h"
#include "Duration.h"
#include "Errors.h"
template<typename T, typename... Args>
@@ -66,13 +67,15 @@ public:
QueryCallbackData(std::function<void(QueryCallback&, QueryResult)>&& callback) : _string(std::move(callback)), _isPrepared(false) { }
QueryCallbackData(std::function<void(QueryCallback&, PreparedQueryResult)>&& callback) : _prepared(std::move(callback)), _isPrepared(true) { }
QueryCallbackData(QueryCallbackData&& right)
QueryCallbackData(QueryCallbackData&& right) noexcept
{
_isPrepared = right._isPrepared;
ConstructActiveMember(this);
MoveFrom(this, std::move(right));
}
QueryCallbackData& operator=(QueryCallbackData&& right)
QueryCallbackData& operator=(QueryCallbackData&& right) noexcept
{
if (this != &right)
{
@@ -82,10 +85,13 @@ public:
_isPrepared = right._isPrepared;
ConstructActiveMember(this);
}
MoveFrom(this, std::move(right));
}
return *this;
}
~QueryCallbackData() { DestroyActiveMember(this); }
private:
@@ -105,19 +111,19 @@ private:
};
// Not using initialization lists to work around segmentation faults when compiling with clang without precompiled headers
QueryCallback::QueryCallback(std::future<QueryResult>&& result)
QueryCallback::QueryCallback(QueryResultFuture&& result)
{
_isPrepared = false;
Construct(_string, std::move(result));
}
QueryCallback::QueryCallback(std::future<PreparedQueryResult>&& result)
QueryCallback::QueryCallback(PreparedQueryResultFuture&& result)
{
_isPrepared = true;
Construct(_prepared, std::move(result));
}
QueryCallback::QueryCallback(QueryCallback&& right)
QueryCallback::QueryCallback(QueryCallback&& right) noexcept
{
_isPrepared = right._isPrepared;
ConstructActiveMember(this);
@@ -125,7 +131,7 @@ QueryCallback::QueryCallback(QueryCallback&& right)
_callbacks = std::move(right._callbacks);
}
QueryCallback& QueryCallback::operator=(QueryCallback&& right)
QueryCallback& QueryCallback::operator=(QueryCallback&& right) noexcept
{
if (this != &right)
{
@@ -135,9 +141,11 @@ QueryCallback& QueryCallback::operator=(QueryCallback&& right)
_isPrepared = right._isPrepared;
ConstructActiveMember(this);
}
MoveFrom(this, std::move(right));
_callbacks = std::move(right._callbacks);
}
return *this;
}
@@ -198,7 +206,7 @@ bool QueryCallback::InvokeIfReady()
if (!_isPrepared)
{
if (_string.valid() && _string.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
if (_string.valid() && _string.wait_for(0s) == std::future_status::ready)
{
QueryResultFuture f(std::move(_string));
std::function<void(QueryCallback&, QueryResult)> cb(std::move(callback._string));
@@ -208,7 +216,7 @@ bool QueryCallback::InvokeIfReady()
}
else
{
if (_prepared.valid() && _prepared.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
if (_prepared.valid() && _prepared.wait_for(0s) == std::future_status::ready)
{
PreparedQueryResultFuture f(std::move(_prepared));
std::function<void(QueryCallback&, PreparedQueryResult)> cb(std::move(callback._prepared));

View File

@@ -31,8 +31,9 @@ class AC_DATABASE_API QueryCallback
public:
explicit QueryCallback(QueryResultFuture&& result);
explicit QueryCallback(PreparedQueryResultFuture&& result);
QueryCallback(QueryCallback&& right);
QueryCallback& operator=(QueryCallback&& right);
QueryCallback(QueryCallback&& right) noexcept;
QueryCallback& operator=(QueryCallback&& right) noexcept;
~QueryCallback();
QueryCallback&& WithCallback(std::function<void(QueryResult)>&& callback);
@@ -60,6 +61,7 @@ private:
QueryResultFuture _string;
PreparedQueryResultFuture _prepared;
};
bool _isPrepared;
struct QueryCallbackData;

View File

@@ -26,7 +26,7 @@ bool SQLQueryHolderBase::SetPreparedQueryImpl(size_t index, PreparedStatementBas
{
if (m_queries.size() <= index)
{
LOG_ERROR("sql.sql", "Query index (%u) out of range (size: %u) for prepared statement", uint32(index), (uint32)m_queries.size());
LOG_ERROR("sql.sql", "Query index ({}) out of range (size: {}) for prepared statement", uint32(index), (uint32)m_queries.size());
return false;
}
@@ -37,7 +37,7 @@ bool SQLQueryHolderBase::SetPreparedQueryImpl(size_t index, PreparedStatementBas
PreparedQueryResult SQLQueryHolderBase::GetPreparedResult(size_t index) const
{
// Don't call to this function if the index is of a prepared statement
ASSERT(index < m_queries.size(), "Query holder result index out of range, tried to access index " SZFMTD " but there are only " SZFMTD " results",
ASSERT(index < m_queries.size(), "Query holder result index out of range, tried to access index {} but there are only {} results",
index, m_queries.size());
return m_queries[index].second;

View File

@@ -24,8 +24,7 @@
class AC_DATABASE_API SQLQueryHolderBase
{
friend class SQLQueryHolderTask;
private:
std::vector<std::pair<PreparedStatementBase*, PreparedQueryResult>> m_queries;
public:
SQLQueryHolderBase() = default;
virtual ~SQLQueryHolderBase();
@@ -35,6 +34,9 @@ public:
protected:
bool SetPreparedQueryImpl(size_t index, PreparedStatementBase* stmt);
private:
std::vector<std::pair<PreparedStatementBase*, PreparedQueryResult>> m_queries;
};
template<typename T>
@@ -49,10 +51,6 @@ public:
class AC_DATABASE_API SQLQueryHolderTask : public SQLOperation
{
private:
std::shared_ptr<SQLQueryHolderBase> m_holder;
QueryResultHolderPromise m_result;
public:
explicit SQLQueryHolderTask(std::shared_ptr<SQLQueryHolderBase> holder)
: m_holder(std::move(holder)) { }
@@ -61,6 +59,10 @@ public:
bool Execute() override;
QueryResultHolderFuture GetFuture() { return m_result.get_future(); }
private:
std::shared_ptr<SQLQueryHolderBase> m_holder;
QueryResultHolderPromise m_result;
};
class AC_DATABASE_API SQLQueryHolderCallback
@@ -70,7 +72,6 @@ public:
: m_holder(std::move(holder)), m_future(std::move(future)) { }
SQLQueryHolderCallback(SQLQueryHolderCallback&&) = default;
SQLQueryHolderCallback& operator=(SQLQueryHolderCallback&&) = default;
void AfterComplete(std::function<void(SQLQueryHolderBase const&)> callback) &

View File

@@ -24,154 +24,155 @@
namespace
{
static uint32 SizeForType(MYSQL_FIELD* field)
{
switch (field->type)
static uint32 SizeForType(MYSQL_FIELD* field)
{
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;
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_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_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_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:
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:
LOG_WARN("sql.sql", "MysqlTypeToFieldType(): invalid field type %u", uint32(type));
break;
case MYSQL_TYPE_GEOMETRY:
/*
Following types are not sent over the wire:
MYSQL_TYPE_ENUM:
MYSQL_TYPE_SET:
*/
default:
LOG_WARN("sql.sql", "SQL::SizeForType(): invalid field type {}", uint32(field->type));
return 0;
}
}
return DatabaseFieldTypes::Null;
}
static char const* FieldTypeToString(enum_field_types type)
{
switch (type)
DatabaseFieldTypes MysqlTypeToFieldType(enum_field_types 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-";
}
}
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:
LOG_WARN("sql.sql", "MysqlTypeToFieldType(): invalid field type {}", uint32(type));
break;
}
void InitializeDatabaseFieldMetadata(QueryResultFieldMetadata* meta, MySQLField const* field, uint32 fieldIndex)
{
meta->TableName = field->org_table;
meta->TableAlias = field->table;
meta->Name = field->org_name;
meta->Alias = field->name;
meta->TypeName = FieldTypeToString(field->type);
meta->Index = fieldIndex;
meta->Type = MysqlTypeToFieldType(field->type);
}
return DatabaseFieldTypes::Null;
}
static std::string 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 InitializeDatabaseFieldMetadata(QueryResultFieldMetadata* meta, MySQLField const* field, uint32 fieldIndex)
{
meta->TableName = field->org_table;
meta->TableAlias = field->table;
meta->Name = field->org_name;
meta->Alias = field->name;
meta->TypeName = FieldTypeToString(field->type);
meta->Index = fieldIndex;
meta->Type = MysqlTypeToFieldType(field->type);
}
}
ResultSet::ResultSet(MySQLResult* result, MySQLField* fields, uint64 rowCount, uint32 fieldCount) :
_rowCount(rowCount),
_fieldCount(fieldCount),
_result(result),
_fields(fields)
_rowCount(rowCount),
_fieldCount(fieldCount),
_result(result),
_fields(fields)
{
_fieldMetadata.resize(_fieldCount);
_currentRow = new Field[_fieldCount];
for (uint32 i = 0; i < _fieldCount; i++)
{
InitializeDatabaseFieldMetadata(&_fieldMetadata[i], &_fields[i], i);
@@ -179,13 +180,78 @@ _fields(fields)
}
}
ResultSet::~ResultSet()
{
CleanUp();
}
bool ResultSet::NextRow()
{
MYSQL_ROW row;
if (!_result)
return false;
row = mysql_fetch_row(_result);
if (!row)
{
CleanUp();
return false;
}
unsigned long* lengths = mysql_fetch_lengths(_result);
if (!lengths)
{
LOG_WARN("sql.sql", "{}:mysql_fetch_lengths, cannot retrieve value lengths. Error {}.", __FUNCTION__, mysql_error(_result->handle));
CleanUp();
return false;
}
for (uint32 i = 0; i < _fieldCount; i++)
_currentRow[i].SetStructuredValue(row[i], lengths[i]);
return true;
}
std::string ResultSet::GetFieldName(uint32 index) const
{
ASSERT(index < _fieldCount);
return _fields[index].name;
}
void ResultSet::CleanUp()
{
if (_currentRow)
{
delete[] _currentRow;
_currentRow = nullptr;
}
if (_result)
{
mysql_free_result(_result);
_result = nullptr;
}
}
Field const& ResultSet::operator[](std::size_t index) const
{
ASSERT(index < _fieldCount);
return _currentRow[index];
}
void ResultSet::AssertRows(std::size_t sizeRows)
{
ASSERT(sizeRows == _fieldCount);
}
PreparedResultSet::PreparedResultSet(MySQLStmt* stmt, MySQLResult* result, uint64 rowCount, uint32 fieldCount) :
m_rowCount(rowCount),
m_rowPosition(0),
m_fieldCount(fieldCount),
m_rBind(nullptr),
m_stmt(stmt),
m_metadataResult(result)
m_rowCount(rowCount),
m_rowPosition(0),
m_fieldCount(fieldCount),
m_rBind(nullptr),
m_stmt(stmt),
m_metadataResult(result)
{
if (!m_metadataResult)
return;
@@ -211,7 +277,7 @@ m_metadataResult(result)
//- This is where we store the (entire) resultset
if (mysql_stmt_store_result(m_stmt))
{
LOG_WARN("sql.sql", "%s:mysql_stmt_store_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt));
LOG_WARN("sql.sql", "{}:mysql_stmt_store_result, cannot bind result from MySQL server. Error: {}", __FUNCTION__, mysql_stmt_error(m_stmt));
delete[] m_rBind;
delete[] m_isNull;
delete[] m_length;
@@ -224,6 +290,7 @@ m_metadataResult(result)
MySQLField* field = reinterpret_cast<MySQLField*>(mysql_fetch_fields(m_metadataResult));
m_fieldMetadata.resize(m_fieldCount);
std::size_t rowSize = 0;
for (uint32 i = 0; i < m_fieldCount; ++i)
{
uint32 size = SizeForType(&field[i]);
@@ -249,7 +316,7 @@ m_metadataResult(result)
//- This is where we bind the bind the buffer to the statement
if (mysql_stmt_bind_result(m_stmt, m_rBind))
{
LOG_WARN("sql.sql", "%s:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt));
LOG_WARN("sql.sql", "{}:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: {}", __FUNCTION__, mysql_stmt_error(m_stmt));
mysql_stmt_free_result(m_stmt);
CleanUp();
delete[] m_isNull;
@@ -258,6 +325,7 @@ m_metadataResult(result)
}
m_rows.resize(uint32(m_rowCount) * m_fieldCount);
while (_NextRow())
{
for (uint32 fIndex = 0; fIndex < m_fieldCount; ++fIndex)
@@ -271,90 +339,49 @@ m_metadataResult(result)
void* buffer = m_stmt->bind[fIndex].buffer;
switch (m_rBind[fIndex].buffer_type)
{
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:
// warning - the string will not be null-terminated if there is no space for it in the buffer
// when mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED
// we cannot blindly null-terminate the data either as it may be retrieved as binary blob and not specifically a string
// in this case using Field::GetCString will result in garbage
// TODO: remove Field::GetCString and use std::string_view in C++17
if (fetched_length < buffer_length)
*((char*)buffer + fetched_length) = '\0';
break;
default:
break;
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:
// warning - the string will not be null-terminated if there is no space for it in the buffer
// when mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED
// we cannot blindly null-terminate the data either as it may be retrieved as binary blob and not specifically a string
// in this case using Field::GetCString will result in garbage
// TODO: remove Field::GetCString and use std::string_view in C++17
if (fetched_length < buffer_length)
*((char*)buffer + fetched_length) = '\0';
break;
default:
break;
}
m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue(
(char const*)buffer,
fetched_length);
m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue((char const*)buffer, fetched_length);
// move buffer pointer to next part
m_stmt->bind[fIndex].buffer = (char*)buffer + rowSize;
}
else
{
m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue(
nullptr,
*m_rBind[fIndex].length);
m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue(nullptr, *m_rBind[fIndex].length);
}
}
m_rowPosition++;
}
m_rowPosition = 0;
/// All data is buffered, let go of mysql c api structures
mysql_stmt_free_result(m_stmt);
}
ResultSet::~ResultSet()
{
CleanUp();
}
PreparedResultSet::~PreparedResultSet()
{
CleanUp();
}
bool ResultSet::NextRow()
{
MYSQL_ROW row;
if (!_result)
return false;
row = mysql_fetch_row(_result);
if (!row)
{
CleanUp();
return false;
}
unsigned long* lengths = mysql_fetch_lengths(_result);
if (!lengths)
{
LOG_WARN("sql.sql", "%s:mysql_fetch_lengths, cannot retrieve value lengths. Error %s.", __FUNCTION__, mysql_error(_result->handle));
CleanUp();
return false;
}
for (uint32 i = 0; i < _fieldCount; i++)
_currentRow[i].SetStructuredValue(row[i], lengths[i]);
return true;
}
std::string ResultSet::GetFieldName(uint32 index) const
{
ASSERT(index < _fieldCount);
return _fields[index].name;
}
bool PreparedResultSet::NextRow()
{
/// Only updates the m_rowPosition so upper level code knows in which element
@@ -376,27 +403,6 @@ bool PreparedResultSet::_NextRow()
return retval == 0 || retval == MYSQL_DATA_TRUNCATED;
}
void ResultSet::CleanUp()
{
if (_currentRow)
{
delete [] _currentRow;
_currentRow = nullptr;
}
if (_result)
{
mysql_free_result(_result);
_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);
@@ -422,3 +428,9 @@ void PreparedResultSet::CleanUp()
m_rBind = nullptr;
}
}
void PreparedResultSet::AssertRows(std::size_t sizeRows)
{
ASSERT(m_rowPosition < m_rowCount);
ASSERT(sizeRows == m_fieldCount, "> Tuple size != count fields");
}

View File

@@ -20,6 +20,8 @@
#include "DatabaseEnvFwd.h"
#include "Define.h"
#include "Field.h"
#include <tuple>
#include <vector>
class AC_DATABASE_API ResultSet
@@ -29,13 +31,29 @@ public:
~ResultSet();
bool NextRow();
uint64 GetRowCount() const { return _rowCount; }
uint32 GetFieldCount() const { return _fieldCount; }
std::string GetFieldName(uint32 index) const;
[[nodiscard]] uint64 GetRowCount() const { return _rowCount; }
[[nodiscard]] uint32 GetFieldCount() const { return _fieldCount; }
[[nodiscard]] std::string GetFieldName(uint32 index) const;
Field* Fetch() const { return _currentRow; }
[[nodiscard]] Field* Fetch() const { return _currentRow; }
Field const& operator[](std::size_t index) const;
template<typename... Ts>
inline std::tuple<Ts...> FetchTuple()
{
AssertRows(sizeof...(Ts));
std::tuple<Ts...> theTuple = {};
std::apply([this](Ts&... args)
{
uint8 index{ 0 };
((args = _currentRow[index].Get<Ts>(), index++), ...);
}, theTuple);
return theTuple;
}
protected:
std::vector<QueryResultFieldMetadata> _fieldMetadata;
uint64 _rowCount;
@@ -44,6 +62,8 @@ protected:
private:
void CleanUp();
void AssertRows(std::size_t sizeRows);
MySQLResult* _result;
MySQLField* _fields;
@@ -58,12 +78,28 @@ public:
~PreparedResultSet();
bool NextRow();
uint64 GetRowCount() const { return m_rowCount; }
uint32 GetFieldCount() const { return m_fieldCount; }
[[nodiscard]] uint64 GetRowCount() const { return m_rowCount; }
[[nodiscard]] uint32 GetFieldCount() const { return m_fieldCount; }
Field* Fetch() const;
[[nodiscard]] Field* Fetch() const;
Field const& operator[](std::size_t index) const;
template<typename... Ts>
inline std::tuple<Ts...> FetchTuple()
{
AssertRows(sizeof...(Ts));
std::tuple<Ts...> theTuple = {};
std::apply([this](Ts&... args)
{
uint8 index{ 0 };
((args = m_rows[uint32(m_rowPosition) * m_fieldCount + index].Get<Ts>(), index++), ...);
}, theTuple);
return theTuple;
}
protected:
std::vector<QueryResultFieldMetadata> m_fieldMetadata;
std::vector<Field> m_rows;
@@ -79,6 +115,8 @@ private:
void CleanUp();
bool _NextRow();
void AssertRows(std::size_t sizeRows);
PreparedResultSet(PreparedResultSet const& right) = delete;
PreparedResultSet& operator=(PreparedResultSet const& right) = delete;
};

View File

@@ -20,13 +20,7 @@
#include "DatabaseEnvFwd.h"
#include "Define.h"
//- Union that holds element data
union SQLElementUnion
{
PreparedStatementBase* stmt;
char const* query;
};
#include <variant>
//- Type specifier of our element data
enum SQLElementDataType
@@ -38,7 +32,7 @@ enum SQLElementDataType
//- The element
struct SQLElementData
{
SQLElementUnion element;
std::variant<PreparedStatementBase*, std::string> element;
SQLElementDataType type;
};
@@ -47,18 +41,19 @@ class MySQLConnection;
class AC_DATABASE_API SQLOperation
{
public:
SQLOperation(): m_conn(nullptr) { }
virtual ~SQLOperation() { }
SQLOperation() = default;
virtual ~SQLOperation() = default;
virtual int call()
{
Execute();
return 0;
}
virtual bool Execute() = 0;
virtual void SetConnection(MySQLConnection* con) { m_conn = con; }
MySQLConnection* m_conn;
MySQLConnection* m_conn{nullptr};
private:
SQLOperation(SQLOperation const& right) = delete;

View File

@@ -16,6 +16,7 @@
*/
#include "Transaction.h"
#include "Errors.h"
#include "Log.h"
#include "MySQLConnection.h"
#include "PreparedStatement.h"
@@ -26,24 +27,24 @@
std::mutex TransactionTask::_deadlockLock;
#define DEADLOCK_MAX_RETRY_TIME_MS 60000
constexpr Milliseconds DEADLOCK_MAX_RETRY_TIME_MS = 1min;
//- Append a raw ad-hoc query to the transaction
void TransactionBase::Append(char const* sql)
void TransactionBase::Append(std::string_view sql)
{
SQLElementData data;
SQLElementData data = {};
data.type = SQL_ELEMENT_RAW;
data.element.query = strdup(sql);
m_queries.push_back(data);
data.element = std::string(sql);
m_queries.emplace_back(data);
}
//- Append a prepared statement to the transaction
void TransactionBase::AppendPreparedStatement(PreparedStatementBase* stmt)
{
SQLElementData data;
SQLElementData data = {};
data.type = SQL_ELEMENT_PREPARED;
data.element.stmt = stmt;
m_queries.push_back(data);
data.element = stmt;
m_queries.emplace_back(data);
}
void TransactionBase::Cleanup()
@@ -52,15 +53,38 @@ void TransactionBase::Cleanup()
if (_cleanedUp)
return;
for (SQLElementData const& data : m_queries)
for (SQLElementData& data : m_queries)
{
switch (data.type)
{
case SQL_ELEMENT_PREPARED:
delete data.element.stmt;
{
try
{
PreparedStatementBase* stmt = std::get<PreparedStatementBase*>(data.element);
ASSERT(stmt);
delete stmt;
}
catch (const std::bad_variant_access& ex)
{
LOG_FATAL("sql.sql", "> PreparedStatementBase not found in SQLElementData. {}", ex.what());
ABORT();
}
}
break;
case SQL_ELEMENT_RAW:
free((void*)(data.element.query));
{
try
{
std::get<std::string>(data.element).clear();
}
catch (const std::bad_variant_access& ex)
{
LOG_FATAL("sql.sql", "> std::string not found in SQLElementData. {}", ex.what());
ABORT();
}
}
break;
}
}
@@ -72,6 +96,7 @@ void TransactionBase::Cleanup()
bool TransactionTask::Execute()
{
int errorCode = TryExecute();
if (!errorCode)
return true;
@@ -81,18 +106,20 @@ bool TransactionTask::Execute()
threadIdStream << std::this_thread::get_id();
std::string threadId = threadIdStream.str();
// Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other
std::lock_guard<std::mutex> lock(_deadlockLock);
for (uint32 loopDuration = 0, startMSTime = getMSTime(); loopDuration <= DEADLOCK_MAX_RETRY_TIME_MS; loopDuration = GetMSTimeDiffToNow(startMSTime))
{
if (!TryExecute())
return true;
// Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other
std::lock_guard<std::mutex> lock(_deadlockLock);
LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: %u ms, Thread Id: %s", loopDuration, threadId.c_str());
for (Milliseconds loopDuration = 0s, startMSTime = GetTimeMS(); loopDuration <= DEADLOCK_MAX_RETRY_TIME_MS; loopDuration = GetMSTimeDiffToNow(startMSTime))
{
if (!TryExecute())
return true;
LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: {} ms, Thread Id: {}", loopDuration.count(), threadId);
}
}
LOG_ERROR("sql.sql", "Fatal deadlocked SQL Transaction, it will not be retried anymore. Thread Id: %s", threadId.c_str());
LOG_ERROR("sql.sql", "Fatal deadlocked SQL Transaction, it will not be retried anymore. Thread Id: {}", threadId);
}
// Clean up now.
@@ -126,20 +153,23 @@ bool TransactionWithResultTask::Execute()
threadIdStream << std::this_thread::get_id();
std::string threadId = threadIdStream.str();
// Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other
std::lock_guard<std::mutex> lock(_deadlockLock);
for (uint32 loopDuration = 0, startMSTime = getMSTime(); loopDuration <= DEADLOCK_MAX_RETRY_TIME_MS; loopDuration = GetMSTimeDiffToNow(startMSTime))
{
if (!TryExecute())
{
m_result.set_value(true);
return true;
}
// Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other
std::lock_guard<std::mutex> lock(_deadlockLock);
LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: %u ms, Thread Id: %s", loopDuration, threadId.c_str());
for (Milliseconds loopDuration = 0s, startMSTime = GetTimeMS(); loopDuration <= DEADLOCK_MAX_RETRY_TIME_MS; loopDuration = GetMSTimeDiffToNow(startMSTime))
{
if (!TryExecute())
{
m_result.set_value(true);
return true;
}
LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: {} ms, Thread Id: {}", loopDuration.count(), threadId);
}
}
LOG_ERROR("sql.sql", "Fatal deadlocked SQL Transaction, it will not be retried anymore. Thread Id: %s", threadId.c_str());
LOG_ERROR("sql.sql", "Fatal deadlocked SQL Transaction, it will not be retried anymore. Thread Id: {}", threadId);
}
// Clean up now.
@@ -151,7 +181,7 @@ bool TransactionWithResultTask::Execute()
bool TransactionCallback::InvokeIfReady()
{
if (m_future.valid() && m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
if (m_future.valid() && m_future.wait_for(0s) == std::future_status::ready)
{
m_callback(m_future.get());
return true;

View File

@@ -24,29 +24,31 @@
#include "StringFormat.h"
#include <functional>
#include <mutex>
#include <utility>
#include <vector>
/*! Transactions, high level class. */
class AC_DATABASE_API TransactionBase
{
friend class TransactionTask;
friend class MySQLConnection;
friend class TransactionTask;
friend class MySQLConnection;
template <typename T>
friend class DatabaseWorkerPool;
template <typename T>
friend class DatabaseWorkerPool;
public:
TransactionBase() : _cleanedUp(false) { }
TransactionBase() = default;
virtual ~TransactionBase() { Cleanup(); }
void Append(char const* sql);
template<typename Format, typename... Args>
void PAppend(Format&& sql, Args&&... args)
void Append(std::string_view sql);
template<typename... Args>
void Append(std::string_view sql, Args&&... args)
{
Append(Acore::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
Append(Acore::StringFormatFmt(sql, std::forward<Args>(args)...));
}
std::size_t GetSize() const { return m_queries.size(); }
[[nodiscard]] std::size_t GetSize() const { return m_queries.size(); }
protected:
void AppendPreparedStatement(PreparedStatementBase* statement);
@@ -54,7 +56,7 @@ protected:
std::vector<SQLElementData> m_queries;
private:
bool _cleanedUp;
bool _cleanedUp{false};
};
template<typename T>
@@ -62,6 +64,7 @@ class Transaction : public TransactionBase
{
public:
using TransactionBase::Append;
void Append(PreparedStatement<T>* statement)
{
AppendPreparedStatement(statement);
@@ -71,13 +74,15 @@ public:
/*! Low level class*/
class AC_DATABASE_API TransactionTask : public SQLOperation
{
template <class T> friend class DatabaseWorkerPool;
friend class DatabaseWorker;
friend class TransactionCallback;
template <class T>
friend class DatabaseWorkerPool;
friend class DatabaseWorker;
friend class TransactionCallback;
public:
TransactionTask(std::shared_ptr<TransactionBase> trans) : m_trans(trans) { }
~TransactionTask() { }
TransactionTask(std::shared_ptr<TransactionBase> trans) : m_trans(std::move(trans)) { }
~TransactionTask() override = default;
protected:
bool Execute() override;

View File

@@ -27,16 +27,16 @@ AppenderDB::~AppenderDB() { }
void AppenderDB::_write(LogMessage const* message)
{
// Avoid infinite loop, PExecute triggers Logging with "sql.sql" type
// Avoid infinite loop, Execute triggers Logging with "sql.sql" type
if (!enabled || (message->type.find("sql") != std::string::npos))
return;
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_LOG);
stmt->setUInt64(0, message->mtime);
stmt->setUInt32(1, realmId);
stmt->setString(2, message->type);
stmt->setUInt8(3, uint8(message->level));
stmt->setString(4, message->text);
stmt->SetData(0, message->mtime.count());
stmt->SetData(1, realmId);
stmt->SetData(2, message->type);
stmt->SetData(3, uint8(message->level));
stmt->SetData(4, message->text);
LoginDatabase.Execute(stmt);
}

View File

@@ -50,8 +50,8 @@ bool DBUpdaterUtil::CheckExecutable()
return true;
}
LOG_FATAL("sql.updates", "Didn't find any executable MySQL binary at \'%s\' or in path, correct the path in the *.conf (\"MySQLExecutable\").",
absolute(exe).generic_string().c_str());
LOG_FATAL("sql.updates", "Didn't find any executable MySQL binary at \'{}\' or in path, correct the path in the *.conf (\"MySQLExecutable\").",
absolute(exe).generic_string());
return false;
}
@@ -77,10 +77,16 @@ std::string DBUpdater<LoginDatabaseConnection>::GetTableName()
return "Auth";
}
template<>
std::string DBUpdater<LoginDatabaseConnection>::GetSourceDirectory()
{
return BuiltInConfig::GetSourceDirectory();
}
template<>
std::string DBUpdater<LoginDatabaseConnection>::GetBaseFilesDirectory()
{
return BuiltInConfig::GetSourceDirectory() + "/data/sql/base/db_auth/";
return DBUpdater<LoginDatabaseConnection>::GetSourceDirectory() + "/data/sql/base/db_auth/";
}
template<>
@@ -93,7 +99,7 @@ bool DBUpdater<LoginDatabaseConnection>::IsEnabled(uint32 const updateMask)
template<>
std::string DBUpdater<LoginDatabaseConnection>::GetDBModuleName()
{
return "db-auth";
return "auth";
}
// World Database
@@ -109,10 +115,16 @@ std::string DBUpdater<WorldDatabaseConnection>::GetTableName()
return "World";
}
template<>
std::string DBUpdater<WorldDatabaseConnection>::GetSourceDirectory()
{
return BuiltInConfig::GetSourceDirectory();
}
template<>
std::string DBUpdater<WorldDatabaseConnection>::GetBaseFilesDirectory()
{
return BuiltInConfig::GetSourceDirectory() + "/data/sql/base/db_world/";
return DBUpdater<WorldDatabaseConnection>::GetSourceDirectory() + "/data/sql/base/db_world/";
}
template<>
@@ -125,7 +137,7 @@ bool DBUpdater<WorldDatabaseConnection>::IsEnabled(uint32 const updateMask)
template<>
std::string DBUpdater<WorldDatabaseConnection>::GetDBModuleName()
{
return "db-world";
return "world";
}
// Character Database
@@ -141,10 +153,16 @@ std::string DBUpdater<CharacterDatabaseConnection>::GetTableName()
return "Character";
}
template<>
std::string DBUpdater<CharacterDatabaseConnection>::GetSourceDirectory()
{
return BuiltInConfig::GetSourceDirectory();
}
template<>
std::string DBUpdater<CharacterDatabaseConnection>::GetBaseFilesDirectory()
{
return BuiltInConfig::GetSourceDirectory() + "/data/sql/base/db_characters/";
return DBUpdater<CharacterDatabaseConnection>::GetSourceDirectory() + "/data/sql/base/db_characters/";
}
template<>
@@ -157,40 +175,46 @@ bool DBUpdater<CharacterDatabaseConnection>::IsEnabled(uint32 const updateMask)
template<>
std::string DBUpdater<CharacterDatabaseConnection>::GetDBModuleName()
{
return "db-characters";
return "characters";
}
#ifdef PLAYERBOTS
// Playerbot Database
#ifdef MOD_PLAYERBOTS
// Playerbots Database
template<>
std::string DBUpdater<PlayerbotDatabaseConnection>::GetConfigEntry()
std::string DBUpdater<PlayerbotsDatabaseConnection>::GetConfigEntry()
{
return "Updates.Playerbot";
return "Updates.Playerbots";
}
template<>
std::string DBUpdater<PlayerbotDatabaseConnection>::GetTableName()
std::string DBUpdater<PlayerbotsDatabaseConnection>::GetTableName()
{
return "Playerbot";
return "Playerbots";
}
template<>
std::string DBUpdater<PlayerbotDatabaseConnection>::GetBaseFilesDirectory()
std::string DBUpdater<PlayerbotsDatabaseConnection>::GetSourceDirectory()
{
return BuiltInConfig::GetSourceDirectory() + "/modules/mod-playerbots/sql/base/db_playerbot/";
return BuiltInConfig::GetSourceDirectory() + "/modules/mod-playerbots";
}
template<>
bool DBUpdater<PlayerbotDatabaseConnection>::IsEnabled(uint32 const updateMask)
std::string DBUpdater<PlayerbotsDatabaseConnection>::GetBaseFilesDirectory()
{
return DBUpdater<PlayerbotsDatabaseConnection>::GetSourceDirectory() + "/sql/playerbots/base/";
}
template<>
bool DBUpdater<PlayerbotsDatabaseConnection>::IsEnabled(uint32 const updateMask)
{
// This way silences warnings under msvc
return (updateMask & DatabaseLoader::DATABASE_PLAYERBOT) ? true : false;
return (updateMask & DatabaseLoader::DATABASE_PLAYERBOTS) ? true : false;
}
template<>
std::string DBUpdater<PlayerbotDatabaseConnection>::GetDBModuleName()
std::string DBUpdater<PlayerbotsDatabaseConnection>::GetDBModuleName()
{
return "db-playerbot";
return "db_playerbot";
}
#endif
@@ -204,15 +228,15 @@ BaseLocation DBUpdater<T>::GetBaseLocationType()
template<class T>
bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
{
LOG_WARN("sql.updates", "Database \"%s\" does not exist, do you want to create it? [yes (default) / no]: ",
pool.GetConnectionInfo()->database.c_str());
LOG_WARN("sql.updates", "Database \"{}\" does not exist, do you want to create it? [yes (default) / no]: ",
pool.GetConnectionInfo()->database);
std::string answer;
std::getline(std::cin, answer);
if (!answer.empty() && !(answer.substr(0, 1) == "y"))
return false;
LOG_INFO("sql.updates", "Creating database \"%s\"...", pool.GetConnectionInfo()->database.c_str());
LOG_INFO("sql.updates", "Creating database \"{}\"...", pool.GetConnectionInfo()->database);
// Path of temp file
static Path const temp("create_table.sql");
@@ -221,7 +245,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
std::ofstream file(temp.generic_string());
if (!file.is_open())
{
LOG_FATAL("sql.updates", "Failed to create temporary query file \"%s\"!", temp.generic_string().c_str());
LOG_FATAL("sql.updates", "Failed to create temporary query file \"{}\"!", temp.generic_string());
return false;
}
@@ -236,7 +260,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
}
catch (UpdateException&)
{
LOG_FATAL("sql.updates", "Failed to create database %s! Does the user (named in *.conf) have `CREATE`, `ALTER`, `DROP`, `INSERT` and `DELETE` privileges on the MySQL server?", pool.GetConnectionInfo()->database.c_str());
LOG_FATAL("sql.updates", "Failed to create database {}! Does the user (named in *.conf) have `CREATE`, `ALTER`, `DROP`, `INSERT` and `DELETE` privileges on the MySQL server?", pool.GetConnectionInfo()->database);
std::filesystem::remove(temp);
return false;
}
@@ -253,23 +277,23 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool, std::string_view modulesL
if (!DBUpdaterUtil::CheckExecutable())
return false;
LOG_INFO("sql.updates", "Updating %s database...", DBUpdater<T>::GetTableName().c_str());
LOG_INFO("sql.updates", "Updating {} database...", DBUpdater<T>::GetTableName());
Path const sourceDirectory(BuiltInConfig::GetSourceDirectory());
Path const sourceDirectory(DBUpdater<T>::GetSourceDirectory());
if (!is_directory(sourceDirectory))
{
LOG_ERROR("sql.updates", "DBUpdater: The given source directory %s does not exist, change the path to the directory where your sql directory exists (for example c:\\source\\trinitycore). Shutting down.",
sourceDirectory.generic_string().c_str());
LOG_ERROR("sql.updates", "DBUpdater: The given source directory {} does not exist, change the path to the directory where your sql directory exists (for example c:\\source\\trinitycore). Shutting down.",
sourceDirectory.generic_string());
return false;
}
auto CheckUpdateTable = [&](std::string const& tableName)
{
auto checkTable = DBUpdater<T>::Retrieve(pool, Acore::StringFormat("SHOW TABLES LIKE '%s'", tableName.c_str()));
auto checkTable = DBUpdater<T>::Retrieve(pool, Acore::StringFormatFmt("SHOW TABLES LIKE '{}'", tableName));
if (!checkTable)
{
LOG_WARN("sql.updates", "> Table '%s' not exist! Try add based table", tableName.c_str());
LOG_WARN("sql.updates", "> Table '{}' not exist! Try add based table", tableName);
Path const temp(GetBaseFilesDirectory() + tableName + ".sql");
@@ -279,7 +303,7 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool, std::string_view modulesL
}
catch (UpdateException&)
{
LOG_FATAL("sql.updates", "Failed apply file to database %s! Does the user (named in *.conf) have `INSERT` and `DELETE` privileges on the MySQL server?", pool.GetConnectionInfo()->database.c_str());
LOG_FATAL("sql.updates", "Failed apply file to database {}! Does the user (named in *.conf) have `INSERT` and `DELETE` privileges on the MySQL server?", pool.GetConnectionInfo()->database);
return false;
}
@@ -310,13 +334,12 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool, std::string_view modulesL
return false;
}
std::string const info = Acore::StringFormat("Containing " SZFMTD " new and " SZFMTD " archived updates.",
result.recent, result.archived);
std::string const info = Acore::StringFormatFmt("Containing {} new and {} archived updates.", result.recent, result.archived);
if (!result.updated)
LOG_INFO("sql.updates", ">> %s database is up-to-date! %s", DBUpdater<T>::GetTableName().c_str(), info.c_str());
LOG_INFO("sql.updates", ">> {} database is up-to-date! {}", DBUpdater<T>::GetTableName(), info);
else
LOG_INFO("sql.updates", ">> Applied " SZFMTD " %s. %s", result.updated, result.updated == 1 ? "query" : "queries", info.c_str());
LOG_INFO("sql.updates", ">> Applied {} {}. {}", result.updated, result.updated == 1 ? "query" : "queries", info);
LOG_INFO("sql.updates", " ");
@@ -331,7 +354,7 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool, std::vector<std::string>
return false;
}
Path const sourceDirectory(BuiltInConfig::GetSourceDirectory());
Path const sourceDirectory(DBUpdater<T>::GetSourceDirectory());
if (!is_directory(sourceDirectory))
{
return false;
@@ -339,7 +362,7 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool, std::vector<std::string>
auto CheckUpdateTable = [&](std::string const& tableName)
{
auto checkTable = DBUpdater<T>::Retrieve(pool, Acore::StringFormat("SHOW TABLES LIKE '%s'", tableName.c_str()));
auto checkTable = DBUpdater<T>::Retrieve(pool, Acore::StringFormatFmt("SHOW TABLES LIKE '{}'", tableName));
if (!checkTable)
{
Path const temp(GetBaseFilesDirectory() + tableName + ".sql");
@@ -396,20 +419,20 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
if (!DBUpdaterUtil::CheckExecutable())
return false;
LOG_INFO("sql.updates", "Database %s is empty, auto populating it...", DBUpdater<T>::GetTableName().c_str());
LOG_INFO("sql.updates", "Database {} is empty, auto populating it...", DBUpdater<T>::GetTableName());
std::string const DirPathStr = DBUpdater<T>::GetBaseFilesDirectory();
Path const DirPath(DirPathStr);
if (!std::filesystem::is_directory(DirPath))
{
LOG_ERROR("sql.updates", ">> Directory \"%s\" not exist", DirPath.generic_string().c_str());
LOG_ERROR("sql.updates", ">> Directory \"{}\" not exist", DirPath.generic_string());
return false;
}
if (DirPath.empty())
{
LOG_ERROR("sql.updates", ">> Directory \"%s\" is empty", DirPath.generic_string().c_str());
LOG_ERROR("sql.updates", ">> Directory \"{}\" is empty", DirPath.generic_string());
return false;
}
@@ -424,7 +447,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
if (!FilesCount)
{
LOG_ERROR("sql.updates", ">> In directory \"%s\" not exist '*.sql' files", DirPath.generic_string().c_str());
LOG_ERROR("sql.updates", ">> In directory \"{}\" not exist '*.sql' files", DirPath.generic_string());
return false;
}
@@ -433,7 +456,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
if (itr->path().extension() != ".sql")
continue;
LOG_INFO("sql.updates", ">> Applying \'%s\'...", itr->path().filename().generic_string().c_str());
LOG_INFO("sql.updates", ">> Applying \'{}\'...", itr->path().filename().generic_string());
try
{
@@ -534,12 +557,12 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
if (ret != EXIT_SUCCESS)
{
LOG_FATAL("sql.updates", "Applying of file \'%s\' to database \'%s\' failed!" \
LOG_FATAL("sql.updates", "Applying of file \'{}\' to database \'{}\' failed!" \
" If you are a user, please pull the latest revision from the repository. "
"Also make sure you have not applied any of the databases with your sql client. "
"You cannot use auto-update system and import sql files from AzerothCore repository with your sql client. "
"If you are a developer, please fix your sql query.",
path.generic_string().c_str(), pool.GetConnectionInfo()->database.c_str());
path.generic_string(), pool.GetConnectionInfo()->database);
throw UpdateException("update failed");
}
@@ -549,6 +572,6 @@ template class AC_DATABASE_API DBUpdater<LoginDatabaseConnection>;
template class AC_DATABASE_API DBUpdater<WorldDatabaseConnection>;
template class AC_DATABASE_API DBUpdater<CharacterDatabaseConnection>;
#ifdef PLAYERBOTS
template class AC_DATABASE_API DBUpdater<PlayerbotDatabaseConnection>;
#ifdef MOD_PLAYERBOTS
template class AC_DATABASE_API DBUpdater<PlayerbotsDatabaseConnection>;
#endif

View File

@@ -71,6 +71,7 @@ public:
static inline std::string GetConfigEntry();
static inline std::string GetTableName();
static std::string GetSourceDirectory();
static std::string GetBaseFilesDirectory();
static bool IsEnabled(uint32 const updateMask);
static BaseLocation GetBaseLocationType();

View File

@@ -20,7 +20,6 @@
#include "DBUpdater.h"
#include "Field.h"
#include "Log.h"
#include "QueryResult.h"
#include "Tokenize.h"
#include "Util.h"
#include <fstream>
@@ -84,7 +83,7 @@ void UpdateFetcher::FillFileListRecursively(Path const& path, LocaleFileStorage&
}
else if (itr->path().extension() == ".sql")
{
LOG_TRACE("sql.updates", "Added locale file \"%s\" state '%s'.", itr->path().filename().generic_string().c_str(), AppliedFileEntry::StateConvert(state).c_str());
LOG_TRACE("sql.updates", "Added locale file \"{}\" state '{}'.", itr->path().filename().generic_string(), AppliedFileEntry::StateConvert(state));
LocaleFileEntry const entry = { itr->path(), state };
@@ -92,8 +91,8 @@ void UpdateFetcher::FillFileListRecursively(Path const& path, LocaleFileStorage&
// Because elements are only compared by their filenames, this is ok
if (storage.find(entry) != storage.end())
{
LOG_FATAL("sql.updates", "Duplicate filename \"%s\" occurred. Because updates are ordered " \
"by their filenames, every name needs to be unique!", itr->path().generic_string().c_str());
LOG_FATAL("sql.updates", "Duplicate filename \"{}\" occurred. Because updates are ordered " \
"by their filenames, every name needs to be unique!", itr->path().generic_string());
throw UpdateException("Updating failed, see the log for details.");
}
@@ -120,7 +119,7 @@ UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories() cons
DirectoryEntry const entry = {p, AppliedFileEntry::StateConvert("MODULE")};
directories.push_back(entry);
LOG_TRACE("sql.updates", "Added applied extra file \"%s\" from remote.", p.filename().generic_string().c_str());
LOG_TRACE("sql.updates", "Added applied extra file \"{}\" from remote.", p.filename().generic_string());
}
}
else
@@ -133,8 +132,8 @@ UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories() cons
{
Field* fields = result->Fetch();
std::string path = fields[0].GetString();
std::string state = fields[1].GetString();
std::string path = fields[0].Get<std::string>();
std::string state = fields[1].Get<std::string>();
if (path.substr(0, 1) == "$")
path = _sourceDirectory->generic_string() + path.substr(1);
@@ -142,14 +141,14 @@ UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories() cons
if (!is_directory(p))
{
LOG_WARN("sql.updates", "DBUpdater: Given update include directory \"%s\" does not exist, skipped!", p.generic_string().c_str());
LOG_WARN("sql.updates", "DBUpdater: Given update include directory \"{}\" does not exist, skipped!", p.generic_string());
continue;
}
DirectoryEntry const entry = {p, AppliedFileEntry::StateConvert(state)};
directories.push_back(entry);
LOG_TRACE("sql.updates", "Added applied file \"%s\" '%s' state from remote.", p.filename().generic_string().c_str(), state.c_str());
LOG_TRACE("sql.updates", "Added applied file \"{}\" '{}' state from remote.", p.filename().generic_string(), state);
} while (result->NextRow());
@@ -163,7 +162,7 @@ UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories() cons
// data/sql
for (auto const& itr : moduleList)
{
std::string path = _sourceDirectory->generic_string() + "/modules/" + itr + "/data/sql/" + _dbModuleName; // modules/mod-name/data/sql/db-world
std::string path = _sourceDirectory->generic_string() + "/modules/" + itr + "/sql/" + _dbModuleName; // modules/mod-name/sql/world
Path const p(path);
if (!is_directory(p))
@@ -174,7 +173,7 @@ UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories() cons
DirectoryEntry const entry = { p, AppliedFileEntry::StateConvert("MODULE") };
directories.push_back(entry);
LOG_TRACE("sql.updates", "Added applied modules file \"%s\" from remote.", p.filename().generic_string().c_str());
LOG_TRACE("sql.updates", "Added applied modules file \"{}\" from remote.", p.filename().generic_string());
}
}
@@ -195,7 +194,7 @@ UpdateFetcher::AppliedFileStorage UpdateFetcher::ReceiveAppliedFiles() const
AppliedFileEntry const entry =
{
fields[0].GetString(), fields[1].GetString(), AppliedFileEntry::StateConvert(fields[2].GetString()), fields[3].GetUInt64()
fields[0].Get<std::string>(), fields[1].Get<std::string>(), AppliedFileEntry::StateConvert(fields[2].Get<std::string>()), fields[3].Get<uint64>()
};
map.emplace(entry.name, entry);
@@ -209,10 +208,10 @@ std::string UpdateFetcher::ReadSQLUpdate(Path const& file) const
std::ifstream in(file.c_str());
if (!in.is_open())
{
LOG_FATAL("sql.updates", "Failed to open the sql update \"%s\" for reading! "
LOG_FATAL("sql.updates", "Failed to open the sql update \"{}\" for reading! "
"Stopping the server to keep the database integrity, "
"try to identify and solve the issue or disable the database updater.",
file.generic_string().c_str());
file.generic_string());
throw UpdateException("Opening the sql update failed!");
}
@@ -263,7 +262,7 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
auto filePath = sqlFile.first;
auto fileState = sqlFile.second;
LOG_DEBUG("sql.updates", "Checking update \"%s\"...", filePath.filename().generic_string().c_str());
LOG_DEBUG("sql.updates", "Checking update \"{}\"...", filePath.filename().generic_string());
AppliedFileStorage::const_iterator iter = applied.find(filePath.filename().string());
if (iter != applied.end())
@@ -306,15 +305,15 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
// Conflict!
if (localeIter != available.end())
{
LOG_WARN("sql.updates", ">> It seems like the update \"%s\" \'%s\' was renamed, but the old file is still there! " \
"Treating it as a new file! (It is probably an unmodified copy of the file \"%s\")",
filePath.filename().string().c_str(), hash.substr(0, 7).c_str(),
localeIter->first.filename().string().c_str());
LOG_WARN("sql.updates", ">> It seems like the update \"{}\" \'{}\' was renamed, but the old file is still there! " \
"Treating it as a new file! (It is probably an unmodified copy of the file \"{}\")",
filePath.filename().string(), hash.substr(0, 7),
localeIter->first.filename().string());
}
else // It is safe to treat the file as renamed here
{
LOG_INFO("sql.updates", ">> Renaming update \"%s\" to \"%s\" \'%s\'.",
hashIter->second.c_str(), filePath.filename().string().c_str(), hash.substr(0, 7).c_str());
LOG_INFO("sql.updates", ">> Renaming update \"{}\" to \"{}\" \'{}\'.",
hashIter->second, filePath.filename().string(), hash.substr(0, 7));
RenameEntry(hashIter->second, filePath.filename().string());
applied.erase(hashIter->second);
@@ -324,8 +323,8 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
// Apply the update if it was never seen before.
else
{
LOG_INFO("sql.updates", ">> Applying update \"%s\" \'%s\'...",
filePath.filename().string().c_str(), hash.substr(0, 7).c_str());
LOG_INFO("sql.updates", ">> Applying update \"{}\" \'{}\'...",
filePath.filename().string(), hash.substr(0, 7));
}
}
// Rehash the update entry if it exists in our database with an empty hash.
@@ -333,29 +332,29 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
{
mode = MODE_REHASH;
LOG_INFO("sql.updates", ">> Re-hashing update \"%s\" \'%s\'...", filePath.filename().string().c_str(),
hash.substr(0, 7).c_str());
LOG_INFO("sql.updates", ">> Re-hashing update \"{}\" \'{}\'...", filePath.filename().string(),
hash.substr(0, 7));
}
else
{
// If the hash of the files differs from the one stored in our database, reapply the update (because it changed).
if (iter->second.hash != hash)
{
LOG_INFO("sql.updates", ">> Reapplying update \"%s\" \'%s\' -> \'%s\' (it changed)...", filePath.filename().string().c_str(),
iter->second.hash.substr(0, 7).c_str(), hash.substr(0, 7).c_str());
LOG_INFO("sql.updates", ">> Reapplying update \"{}\" \'{}\' -> \'{}\' (it changed)...", filePath.filename().string(),
iter->second.hash.substr(0, 7), hash.substr(0, 7));
}
else
{
// If the file wasn't changed and just moved, update its state (if necessary).
if (iter->second.state != fileState)
{
LOG_DEBUG("sql.updates", ">> Updating the state of \"%s\" to \'%s\'...",
filePath.filename().string().c_str(), AppliedFileEntry::StateConvert(fileState).c_str());
LOG_DEBUG("sql.updates", ">> Updating the state of \"{}\" to \'{}\'...",
filePath.filename().string(), AppliedFileEntry::StateConvert(fileState));
UpdateState(filePath.filename().string(), fileState);
}
LOG_DEBUG("sql.updates", ">> Update is already applied and matches the hash \'%s\'.", hash.substr(0, 7).c_str());
LOG_DEBUG("sql.updates", ">> Update is already applied and matches the hash \'{}\'.", hash.substr(0, 7));
applied.erase(iter);
return;
@@ -369,7 +368,7 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
{
case MODE_APPLY:
speed = Apply(filePath);
/* fallthrough */
[[fallthrough]];
case MODE_REHASH:
UpdateEntry(file, speed);
break;
@@ -407,13 +406,13 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
if (entry.second.state != MODULE)
{
LOG_WARN("sql.updates",
">> The file \'%s\' was applied to the database, but is missing in"
">> The file \'{}\' was applied to the database, but is missing in"
" your update directory now!",
entry.first.c_str());
entry.first);
if (doCleanup)
{
LOG_INFO("sql.updates", "Deleting orphaned entry \'%s\'...", entry.first.c_str());
LOG_INFO("sql.updates", "Deleting orphaned entry \'{}\'...", entry.first);
toCleanup.insert(entry);
}
}
@@ -426,7 +425,7 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
else
{
LOG_ERROR("sql.updates",
"Cleanup is disabled! There were " SZFMTD " dirty files applied to your database, "
"Cleanup is disabled! There were {} dirty files applied to your database, "
"but they are now missing in your source directory!",
toCleanup.size());
}