feat(Core/Database): port TrinityCore database API (#5611)

This commit is contained in:
Kargatum 2021-06-22 11:21:07 +07:00 committed by GitHub
parent 2a2e54d8c5
commit 9ac6fddcae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
155 changed files with 5818 additions and 4321 deletions

View file

@ -13,6 +13,7 @@ option(ENABLE_EXTRAS "Set to 0 to disable extra features optimizing perfor
option(ENABLE_VMAP_CHECKS "Enable Checks relative to DisableMgr system on vmap" 1)
option(ENABLE_EXTRA_LOGS "Enable extra log functions that can be CPU intensive" 0)
option(WITH_DYNAMIC_LINKING "Enable dynamic library linking." 0)
option(WITH_STRICT_DATABASE_TYPE_CHECKS "Enable strict checking of database field value accessors" 0)
IsDynamicLinkingRequired(WITH_DYNAMIC_LINKING_FORCED)

View file

@ -144,6 +144,15 @@ else()
message("* Show source tree : No (For UNIX default)")
endif()
if(WITH_STRICT_DATABASE_TYPE_CHECKS)
message("")
message(" *** WITH_STRICT_DATABASE_TYPE_CHECKS - WARNING!")
message(" *** Validates uses of database Get***() functions from Field class")
message(" *** invalid calls will result in returning value 0")
message(" *** NOT COMPATIBLE WITH MARIADB!")
add_definitions(-DACORE_STRICT_DATABASE_TYPE_CHECKS)
endif()
if(BUILD_SHARED_LIBS)
message("")
message(" *** WITH_DYNAMIC_LINKING - INFO!")

View file

@ -0,0 +1,100 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _PCQ_H
#define _PCQ_H
#include <condition_variable>
#include <mutex>
#include <queue>
#include <atomic>
#include <type_traits>
template <typename T>
class ProducerConsumerQueue
{
private:
std::mutex _queueLock;
std::queue<T> _queue;
std::condition_variable _condition;
std::atomic<bool> _shutdown;
public:
ProducerConsumerQueue<T>() : _shutdown(false) { }
void Push(const T& value)
{
std::lock_guard<std::mutex> lock(_queueLock);
_queue.push(std::move(value));
_condition.notify_one();
}
bool Empty()
{
std::lock_guard<std::mutex> lock(_queueLock);
return _queue.empty();
}
bool Pop(T& value)
{
std::lock_guard<std::mutex> lock(_queueLock);
if (_queue.empty() || _shutdown)
return false;
value = _queue.front();
_queue.pop();
return true;
}
void WaitAndPop(T& value)
{
std::unique_lock<std::mutex> lock(_queueLock);
// we could be using .wait(lock, predicate) overload here but it is broken
// https://connect.microsoft.com/VisualStudio/feedback/details/1098841
while (_queue.empty() && !_shutdown)
_condition.wait(lock);
if (_queue.empty() || _shutdown)
return;
value = _queue.front();
_queue.pop();
}
void Cancel()
{
std::unique_lock<std::mutex> lock(_queueLock);
while (!_queue.empty())
{
T& value = _queue.front();
DeleteQueuedObject(value);
_queue.pop();
}
_shutdown = true;
_condition.notify_all();
}
private:
template<typename E = T>
typename std::enable_if<std::is_pointer<E>::value>::type DeleteQueuedObject(E& obj) { delete obj; }
template<typename E = T>
typename std::enable_if<!std::is_pointer<E>::value>::type DeleteQueuedObject(E const& /*packet*/) { }
};
#endif

View file

@ -0,0 +1,51 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef AsyncCallbackProcessor_h__
#define AsyncCallbackProcessor_h__
#include "Define.h"
#include <algorithm>
#include <vector>
//template <class T>
//concept AsyncCallback = requires(T t) { { t.InvokeIfReady() } -> std::convertible_to<bool> };
template<typename T> // requires AsyncCallback<T>
class AsyncCallbackProcessor
{
public:
AsyncCallbackProcessor() = default;
~AsyncCallbackProcessor() = default;
T& AddCallback(T&& query)
{
_callbacks.emplace_back(std::move(query));
return _callbacks.back();
}
void ProcessReadyCallbacks()
{
if (_callbacks.empty())
return;
std::vector<T> updateCallbacks{ std::move(_callbacks) };
updateCallbacks.erase(std::remove_if(updateCallbacks.begin(), updateCallbacks.end(), [](T& callback)
{
return callback.InvokeIfReady();
}), updateCallbacks.end());
_callbacks.insert(_callbacks.end(), std::make_move_iterator(updateCallbacks.begin()), std::make_move_iterator(updateCallbacks.end()));
}
private:
AsyncCallbackProcessor(AsyncCallbackProcessor const&) = delete;
AsyncCallbackProcessor& operator=(AsyncCallbackProcessor const&) = delete;
std::vector<T> _callbacks;
};
#endif // AsyncCallbackProcessor_h__

View file

@ -23,6 +23,8 @@
#include "Log.h"
#include "RealmAcceptor.h"
#include "RealmList.h"
#include "DatabaseLoader.h"
#include "MySQLThreading.h"
#include "SecretMgr.h"
#include "SharedDefines.h"
#include "SignalHandler.h"
@ -211,7 +213,7 @@ bool StartDB()
// Load databases
// NOTE: While authserver is singlethreaded you should keep synch_threads == 1.
// Increasing it is just silly since only 1 will be used ever.
DatabaseLoader loader;
DatabaseLoader loader("server.authserver");
loader
.AddDatabase(LoginDatabase, "Login");

View file

@ -20,6 +20,7 @@
#include "SecretMgr.h"
#include "TOTP.h"
#include "Threading.h"
#include "Util.h"
#include <algorithm>
#include <openssl/crypto.h>
#include <openssl/md5.h>
@ -577,7 +578,7 @@ bool AuthSocket::_HandleLogonProof()
// Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
// No SQL injection (escaped user name) and IP address as received by socket
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF);
stmt->setBinary(0, _sessionKey);
stmt->setString(1, socket().getRemoteAddress().c_str());
stmt->setUInt32(2, GetLocaleByName(_localizationName));
@ -625,7 +626,7 @@ bool AuthSocket::_HandleLogonProof()
// We can not include the failed account login hook. However, this is a workaround to still log this.
if (sConfigMgr->GetOption<bool>("WrongPass.Logging", false))
{
PreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING);
LoginDatabasePreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING);
logstmt->setString(0, _accountInfo.Login);
logstmt->setString(1, socket().getRemoteAddress());
logstmt->setString(2, "Logged on failed AccountLogin due wrong password");
@ -636,7 +637,7 @@ bool AuthSocket::_HandleLogonProof()
if (MaxWrongPassCount > 0)
{
//Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS);
stmt->setString(0, _accountInfo.Login);
LoginDatabase.Execute(stmt);
@ -720,7 +721,6 @@ bool AuthSocket::_HandleReconnectChallenge()
auto* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RECONNECTCHALLENGE);
stmt->setString(0, login);
PreparedQueryResult result = LoginDatabase.Query(stmt);
// Stop if the account is not found
@ -835,7 +835,7 @@ bool AuthSocket::_HandleRealmList()
// Get the user id (else close the connection)
// No SQL injection (prepared statement)
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME);
stmt->setString(0, _accountInfo.Login);
PreparedQueryResult result = LoginDatabase.Query(stmt);

View file

@ -6,6 +6,7 @@ CollectSourceFiles(
${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE_SOURCES
# Exclude
${CMAKE_CURRENT_SOURCE_DIR}/Updater
${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders)
if(USE_COREPCH)
@ -40,12 +41,11 @@ target_include_directories(database
${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(database
# PRIVATE
PRIVATE
# acore-core-interface
# mysql
mysql
PUBLIC
common
mysql)
common)
set_target_properties(database
PROPERTIES

View file

@ -1,29 +1,30 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "AdhocStatement.h"
#include "Errors.h"
#include "MySQLConnection.h"
#include "QueryResult.h"
#include <cstdlib>
#include <cstring>
/*! Basic, ad-hoc queries. */
BasicStatementTask::BasicStatementTask(const char* sql) :
m_has_result(false)
{
m_sql = strdup(sql);
}
BasicStatementTask::BasicStatementTask(const char* sql, QueryResultFuture result) :
m_has_result(true),
m_result(result)
BasicStatementTask::BasicStatementTask(char const* sql, bool async) :
m_result(nullptr)
{
m_sql = strdup(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)
delete m_result;
}
bool BasicStatementTask::Execute()
@ -31,14 +32,14 @@ bool BasicStatementTask::Execute()
if (m_has_result)
{
ResultSet* result = m_conn->Query(m_sql);
if (!result || !result->GetRowCount())
if (!result || !result->GetRowCount() || !result->NextRow())
{
delete result;
m_result.set(QueryResult(nullptr));
m_result->set_value(QueryResult(nullptr));
return false;
}
result->NextRow();
m_result.set(QueryResult(result));
m_result->set_value(QueryResult(result));
return true;
}

View file

@ -1,30 +1,29 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _ADHOCSTATEMENT_H
#define _ADHOCSTATEMENT_H
#include <ace/Future.h>
#include "DatabaseEnvFwd.h"
#include "Define.h"
#include "SQLOperation.h"
typedef ACE_Future<QueryResult> QueryResultFuture;
/*! Raw, ad-hoc query. */
class BasicStatementTask : public SQLOperation
class AC_DATABASE_API BasicStatementTask : public SQLOperation
{
public:
BasicStatementTask(const char* sql);
BasicStatementTask(const char* sql, QueryResultFuture result);
~BasicStatementTask() override;
BasicStatementTask(char const* sql, bool async = false);
~BasicStatementTask();
bool Execute() override;
QueryResultFuture GetFuture() const { return m_result->get_future(); }
private:
const char* m_sql; //- Raw query to be executed
char const* m_sql; //- Raw query to be executed
bool m_has_result;
QueryResultFuture m_result;
QueryResultPromise* m_result;
};
#endif

View file

@ -1,10 +1,10 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "DatabaseEnv.h"
WorldDatabaseWorkerPool WorldDatabase;
CharacterDatabaseWorkerPool CharacterDatabase;
LoginDatabaseWorkerPool LoginDatabase;
DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabase;
DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabase;
DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabase;

View file

@ -1,38 +1,29 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef DATABASEENV_H
#define DATABASEENV_H
#include "Common.h"
#include "Errors.h"
#include "Log.h"
#include "DatabaseWorkerPool.h"
#include "Define.h"
#include "Implementation/CharacterDatabase.h"
#include "Implementation/LoginDatabase.h"
#include "Implementation/WorldDatabase.h"
#include "Field.h"
#include "PreparedStatement.h"
#include "QueryCallback.h"
#include "QueryResult.h"
#include "MySQLThreading.h"
#include "Transaction.h"
#define _LIKE_ "LIKE"
#define _TABLE_SIM_ "`"
#define _CONCAT3_(A, B, C) "CONCAT( " A ", " B ", " C " )"
#define _OFFSET_ "LIMIT %d, 1"
#include "LoginDatabase.h"
#include "CharacterDatabase.h"
#include "WorldDatabase.h"
/// Accessor to the world database
extern WorldDatabaseWorkerPool WorldDatabase;
AC_DATABASE_API extern DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabase;
/// Accessor to the character database
extern CharacterDatabaseWorkerPool CharacterDatabase;
AC_DATABASE_API extern DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabase;
/// Accessor to the realm/login database
extern LoginDatabaseWorkerPool LoginDatabase;
AC_DATABASE_API extern DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabase;
#endif

View file

@ -0,0 +1,82 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef DatabaseEnvFwd_h__
#define DatabaseEnvFwd_h__
#include <future>
#include <memory>
struct QueryResultFieldMetadata;
class Field;
class ResultSet;
using QueryResult = std::shared_ptr<ResultSet>;
using QueryResultFuture = std::future<QueryResult>;
using QueryResultPromise = std::promise<QueryResult>;
class CharacterDatabaseConnection;
class LoginDatabaseConnection;
class WorldDatabaseConnection;
class PreparedStatementBase;
template<typename T>
class PreparedStatement;
using CharacterDatabasePreparedStatement = PreparedStatement<CharacterDatabaseConnection>;
using LoginDatabasePreparedStatement = PreparedStatement<LoginDatabaseConnection>;
using WorldDatabasePreparedStatement = PreparedStatement<WorldDatabaseConnection>;
class PreparedResultSet;
using PreparedQueryResult = std::shared_ptr<PreparedResultSet>;
using PreparedQueryResultFuture = std::future<PreparedQueryResult>;
using PreparedQueryResultPromise = std::promise<PreparedQueryResult>;
class QueryCallback;
template<typename T>
class AsyncCallbackProcessor;
using QueryCallbackProcessor = AsyncCallbackProcessor<QueryCallback>;
class TransactionBase;
using TransactionFuture = std::future<bool>;
using TransactionPromise = std::promise<bool>;
template<typename T>
class Transaction;
class TransactionCallback;
template<typename T>
using SQLTransaction = std::shared_ptr<Transaction<T>>;
using CharacterDatabaseTransaction = SQLTransaction<CharacterDatabaseConnection>;
using LoginDatabaseTransaction = SQLTransaction<LoginDatabaseConnection>;
using WorldDatabaseTransaction = SQLTransaction<WorldDatabaseConnection>;
class SQLQueryHolderBase;
using QueryResultHolderFuture = std::future<void>;
using QueryResultHolderPromise = std::promise<void>;
template<typename T>
class SQLQueryHolder;
using CharacterDatabaseQueryHolder = SQLQueryHolder<CharacterDatabaseConnection>;
using LoginDatabaseQueryHolder = SQLQueryHolder<LoginDatabaseConnection>;
using WorldDatabaseQueryHolder = SQLQueryHolder<WorldDatabaseConnection>;
class SQLQueryHolderCallback;
// mysql
struct MySQLHandle;
struct MySQLResult;
struct MySQLField;
struct MySQLBind;
struct MySQLStmt;
#endif // DatabaseEnvFwd_h__

View file

@ -5,12 +5,18 @@
#include "DatabaseLoader.h"
#include "Config.h"
// #include "DBUpdater.h" not implement
#include "DatabaseEnv.h"
#include "Duration.h"
#include "Log.h"
#include "Duration.h"
#include <errmsg.h>
#include <mysqld_error.h>
#include <thread>
DatabaseLoader::DatabaseLoader(std::string const& logger)
: _logger(logger) { }
template <class T>
DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::string const& name)
{
@ -19,14 +25,15 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
std::string const dbString = sConfigMgr->GetOption<std::string>(name + "DatabaseInfo", "");
if (dbString.empty())
{
LOG_INFO("sql.driver", "Database %s not specified in configuration file!", name.c_str());
LOG_ERROR(_logger, "Database %s not specified in configuration file!", name.c_str());
return false;
}
uint8 const asyncThreads = sConfigMgr->GetOption<uint8>(name + "Database.WorkerThreads", 1);
if (asyncThreads < 1 || asyncThreads > 32)
{
LOG_INFO("sql.driver", "%s database: invalid number of worker threads specified. Please pick a value between 1 and 32.", name.c_str());
LOG_ERROR(_logger, "%s database: invalid number of worker threads specified. "
"Please pick a value between 1 and 32.", name.c_str());
return false;
}
@ -39,36 +46,36 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
// Try reconnect
if (error == CR_CONNECTION_ERROR)
{
uint8 const ATTEMPTS = sConfigMgr->GetOption<uint8>("Database.Reconnect.Attempts", 20);
Seconds RECONNECT_SECONDS = Seconds(sConfigMgr->GetOption<uint8>("Database.Reconnect.Seconds", 15));
uint8 count = 0;
uint8 const attempts = sConfigMgr->GetOption<uint8>("Database.Reconnect.Attempts", 20);
Seconds reconnectSeconds = Seconds(sConfigMgr->GetOption<uint8>("Database.Reconnect.Seconds", 15));
uint8 reconnectCount = 0;
while (count < ATTEMPTS)
while (reconnectCount < attempts)
{
LOG_INFO("sql.driver", "> Retrying after %u seconds", static_cast<uint32>(RECONNECT_SECONDS.count()));
std::this_thread::sleep_for(RECONNECT_SECONDS);
LOG_INFO(_logger, "> Retrying after %u seconds", static_cast<uint32>(reconnectSeconds.count()));
std::this_thread::sleep_for(reconnectSeconds);
error = pool.Open();
if (error == CR_CONNECTION_ERROR)
{
count++;
reconnectCount++;
}
else
{
break;
}
}
}
// If the error wasn't handled quit
if (error)
{
LOG_ERROR("sql.driver", "DatabasePool %s NOT opened. There were errors opening the MySQL connections. Check your SQLDriverLogFile for specific errors", name.c_str());
LOG_ERROR(_logger, "DatabasePool %s NOT opened. There were errors opening the MySQL connections. "
"Check your log file for specific errors", name.c_str());
return false;
}
}
// Add the close operation
_close.push([&pool]
{
@ -78,11 +85,11 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
return true;
});
_prepare.push([name, &pool]() -> bool
_prepare.push([this, name, &pool]() -> bool
{
if (!pool.PrepareStatements())
{
LOG_ERROR("sql.driver", "Could not prepare statements of the %s database, see log for details.", name.c_str());
LOG_ERROR(_logger, "Could not prepare statements of the %s database, see log for details.", name.c_str());
return false;
}
@ -129,6 +136,9 @@ bool DatabaseLoader::Process(std::queue<Predicate>& queue)
return true;
}
template DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(DatabaseWorkerPool<LoginDatabaseConnection>&, std::string const&);
template DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(DatabaseWorkerPool<CharacterDatabaseConnection>&, std::string const&);
template DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(DatabaseWorkerPool<WorldDatabaseConnection>&, std::string const&);
template AC_DATABASE_API
DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(DatabaseWorkerPool<LoginDatabaseConnection>&, std::string const&);
template AC_DATABASE_API
DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(DatabaseWorkerPool<CharacterDatabaseConnection>&, std::string const&);
template AC_DATABASE_API
DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(DatabaseWorkerPool<WorldDatabaseConnection>&, std::string const&);

View file

@ -17,9 +17,11 @@ class DatabaseWorkerPool;
// A helper class to initiate all database worker pools,
// handles updating, delays preparing of statements and cleans up on failure.
class DatabaseLoader
class AC_DATABASE_API DatabaseLoader
{
public:
DatabaseLoader(std::string const& logger);
// Register a database to the loader (lazy implemented)
template <class T>
DatabaseLoader& AddDatabase(DatabaseWorkerPool<T>& pool, std::string const& name);
@ -49,6 +51,8 @@ private:
// Returns false when there was an error.
bool Process(std::queue<Predicate>& queue);
std::string const _logger;
std::queue<Predicate> _open, _prepare;
std::stack<Closer> _close;
};

View file

@ -1,39 +1,46 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "DatabaseWorker.h"
#include "ProducerConsumerQueue.h"
#include "SQLOperation.h"
#include "MySQLConnection.h"
#include "MySQLThreading.h"
DatabaseWorker::DatabaseWorker(ACE_Activation_Queue* new_queue, MySQLConnection* con) :
m_queue(new_queue),
m_conn(con)
DatabaseWorker::DatabaseWorker(ProducerConsumerQueue<SQLOperation*>* newQueue, MySQLConnection* connection)
{
/// Assign thread to task
activate();
_connection = connection;
_queue = newQueue;
_cancelationToken = false;
_workerThread = std::thread(&DatabaseWorker::WorkerThread, this);
}
int DatabaseWorker::svc()
DatabaseWorker::~DatabaseWorker()
{
if (!m_queue)
return -1;
_cancelationToken = true;
SQLOperation* request = nullptr;
while (1)
_queue->Cancel();
_workerThread.join();
}
void DatabaseWorker::WorkerThread()
{
if (!_queue)
return;
for (;;)
{
request = (SQLOperation*)(m_queue->dequeue());
if (!request)
break;
SQLOperation* operation = nullptr;
request->SetConnection(m_conn);
request->call();
_queue->WaitAndPop(operation);
delete request;
if (_cancelationToken || !operation)
return;
operation->SetConnection(_connection);
operation->call();
delete operation;
}
return 0;
}

View file

@ -1,30 +1,38 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _WORKERTHREAD_H
#define _WORKERTHREAD_H
#include <ace/Task.h>
#include <ace/Activation_Queue.h>
#include "Define.h"
#include <atomic>
#include <thread>
template <typename T>
class ProducerConsumerQueue;
class MySQLConnection;
class SQLOperation;
class DatabaseWorker : protected ACE_Task_Base
class AC_DATABASE_API DatabaseWorker
{
public:
DatabaseWorker(ACE_Activation_Queue* new_queue, MySQLConnection* con);
///- Inherited from ACE_Task_Base
int svc() override;
int wait() override { return ACE_Task_Base::wait(); }
DatabaseWorker(ProducerConsumerQueue<SQLOperation*>* newQueue, MySQLConnection* connection);
~DatabaseWorker();
private:
DatabaseWorker() : ACE_Task_Base() { }
ACE_Activation_Queue* m_queue;
MySQLConnection* m_conn;
ProducerConsumerQueue<SQLOperation*>* _queue;
MySQLConnection* _connection;
void WorkerThread();
std::thread _workerThread;
std::atomic<bool> _cancelationToken;
DatabaseWorker(DatabaseWorker const& right) = delete;
DatabaseWorker& operator=(DatabaseWorker const& right) = delete;
};
#endif

View file

@ -1,33 +1,65 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "DatabaseWorkerPool.h"
#include "DatabaseEnv.h"
#include "AdhocStatement.h"
#include "Common.h"
#include "Errors.h"
#include "Implementation/CharacterDatabase.h"
#include "Implementation/LoginDatabase.h"
#include "Implementation/WorldDatabase.h"
#include "Log.h"
#include "MySQLPreparedStatement.h"
#include "MySQLWorkaround.h"
#include "PreparedStatement.h"
#include "ProducerConsumerQueue.h"
#include "QueryCallback.h"
#include "QueryHolder.h"
#include "QueryResult.h"
#include "SQLOperation.h"
#include "Transaction.h"
#include <mysqld_error.h>
#ifdef ACORE_DEBUG
#include <boost/stacktrace.hpp>
#include <sstream>
#endif
#define MIN_MYSQL_SERVER_VERSION 50700u
#define MIN_MYSQL_CLIENT_VERSION 50700u
template <class T> DatabaseWorkerPool<T>::DatabaseWorkerPool() :
_mqueue(new ACE_Message_Queue<ACE_SYNCH>(2 * 1024 * 1024, 2 * 1024 * 1024)),
_queue(new ACE_Activation_Queue(_mqueue)),
_async_threads(0),
_synch_threads(0)
class PingOperation : public SQLOperation
{
memset(_connectionCount, 0, sizeof(_connectionCount));
_connections.resize(IDX_SIZE);
//! Operation for idle delaythreads
bool Execute() override
{
m_conn->Ping();
return true;
}
};
template <class T>
DatabaseWorkerPool<T>::DatabaseWorkerPool()
: _queue(new ProducerConsumerQueue<SQLOperation*>()),
_async_threads(0), _synch_threads(0)
{
WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
WPFatal(mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION, "AzerothCore does not support MySQL versions below 5.7");
WPFatal(mysql_get_client_version() == MYSQL_VERSION_ID, "Used MySQL library version (%s id %lu) does not match the version id used to compile AzerothCore (id %u)",
mysql_get_client_info(), mysql_get_client_version(), MYSQL_VERSION_ID);
}
template <class T>
DatabaseWorkerPool<T>::~DatabaseWorkerPool()
{
_queue->Cancel();
}
template <class T>
void DatabaseWorkerPool<T>::SetConnectionInfo(std::string const& infoString,
uint8 const asyncThreads, uint8 const synchThreads)
uint8 const asyncThreads, uint8 const synchThreads)
{
_connectionInfo = std::make_unique<MySQLConnectionInfo>(infoString);
@ -40,22 +72,22 @@ 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 '%s'. "
"Asynchronous connections: %u, synchronous connections: %u.",
GetDatabaseName(), _async_threads, _synch_threads);
uint32 error = OpenConnections(IDX_ASYNC, _async_threads);
if (error)
{
return error;
}
error = OpenConnections(IDX_SYNCH, _synch_threads);
if (!error)
{
LOG_INFO("sql.driver", "DatabasePool '%s' opened successfully. %u total connections running.",
GetDatabaseName(), (_connectionCount[IDX_SYNCH] + _connectionCount[IDX_ASYNC]));
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", " ");
@ -68,109 +100,59 @@ void DatabaseWorkerPool<T>::Close()
{
LOG_INFO("sql.driver", "Closing down DatabasePool '%s'.", GetDatabaseName());
//! Shuts down delaythreads for this connection pool by underlying deactivate().
//! The next dequeue attempt in the worker thread tasks will result in an error,
//! ultimately ending the worker thread task.
_queue->queue()->close();
//! Closes the actualy MySQL connection.
_connections[IDX_ASYNC].clear();
for (uint8 i = 0; i < _connectionCount[IDX_ASYNC]; ++i)
{
T* t = _connections[IDX_ASYNC][i];
DatabaseWorker* worker = t->m_worker;
worker->wait(); //! Block until no more threads are running this task.
delete worker;
t->Close(); //! Closes the actualy MySQL connection.
}
LOG_INFO("sql.driver", "Asynchronous connections on DatabasePool '%s' terminated. Proceeding with synchronous connections.",
LOG_INFO("sql.driver", "Asynchronous connections on DatabasePool '%s' terminated. "
"Proceeding with synchronous connections.",
GetDatabaseName());
//! Shut down the synchronous connections
//! There's no need for locking the connection, because DatabaseWorkerPool<>::Close
//! should only be called after any other thread tasks in the core have exited,
//! meaning there can be no concurrent access at this point.
for (uint8 i = 0; i < _connectionCount[IDX_SYNCH]; ++i)
_connections[IDX_SYNCH][i]->Close();
//! Deletes the ACE_Activation_Queue object and its underlying ACE_Message_Queue
delete _queue;
delete _mqueue;
_connections[IDX_SYNCH].clear();
LOG_INFO("sql.driver", "All connections on DatabasePool '%s' closed.", GetDatabaseName());
}
template <class T>
uint32 DatabaseWorkerPool<T>::OpenConnections(InternalIndex type, uint8 numConnections)
{
_connections[type].resize(numConnections);
for (uint8 i = 0; i < numConnections; ++i)
{
T* t;
if (type == IDX_ASYNC)
{
t = new T(_queue, *_connectionInfo);
}
else if (type == IDX_SYNCH)
{
t = new T(*_connectionInfo);
}
else
{
ASSERT(false, "> Incorrect InternalIndex (%u)", static_cast<uint32>(type));
}
_connections[type][i] = t;
++_connectionCount[type];
uint32 error = t->Open();
if (!error)
{
if (mysql_get_server_version(t->GetHandle()) < MIN_MYSQL_SERVER_VERSION)
{
LOG_ERROR("sql.driver", "Not support MySQL versions below 5.7");
error = 1;
}
}
// Failed to open a connection or invalid version, abort and cleanup
if (error)
{
while (_connectionCount[type] != 0)
{
T* t = _connections[type][i--];
delete t;
--_connectionCount[type];
}
return error;
}
}
// Everything is fine
return 0;
}
template <class T>
bool DatabaseWorkerPool<T>::PrepareStatements()
{
for (uint8 i = 0; i < IDX_SIZE; ++i)
for (auto& connections : _connections)
{
for (uint32 c = 0; c < _connectionCount[i]; ++c)
for (auto& connection : connections)
{
T* t = _connections[i][c];
t->LockIfReady();
if (!t->PrepareStatements())
connection->LockIfReady();
if (!connection->PrepareStatements())
{
t->Unlock();
connection->Unlock();
Close();
return false;
}
else
connection->Unlock();
size_t const preparedSize = connection->m_stmts.size();
if (_preparedStatementSize.size() < preparedSize)
_preparedStatementSize.resize(preparedSize);
for (size_t i = 0; i < preparedSize; ++i)
{
t->Unlock();
// already set by another connection
// (each connection only has prepared statements of it's own type sync/async)
if (_preparedStatementSize[i] > 0)
continue;
if (MySQLPreparedStatement* stmt = connection->m_stmts[i].get())
{
uint32 const paramCount = stmt->GetParameterCount();
// TC only supports uint8 indices.
ASSERT(paramCount < std::numeric_limits<uint8>::max());
_preparedStatementSize[i] = static_cast<uint8>(paramCount);
}
}
}
}
@ -179,74 +161,28 @@ bool DatabaseWorkerPool<T>::PrepareStatements()
}
template <class T>
char const* DatabaseWorkerPool<T>::GetDatabaseName() const
QueryResult DatabaseWorkerPool<T>::Query(char const* sql, T* connection /*= nullptr*/)
{
return _connectionInfo->database.c_str();
}
if (!connection)
connection = GetFreeConnection();
template <class T>
void DatabaseWorkerPool<T>::Execute(const char* sql)
{
if (!sql)
return;
BasicStatementTask* task = new BasicStatementTask(sql);
Enqueue(task);
}
template <class T>
void DatabaseWorkerPool<T>::Execute(PreparedStatement* stmt)
{
PreparedStatementTask* task = new PreparedStatementTask(stmt);
Enqueue(task);
}
template <class T>
void DatabaseWorkerPool<T>::DirectExecute(const char* sql)
{
if (!sql)
return;
T* t = GetFreeConnection();
t->Execute(sql);
t->Unlock();
}
template <class T>
void DatabaseWorkerPool<T>::DirectExecute(PreparedStatement* stmt)
{
T* t = GetFreeConnection();
t->Execute(stmt);
t->Unlock();
//! Delete proxy-class. Not needed anymore
delete stmt;
}
template <class T>
QueryResult DatabaseWorkerPool<T>::Query(const char* sql, T* conn /* = nullptr*/)
{
if (!conn)
conn = GetFreeConnection();
ResultSet* result = conn->Query(sql);
conn->Unlock();
if (!result || !result->GetRowCount())
ResultSet* result = connection->Query(sql);
connection->Unlock();
if (!result || !result->GetRowCount() || !result->NextRow())
{
delete result;
return QueryResult(nullptr);
}
result->NextRow();
return QueryResult(result);
}
template <class T>
PreparedQueryResult DatabaseWorkerPool<T>::Query(PreparedStatement* stmt)
PreparedQueryResult DatabaseWorkerPool<T>::Query(PreparedStatement<T>* stmt)
{
T* t = GetFreeConnection();
PreparedResultSet* ret = t->Query(stmt);
t->Unlock();
auto connection = GetFreeConnection();
PreparedResultSet* ret = connection->Query(stmt);
connection->Unlock();
//! Delete proxy-class. Not needed anymore
delete stmt;
@ -261,40 +197,66 @@ PreparedQueryResult DatabaseWorkerPool<T>::Query(PreparedStatement* stmt)
}
template <class T>
QueryResultFuture DatabaseWorkerPool<T>::AsyncQuery(const char* sql)
QueryCallback DatabaseWorkerPool<T>::AsyncQuery(char const* sql)
{
QueryResultFuture res;
BasicStatementTask* task = new BasicStatementTask(sql, res);
BasicStatementTask* task = new BasicStatementTask(sql, true);
// Store future result before enqueueing - task might get already processed and deleted before returning from this method
QueryResultFuture result = task->GetFuture();
Enqueue(task);
return res; //! Actual return value has no use yet
return QueryCallback(std::move(result));
}
template <class T>
PreparedQueryResultFuture DatabaseWorkerPool<T>::AsyncQuery(PreparedStatement* stmt)
QueryCallback DatabaseWorkerPool<T>::AsyncQuery(PreparedStatement<T>* stmt)
{
PreparedQueryResultFuture res;
PreparedStatementTask* task = new PreparedStatementTask(stmt, res);
PreparedStatementTask* task = new PreparedStatementTask(stmt, true);
// Store future result before enqueueing - task might get already processed and deleted before returning from this method
PreparedQueryResultFuture result = task->GetFuture();
Enqueue(task);
return res;
return QueryCallback(std::move(result));
}
template <class T>
QueryResultHolderFuture DatabaseWorkerPool<T>::DelayQueryHolder(SQLQueryHolder* holder)
SQLQueryHolderCallback DatabaseWorkerPool<T>::DelayQueryHolder(std::shared_ptr<SQLQueryHolder<T>> holder)
{
QueryResultHolderFuture res;
SQLQueryHolderTask* task = new SQLQueryHolderTask(holder, res);
SQLQueryHolderTask* task = new SQLQueryHolderTask(holder);
// Store future result before enqueueing - task might get already processed and deleted before returning from this method
QueryResultHolderFuture result = task->GetFuture();
Enqueue(task);
return res; //! Fool compiler, has no use yet
return { std::move(holder), std::move(result) };
}
template <class T>
SQLTransaction DatabaseWorkerPool<T>::BeginTransaction()
SQLTransaction<T> DatabaseWorkerPool<T>::BeginTransaction()
{
return SQLTransaction(new Transaction);
return std::make_shared<Transaction<T>>();
}
template <class T>
void DatabaseWorkerPool<T>::CommitTransaction(SQLTransaction transaction)
void DatabaseWorkerPool<T>::CommitTransaction(SQLTransaction<T> transaction)
{
#ifdef ACORE_DEBUG
//! Only analyze transaction weaknesses in Debug mode.
//! Ideally we catch the faults in Debug mode and then correct them,
//! so there's no need to waste these CPU cycles in Release mode.
switch (transaction->GetSize())
{
case 0:
LOG_DEBUG("sql.driver", "Transaction contains 0 queries. Not executing.");
return;
case 1:
LOG_DEBUG("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code.");
break;
default:
break;
}
#endif // ACORE_DEBUG
Enqueue(new TransactionTask(transaction));
}
template <class T>
TransactionCallback DatabaseWorkerPool<T>::AsyncCommitTransaction(SQLTransaction<T> transaction)
{
#ifdef ACORE_DEBUG
//! Only analyze transaction weaknesses in Debug mode.
@ -303,38 +265,42 @@ void DatabaseWorkerPool<T>::CommitTransaction(SQLTransaction transaction)
switch (transaction->GetSize())
{
case 0:
LOG_INFO("sql.driver", "Transaction contains 0 queries. Not executing.");
return;
LOG_DEBUG("sql.driver", "Transaction contains 0 queries. Not executing.");
break;
case 1:
LOG_INFO("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code.");
LOG_DEBUG("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code.");
break;
default:
break;
}
#endif // ACORE_DEBUG
Enqueue(new TransactionTask(transaction));
TransactionWithResultTask* task = new TransactionWithResultTask(transaction);
TransactionFuture result = task->GetFuture();
Enqueue(task);
return TransactionCallback(std::move(result));
}
template <class T>
void DatabaseWorkerPool<T>::DirectCommitTransaction(SQLTransaction& transaction)
void DatabaseWorkerPool<T>::DirectCommitTransaction(SQLTransaction<T>& transaction)
{
T* con = GetFreeConnection();
int errorCode = con->ExecuteTransaction(transaction);
T* connection = GetFreeConnection();
int errorCode = connection->ExecuteTransaction(transaction);
if (!errorCode)
{
con->Unlock(); // OK, operation succesful
connection->Unlock(); // OK, operation succesful
return;
}
//! Handle MySQL Errno 1213 without extending deadlock to the core itself
//! TODO: More elegant way
/// @todo More elegant way
if (errorCode == ER_LOCK_DEADLOCK)
{
//todo: handle multiple sync threads deadlocking in a similar way as async threads
uint8 loopBreaker = 5;
for (uint8 i = 0; i < loopBreaker; ++i)
{
if (!con->ExecuteTransaction(transaction))
if (!connection->ExecuteTransaction(transaction))
break;
}
}
@ -342,20 +308,177 @@ void DatabaseWorkerPool<T>::DirectCommitTransaction(SQLTransaction& transaction)
//! Clean up now.
transaction->Cleanup();
con->Unlock();
connection->Unlock();
}
template <class T>
void DatabaseWorkerPool<T>::ExecuteOrAppend(SQLTransaction& trans, PreparedStatement* stmt)
PreparedStatement<T>* DatabaseWorkerPool<T>::GetPreparedStatement(PreparedStatementIndex index)
{
if (!trans)
Execute(stmt);
else
trans->Append(stmt);
return new PreparedStatement<T>(index, _preparedStatementSize[index]);
}
template <class T>
void DatabaseWorkerPool<T>::ExecuteOrAppend(SQLTransaction& trans, const char* sql)
void DatabaseWorkerPool<T>::EscapeString(std::string& str)
{
if (str.empty())
return;
char* buf = new char[str.size() * 2 + 1];
EscapeString(buf, str.c_str(), uint32(str.size()));
str = buf;
delete[] buf;
}
template <class T>
void DatabaseWorkerPool<T>::KeepAlive()
{
//! Ping synchronous connections
for (auto& connection : _connections[IDX_SYNCH])
{
if (connection->LockIfReady())
{
connection->Ping();
connection->Unlock();
}
}
//! Assuming all worker threads are free, every worker thread will receive 1 ping operation request
//! 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);
}
template <class T>
uint32 DatabaseWorkerPool<T>::OpenConnections(InternalIndex type, uint8 numConnections)
{
for (uint8 i = 0; i < numConnections; ++i)
{
// Create the connection
auto connection = [&] {
switch (type)
{
case IDX_ASYNC:
return std::make_unique<T>(_queue.get(), *_connectionInfo);
case IDX_SYNCH:
return std::make_unique<T>(*_connectionInfo);
default:
ABORT();
}
}();
if (uint32 error = connection->Open())
{
// Failed to open a connection or invalid version, abort and cleanup
_connections[type].clear();
return error;
}
else if (connection->GetServerVersion() < MIN_MYSQL_SERVER_VERSION)
{
LOG_ERROR("sql.driver", "AzerothCore does not support MySQL versions below 5.7");
return 1;
}
else
{
_connections[type].push_back(std::move(connection));
}
}
// Everything is fine
return 0;
}
template <class T>
unsigned long DatabaseWorkerPool<T>::EscapeString(char* to, char const* from, unsigned long length)
{
if (!to || !from || !length)
return 0;
return _connections[IDX_SYNCH].front()->EscapeString(to, from, length);
}
template <class T>
void DatabaseWorkerPool<T>::Enqueue(SQLOperation* op)
{
_queue->Push(op);
}
template <class T>
T* DatabaseWorkerPool<T>::GetFreeConnection()
{
#ifdef ACORE_DEBUG
if (_warnSyncQueries)
{
std::ostringstream ss;
ss << boost::stacktrace::stacktrace();
LOG_WARN("sql.performances", "Sync query at:\n%s", ss.str().c_str());
}
#endif
uint8 i = 0;
auto const num_cons = _connections[IDX_SYNCH].size();
T* connection = nullptr;
//! Block forever until a connection is free
for (;;)
{
connection = _connections[IDX_SYNCH][++i % num_cons].get();
//! Must be matched with t->Unlock() or you will get deadlocks
if (connection->LockIfReady())
break;
}
return connection;
}
template <class T>
char const* DatabaseWorkerPool<T>::GetDatabaseName() const
{
return _connectionInfo->database.c_str();
}
template <class T>
void DatabaseWorkerPool<T>::Execute(char const* sql)
{
if (Acore::IsFormatEmptyOrNull(sql))
return;
BasicStatementTask* task = new BasicStatementTask(sql);
Enqueue(task);
}
template <class T>
void DatabaseWorkerPool<T>::Execute(PreparedStatement<T>* stmt)
{
PreparedStatementTask* task = new PreparedStatementTask(stmt);
Enqueue(task);
}
template <class T>
void DatabaseWorkerPool<T>::DirectExecute(char const* sql)
{
if (Acore::IsFormatEmptyOrNull(sql))
return;
T* connection = GetFreeConnection();
connection->Execute(sql);
connection->Unlock();
}
template <class T>
void DatabaseWorkerPool<T>::DirectExecute(PreparedStatement<T>* stmt)
{
T* connection = GetFreeConnection();
connection->Execute(stmt);
connection->Unlock();
//! Delete proxy-class. Not needed anymore
delete stmt;
}
template <class T>
void DatabaseWorkerPool<T>::ExecuteOrAppend(SQLTransaction<T>& trans, char const* sql)
{
if (!trans)
Execute(sql);
@ -364,51 +487,14 @@ void DatabaseWorkerPool<T>::ExecuteOrAppend(SQLTransaction& trans, const char* s
}
template <class T>
PreparedStatement* DatabaseWorkerPool<T>::GetPreparedStatement(uint32 index)
void DatabaseWorkerPool<T>::ExecuteOrAppend(SQLTransaction<T>& trans, PreparedStatement<T>* stmt)
{
return new PreparedStatement(index);
if (!trans)
Execute(stmt);
else
trans->Append(stmt);
}
template <class T>
void DatabaseWorkerPool<T>::KeepAlive()
{
//! Ping synchronous connections
for (uint8 i = 0; i < _connectionCount[IDX_SYNCH]; ++i)
{
T* t = _connections[IDX_SYNCH][i];
if (t->LockIfReady())
{
t->Ping();
t->Unlock();
}
}
//! Assuming all worker threads are free, every worker thread will receive 1 ping operation request
//! 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.
for (size_t i = 0; i < _connections[IDX_ASYNC].size(); ++i)
Enqueue(new PingOperation);
}
template <class T>
T* DatabaseWorkerPool<T>::GetFreeConnection()
{
uint8 i = 0;
size_t num_cons = _connectionCount[IDX_SYNCH];
T* t = nullptr;
//! Block forever until a connection is free
for (;;)
{
t = _connections[IDX_SYNCH][++i % num_cons];
//! Must be matched with t->Unlock() or you will get deadlocks
if (t->LockIfReady())
break;
}
return t;
}
template class DatabaseWorkerPool<LoginDatabaseConnection>;
template class DatabaseWorkerPool<WorldDatabaseConnection>;
template class DatabaseWorkerPool<CharacterDatabaseConnection>;
template class AC_DATABASE_API DatabaseWorkerPool<LoginDatabaseConnection>;
template class AC_DATABASE_API DatabaseWorkerPool<WorldDatabaseConnection>;
template class AC_DATABASE_API DatabaseWorkerPool<CharacterDatabaseConnection>;

View file

@ -1,45 +1,45 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _DATABASEWORKERPOOL_H
#define _DATABASEWORKERPOOL_H
#include "Common.h"
#include "Callback.h"
#include "MySQLConnection.h"
#include "Transaction.h"
#include "DatabaseWorker.h"
#include "PreparedStatement.h"
#include "Log.h"
#include "QueryResult.h"
#include "QueryHolder.h"
#include "AdhocStatement.h"
#include "DatabaseEnvFwd.h"
#include "Define.h"
#include "StringFormat.h"
#include <mutex>
#include <array>
#include <string>
#include <vector>
class PingOperation : public SQLOperation
{
//! Operation for idle delaythreads
bool Execute() override
{
m_conn->Ping();
return true;
}
};
template <typename T>
class ProducerConsumerQueue;
class SQLOperation;
struct MySQLConnectionInfo;
template <class T>
class DatabaseWorkerPool
{
private:
enum InternalIndex
{
IDX_ASYNC,
IDX_SYNCH,
IDX_SIZE
};
public:
/* Activity state */
DatabaseWorkerPool();
~DatabaseWorkerPool() = default;
~DatabaseWorkerPool();
void SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads);
uint32 Open();
void Close();
//! Prepares all prepared statements
@ -56,12 +56,12 @@ 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(const char* sql);
void Execute(char const* sql);
//! Enqueues a one-way SQL operation in string format -with variable args- that will be executed asynchronously.
//! This method should only be used for queries that are only executed once, e.g during startup.
template<typename Format, typename... Args>
void PExecute(Format&& sql, Args&& ... args)
void PExecute(Format&& sql, Args&&... args)
{
if (Acore::IsFormatEmptyOrNull(sql))
return;
@ -71,7 +71,7 @@ public:
//! Enqueues a one-way SQL operation in prepared statement format that will be executed asynchronously.
//! Statement must be prepared with CONNECTION_ASYNC flag.
void Execute(PreparedStatement* stmt);
void Execute(PreparedStatement<T>* stmt);
/**
Direct synchronous one-way statement methods.
@ -79,12 +79,12 @@ 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(const char* sql);
void DirectExecute(char const* sql);
//! Directly executes a one-way SQL operation in string format -with variable args-, that will block the calling thread until finished.
//! This method should only be used for queries that are only executed once, e.g during startup.
template<typename Format, typename... Args>
void DirectPExecute(Format&& sql, Args&& ... args)
void DirectPExecute(Format&& sql, Args&&... args)
{
if (Acore::IsFormatEmptyOrNull(sql))
return;
@ -94,7 +94,7 @@ public:
//! Directly executes a one-way SQL operation in prepared statement format, that will block the calling thread until finished.
//! Statement must be prepared with the CONNECTION_SYNCH flag.
void DirectExecute(PreparedStatement* stmt);
void DirectExecute(PreparedStatement<T>* stmt);
/**
Synchronous query (with resultset) methods.
@ -102,12 +102,12 @@ 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(const char* sql, T* conn = nullptr);
QueryResult Query(char const* sql, T* connection = nullptr);
//! Directly executes an SQL query in string format -with variable args- that will block the calling thread until finished.
//! Returns reference counted auto pointer, no need for manual memory management in upper level code.
template<typename Format, typename... Args>
QueryResult PQuery(Format&& sql, T* conn, Args&& ... args)
QueryResult PQuery(Format&& sql, T* conn, Args&&... args)
{
if (Acore::IsFormatEmptyOrNull(sql))
return QueryResult(nullptr);
@ -118,7 +118,7 @@ public:
//! 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)
QueryResult PQuery(Format&& sql, Args&&... args)
{
if (Acore::IsFormatEmptyOrNull(sql))
return QueryResult(nullptr);
@ -129,7 +129,7 @@ public:
//! Directly executes an SQL query in prepared format that will block the calling thread until finished.
//! Returns reference counted auto pointer, no need for manual memory management in upper level code.
//! Statement must be prepared with CONNECTION_SYNCH flag.
PreparedQueryResult Query(PreparedStatement* stmt);
PreparedQueryResult Query(PreparedStatement<T>* stmt);
/**
Asynchronous query (with resultset) methods.
@ -137,113 +137,92 @@ 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.
QueryResultFuture AsyncQuery(const char* sql);
//! Enqueues a query in string format -with variable args- 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.
template<typename Format, typename... Args>
QueryResultFuture AsyncPQuery(Format&& sql, Args&& ... args)
{
if (Acore::IsFormatEmptyOrNull(sql))
return QueryResult(nullptr);
return AsyncQuery(Acore::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
}
QueryCallback AsyncQuery(char const* sql);
//! Enqueues a query in prepared format that will set the value of the PreparedQueryResultFuture return object as soon as the query is executed.
//! The return value is then processed in ProcessQueryCallback methods.
//! Statement must be prepared with CONNECTION_ASYNC flag.
PreparedQueryResultFuture AsyncQuery(PreparedStatement* stmt);
QueryCallback AsyncQuery(PreparedStatement<T>* stmt);
//! Enqueues a vector of SQL operations (can be both adhoc and prepared) that will set the value of the QueryResultHolderFuture
//! return object as soon as the query is executed.
//! The return value is then processed in ProcessQueryCallback methods.
//! Any prepared statements added to this holder need to be prepared with the CONNECTION_ASYNC flag.
QueryResultHolderFuture DelayQueryHolder(SQLQueryHolder* holder);
SQLQueryHolderCallback DelayQueryHolder(std::shared_ptr<SQLQueryHolder<T>> holder);
/**
Transaction context methods.
*/
//! Begins an automanaged transaction pointer that will automatically rollback if not commited. (Autocommit=0)
SQLTransaction BeginTransaction();
SQLTransaction<T> BeginTransaction();
//! Enqueues a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations
//! were appended to the transaction will be respected during execution.
void CommitTransaction(SQLTransaction transaction);
void CommitTransaction(SQLTransaction<T> transaction);
//! Enqueues a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations
//! were appended to the transaction will be respected during execution.
TransactionCallback AsyncCommitTransaction(SQLTransaction<T> transaction);
//! Directly executes a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations
//! were appended to the transaction will be respected during execution.
void DirectCommitTransaction(SQLTransaction& transaction);
//! Method used to execute prepared statements in a diverse context.
//! Will be wrapped in a transaction if valid object is present, otherwise executed standalone.
void ExecuteOrAppend(SQLTransaction& trans, PreparedStatement* stmt);
void DirectCommitTransaction(SQLTransaction<T>& transaction);
//! Method used to execute ad-hoc statements in a diverse context.
//! Will be wrapped in a transaction if valid object is present, otherwise executed standalone.
void ExecuteOrAppend(SQLTransaction& trans, const char* sql);
void ExecuteOrAppend(SQLTransaction<T>& trans, char const* 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.
void ExecuteOrAppend(SQLTransaction<T>& trans, PreparedStatement<T>* stmt);
/**
Other
*/
typedef typename T::Statements PreparedStatementIndex;
//! Automanaged (internally) pointer to a prepared statement object for usage in upper level code.
//! Pointer is deleted in this->DirectExecute(PreparedStatement*), this->Query(PreparedStatement*) or PreparedStatementTask::~PreparedStatementTask.
//! This object is not tied to the prepared statement on the MySQL context yet until execution.
PreparedStatement* GetPreparedStatement(uint32 index);
PreparedStatement<T>* GetPreparedStatement(PreparedStatementIndex index);
//! Apply escape string'ing for current collation. (utf8)
unsigned long EscapeString(char* to, const char* from, unsigned long length)
{
if (!to || !from || !length)
return 0;
return mysql_real_escape_string(_connections[IDX_SYNCH][0]->GetHandle(), to, from, length);
}
void EscapeString(std::string& str);
//! Keeps all our MySQL connections alive, prevent the server from disconnecting us.
void KeepAlive();
void EscapeString(std::string& str)
void WarnAboutSyncQueries([[maybe_unused]] bool warn)
{
if (str.empty())
return;
char* buf = new char[str.size() * 2 + 1];
EscapeString(buf, str.c_str(), str.size());
str = buf;
delete[] buf;
#ifdef ACORE_DEBUG
_warnSyncQueries = warn;
#endif
}
private:
enum InternalIndex
{
IDX_ASYNC,
IDX_SYNCH,
IDX_SIZE
};
uint32 OpenConnections(InternalIndex type, uint8 numConnections);
void Enqueue(SQLOperation* op)
{
_queue->enqueue(op);
}
unsigned long EscapeString(char* to, char const* from, unsigned long length);
[[nodiscard]] char const* GetDatabaseName() const;
void Enqueue(SQLOperation* op);
//! Gets a free connection in the synchronous connection pool.
//! Caller MUST call t->Unlock() after touching the MySQL context to prevent deadlocks.
T* GetFreeConnection();
ACE_Message_Queue<ACE_SYNCH>* _mqueue;
ACE_Activation_Queue* _queue; //! Queue shared by async worker threads.
std::vector<std::vector<T*>> _connections;
uint32 _connectionCount[IDX_SIZE]; //! Counter of MySQL connections;
char const* GetDatabaseName() const;
//! Queue shared by async worker threads.
std::unique_ptr<ProducerConsumerQueue<SQLOperation*>> _queue;
std::array<std::vector<std::unique_ptr<T>>, IDX_SIZE> _connections;
std::unique_ptr<MySQLConnectionInfo> _connectionInfo;
std::vector<uint8> _preparedStatementSize;
uint8 _async_threads, _synch_threads;
#ifdef ACORE_DEBUG
static inline thread_local bool _warnSyncQueries = false;
#endif
};
#endif

View file

@ -1,57 +1,240 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "Field.h"
#include "Errors.h"
#include "Log.h"
#include "MySQLHacks.h"
Field::Field()
{
data.value = nullptr;
data.type = MYSQL_TYPE_NULL;
data.length = 0;
data.raw = false;
meta = nullptr;
}
Field::~Field()
{
CleanUp();
}
Field::~Field() = default;
void Field::SetByteValue(const void* newValue, const size_t newSize, enum_field_types newType, uint32 length)
uint8 Field::GetUInt8() const
{
if (data.value)
CleanUp();
if (!data.value)
return 0;
// This value stores raw bytes that have to be explicitly cast later
if (newValue)
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int8))
{
data.value = new char[newSize];
memcpy(data.value, newValue, newSize);
data.length = length;
LogWrongType(__FUNCTION__);
return 0;
}
data.type = newType;
data.raw = true;
#endif
if (data.raw)
return *reinterpret_cast<uint8 const*>(data.value);
return static_cast<uint8>(strtoul(data.value, nullptr, 10));
}
void Field::SetStructuredValue(char* newValue, enum_field_types newType, uint32 length)
int8 Field::GetInt8() const
{
if (data.value)
CleanUp();
if (!data.value)
return 0;
// This value stores somewhat structured data that needs function style casting
if (newValue)
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
if (!IsType(DatabaseFieldTypes::Int8))
{
data.value = new char[length + 1];
memcpy(data.value, newValue, length);
*(reinterpret_cast<char*>(data.value) + length) = '\0';
data.length = length;
LogWrongType(__FUNCTION__);
return 0;
}
#endif
data.type = newType;
data.raw = false;
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))
{
LogWrongType(__FUNCTION__);
return 0;
}
#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))
{
LogWrongType(__FUNCTION__);
return 0;
}
#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
@ -67,6 +250,48 @@ std::vector<uint8> Field::GetBinary() const
void Field::GetBinarySizeChecked(uint8* buf, size_t length) const
{
ASSERT(data.value && (data.length == length));
ASSERT(data.value && (data.length == length), "Expected %zu-byte binary blob, got %sdata (%u bytes) instead", length, data.value ? "" : "no ", data.length);
memcpy(buf, data.value, length);
}
void Field::SetByteValue(char const* newValue, uint32 length)
{
// This value stores raw bytes that have to be explicitly cast later
data.value = newValue;
data.length = length;
data.raw = true;
}
void Field::SetStructuredValue(char const* newValue, uint32 length)
{
// This value stores somewhat structured data that needs function style casting
data.value = newValue;
data.length = length;
data.raw = false;
}
bool Field::IsType(DatabaseFieldTypes type) const
{
return meta->Type == type;
}
bool Field::IsNumeric() const
{
return (meta->Type == DatabaseFieldTypes::Int8 ||
meta->Type == DatabaseFieldTypes::Int16 ||
meta->Type == DatabaseFieldTypes::Int32 ||
meta->Type == DatabaseFieldTypes::Int64 ||
meta->Type == DatabaseFieldTypes::Float ||
meta->Type == DatabaseFieldTypes::Double);
}
void Field::LogWrongType(char const* getter) 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);
}
void Field::SetMetadata(QueryResultFieldMetadata const* fieldMeta)
{
meta = fieldMeta;
}

View file

@ -1,437 +1,135 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef AZEROTHCORE_FIELD_H
#define AZEROTHCORE_FIELD_H
#ifndef _FIELD_H
#define _FIELD_H
#include "Common.h"
#include "Log.h"
#include "DatabaseEnvFwd.h"
#include "Define.h"
#include <array>
#include <mysql.h>
#include <string>
#include <string_view>
#include <vector>
class Field
enum class DatabaseFieldTypes : uint8
{
friend class ResultSet;
friend class PreparedResultSet;
Null,
Int8,
Int16,
Int32,
Int64,
Float,
Double,
Decimal,
Date,
Binary
};
struct QueryResultFieldMetadata
{
char const* TableName = nullptr;
char const* TableAlias = nullptr;
char const* Name = nullptr;
char const* Alias = nullptr;
char const* TypeName = nullptr;
uint32 Index = 0;
DatabaseFieldTypes Type = DatabaseFieldTypes::Null;
};
/**
@class Field
@brief Class used to access individual fields of database query result
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 |
Return types of aggregate functions:
| Function | Type |
|----------|-------------------|
| MIN, MAX | Same as the field |
| SUM, AVG | DECIMAL |
| COUNT | BIGINT |
*/
class AC_DATABASE_API Field
{
friend class ResultSet;
friend class PreparedResultSet;
public:
[[nodiscard]] bool GetBool() const // Wrapper, actually gets integer
Field();
~Field();
bool GetBool() const // Wrapper, actually gets integer
{
return (GetUInt8() == 1);
return GetUInt8() == 1 ? true : false;
}
[[nodiscard]] uint8 GetUInt8() const
{
if (!data.value)
return 0;
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;
#ifdef ACORE_DEBUG
if (!IsType(MYSQL_TYPE_TINY))
{
LOG_INFO("sql.driver", "Warning: GetUInt8() on non-tinyint field. Using type: %s.", FieldTypeToString(data.type));
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<uint8*>(data.value);
return static_cast<uint8>(atol((char*)data.value));
}
[[nodiscard]] int8 GetInt8() const
{
if (!data.value)
return 0;
#ifdef ACORE_DEBUG
if (!IsType(MYSQL_TYPE_TINY))
{
LOG_INFO("sql.driver", "Warning: GetInt8() on non-tinyint field. Using type: %s.", FieldTypeToString(data.type));
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<int8*>(data.value);
return static_cast<int8>(atol((char*)data.value));
}
#ifdef ELUNA
enum_field_types GetType() const
{
return data.type;
}
#endif
[[nodiscard]] uint16 GetUInt16() const
{
if (!data.value)
return 0;
#ifdef ACORE_DEBUG
if (!IsType(MYSQL_TYPE_SHORT) && !IsType(MYSQL_TYPE_YEAR))
{
LOG_INFO("sql.driver", "Warning: GetUInt16() on non-smallint field. Using type: %s.", FieldTypeToString(data.type));
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<uint16*>(data.value);
return static_cast<uint16>(atol((char*)data.value));
}
[[nodiscard]] int16 GetInt16() const
{
if (!data.value)
return 0;
#ifdef ACORE_DEBUG
if (!IsType(MYSQL_TYPE_SHORT) && !IsType(MYSQL_TYPE_YEAR))
{
LOG_INFO("sql.driver", "Warning: GetInt16() on non-smallint field. Using type: %s.", FieldTypeToString(data.type));
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<int16*>(data.value);
return static_cast<int16>(atol((char*)data.value));
}
[[nodiscard]] uint32 GetUInt32() const
{
if (!data.value)
return 0;
#ifdef ACORE_DEBUG
if (!IsType(MYSQL_TYPE_INT24) && !IsType(MYSQL_TYPE_LONG))
{
LOG_INFO("sql.driver", "Warning: GetUInt32() on non-(medium)int field. Using type: %s.", FieldTypeToString(data.type));
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<uint32*>(data.value);
return static_cast<uint32>(atol((char*)data.value));
}
[[nodiscard]] int32 GetInt32() const
{
if (!data.value)
return 0;
#ifdef ACORE_DEBUG
if (!IsType(MYSQL_TYPE_INT24) && !IsType(MYSQL_TYPE_LONG))
{
LOG_INFO("sql.driver", "Warning: GetInt32() on non-(medium)int field. Using type: %s.", FieldTypeToString(data.type));
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<int32*>(data.value);
return static_cast<int32>(atol((char*)data.value));
}
[[nodiscard]] uint64 GetUInt64() const
{
if (!data.value)
return 0;
#ifdef ACORE_DEBUG
if (!IsType(MYSQL_TYPE_LONGLONG) && !IsType(MYSQL_TYPE_BIT))
{
LOG_INFO("sql.driver", "Warning: GetUInt64() on non-bigint field. Using type: %s.", FieldTypeToString(data.type));
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<uint64*>(data.value);
return static_cast<uint64>(atol((char*)data.value));
}
[[nodiscard]] int64 GetInt64() const
{
if (!data.value)
return 0;
#ifdef ACORE_DEBUG
if (!IsType(MYSQL_TYPE_LONGLONG) && !IsType(MYSQL_TYPE_BIT))
{
LOG_INFO("sql.driver", "Warning: GetInt64() on non-bigint field. Using type: %s.", FieldTypeToString(data.type));
return 0;
}
#endif
if (data.raw)
return *reinterpret_cast<int64*>(data.value);
return static_cast<int64>(strtol((char*)data.value, nullptr, 10));
}
[[nodiscard]] float GetFloat() const
{
if (!data.value)
return 0.0f;
#ifdef ACORE_DEBUG
if (!IsType(MYSQL_TYPE_FLOAT))
{
LOG_INFO("sql.driver", "Warning: GetFloat() on non-float field. Using type: %s.", FieldTypeToString(data.type));
return 0.0f;
}
#endif
if (data.raw)
return *reinterpret_cast<float*>(data.value);
return static_cast<float>(atof((char*)data.value));
}
[[nodiscard]] double GetDouble() const
{
if (!data.value)
return 0.0f;
#ifdef ACORE_DEBUG
if (!IsType(MYSQL_TYPE_DOUBLE))
{
LOG_INFO("sql.driver", "Warning: GetDouble() on non-double field. Using type: %s.", FieldTypeToString(data.type));
return 0.0f;
}
#endif
if (data.raw)
return *reinterpret_cast<double*>(data.value);
return static_cast<double>(atof((char*)data.value));
}
[[nodiscard]] char const* GetCString() const
{
if (!data.value)
return nullptr;
#ifdef ACORE_DEBUG
if (IsNumeric())
{
LOG_INFO("sql.driver", "Error: GetCString() on numeric field. Using type: %s.", FieldTypeToString(data.type));
return nullptr;
}
#endif
return static_cast<char const*>(data.value);
}
[[nodiscard]] std::string GetString() const
{
if (!data.value)
return "";
if (data.raw)
{
char const* string = GetCString();
if (!string)
string = "";
return std::string(string, data.length);
}
return std::string((char*)data.value);
}
[[nodiscard]] bool IsNull() const
{
if (IsBinary() && data.length == 0)
{
return true;
}
return data.value == nullptr;
}
[[nodiscard]] std::vector<uint8> GetBinary() const;
template<size_t S>
[[nodiscard]] std::array<uint8, S> GetBinary() const
template <size_t S>
std::array<uint8, S> GetBinary() const
{
std::array<uint8, S> buf;
GetBinarySizeChecked(buf.data(), S);
return buf;
}
protected:
Field();
~Field();
bool IsNull() const
{
return data.value == nullptr;
}
#if defined(__GNUC__)
#pragma pack(1)
#else
#pragma pack(push, 1)
#endif
DatabaseFieldTypes GetType() { return meta->Type; }
protected:
struct
{
uint32 length; // Length (prepared strings only)
void* value; // Actual data in memory
enum_field_types type; // Field type
char const* value; // Actual data in memory
uint32 length; // Length
bool raw; // Raw bytes? (Prepared statement or ad hoc)
} data;
#if defined(__GNUC__)
#pragma pack()
#else
#pragma pack(pop)
#endif
void SetByteValue(void const* newValue, size_t const newSize, enum_field_types newType, uint32 length);
void SetStructuredValue(char* newValue, enum_field_types newType, uint32 length);
void CleanUp()
{
delete[] ((char*)data.value);
data.value = nullptr;
}
static size_t SizeForType(MYSQL_FIELD* field)
{
switch (field->type)
{
case MYSQL_TYPE_NULL:
return 0;
case MYSQL_TYPE_TINY:
return 1;
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_SHORT:
return 2;
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_LONG:
case MYSQL_TYPE_FLOAT:
return 4;
case MYSQL_TYPE_DOUBLE:
case MYSQL_TYPE_LONGLONG:
case MYSQL_TYPE_BIT:
return 8;
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATETIME:
return sizeof(MYSQL_TIME);
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_STRING:
case MYSQL_TYPE_VAR_STRING:
return field->max_length + 1;
case MYSQL_TYPE_DECIMAL:
case MYSQL_TYPE_NEWDECIMAL:
return 64;
case MYSQL_TYPE_GEOMETRY:
/*
Following types are not sent over the wire:
MYSQL_TYPE_ENUM:
MYSQL_TYPE_SET:
*/
default:
LOG_INFO("sql.driver", "SQL::SizeForType(): invalid field type %u", uint32(field->type));
return 0;
}
}
[[nodiscard]] bool IsType(enum_field_types type) const
{
return data.type == type;
}
[[nodiscard]] bool IsNumeric() const
{
return (data.type == MYSQL_TYPE_TINY ||
data.type == MYSQL_TYPE_SHORT ||
data.type == MYSQL_TYPE_INT24 ||
data.type == MYSQL_TYPE_LONG ||
data.type == MYSQL_TYPE_FLOAT ||
data.type == MYSQL_TYPE_DOUBLE ||
data.type == MYSQL_TYPE_LONGLONG );
}
[[nodiscard]] bool IsBinary() const
{
return (
data.type == MYSQL_TYPE_TINY_BLOB ||
data.type == MYSQL_TYPE_MEDIUM_BLOB ||
data.type == MYSQL_TYPE_LONG_BLOB ||
data.type == MYSQL_TYPE_BLOB ||
data.type == MYSQL_TYPE_VAR_STRING ||
data.type == MYSQL_TYPE_STRING
);
}
void GetBinarySizeChecked(uint8* buf, size_t size) const;
void SetByteValue(char const* newValue, uint32 length);
void SetStructuredValue(char const* newValue, uint32 length);
bool IsType(DatabaseFieldTypes type) const;
bool IsNumeric() const;
private:
#ifdef ACORE_DEBUG
static char const* FieldTypeToString(enum_field_types type)
{
switch (type)
{
case MYSQL_TYPE_BIT:
return "BIT";
case MYSQL_TYPE_BLOB:
return "BLOB";
case MYSQL_TYPE_DATE:
return "DATE";
case MYSQL_TYPE_DATETIME:
return "DATETIME";
case MYSQL_TYPE_NEWDECIMAL:
return "NEWDECIMAL";
case MYSQL_TYPE_DECIMAL:
return "DECIMAL";
case MYSQL_TYPE_DOUBLE:
return "DOUBLE";
case MYSQL_TYPE_ENUM:
return "ENUM";
case MYSQL_TYPE_FLOAT:
return "FLOAT";
case MYSQL_TYPE_GEOMETRY:
return "GEOMETRY";
case MYSQL_TYPE_INT24:
return "INT24";
case MYSQL_TYPE_LONG:
return "LONG";
case MYSQL_TYPE_LONGLONG:
return "LONGLONG";
case MYSQL_TYPE_LONG_BLOB:
return "LONG_BLOB";
case MYSQL_TYPE_MEDIUM_BLOB:
return "MEDIUM_BLOB";
case MYSQL_TYPE_NEWDATE:
return "NEWDATE";
case MYSQL_TYPE_NULL:
return "nullptr";
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-";
}
}
#endif
QueryResultFieldMetadata const* meta;
void LogWrongType(char const* getter) const;
void SetMetadata(QueryResultFieldMetadata const* fieldMeta);
void GetBinarySizeChecked(uint8* buf, size_t size) const;
};
#endif

View file

@ -1,10 +1,10 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "CharacterDatabase.h"
#include "MySQLPreparedStatement.h"
void CharacterDatabaseConnection::DoPrepareStatements()
{
@ -37,7 +37,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"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.guid", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT guid, name FROM characters WHERE guid = ? AND account = ? AND (at_login & ?) = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", 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);
PrepareStatement(CHAR_SEL_CHAR_POSITION_XYZ, "SELECT map, position_x, position_y, position_z FROM characters WHERE guid = ?", CONNECTION_SYNCH);
@ -138,7 +138,6 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES, "INSERT INTO account_instance_times (accountId, instanceId, releaseTime) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_MATCH_MAKER_RATING, "SELECT matchMakerRating, maxMMR FROM character_arena_stats WHERE guid = ? AND slot = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHARACTER_COUNT, "SELECT ? AS account,(SELECT COUNT(*) FROM characters WHERE account =?) AS cnt", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_NAME, "UPDATE characters set name = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_DECLINED_NAME, "DELETE FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC);
// Guild handling
@ -361,7 +360,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_INVALID_PET_SPELL, "DELETE FROM pet_spell WHERE spell = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_GLOBAL_INSTANCE_RESETTIME, "UPDATE instance_reset SET resettime = ? WHERE mapid = ? AND difficulty = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_ONLINE, "UPDATE characters SET online = 1 WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN, "UPDATE characters set name = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN, "UPDATE characters set name = ?, at_login = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_WORLDSTATE, "UPDATE worldstates SET value = ? WHERE entry = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_WORLDSTATE, "INSERT INTO worldstates (entry, value) VALUES (?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE, "DELETE FROM character_instance WHERE instance = ?", CONNECTION_ASYNC);
@ -396,6 +395,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_POOL_QUEST_SAVE, "SELECT quest_id FROM pool_quest_save WHERE pool_id = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHARACTER_AT_LOGIN, "SELECT at_login FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN, "SELECT class, level, at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_CUSTOMIZE_INFO, "SELECT name, race, class, gender, at_login FROM characters WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS, "SELECT at_login, knownTitles, money FROM characters WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_AT_LOGIN_TITLES_MONEY, "SELECT at_login, knownTitles, money FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL, "SELECT id, messageType, mailTemplateId, sender, subject, body, money, has_items FROM mail WHERE receiver = ? AND has_items <> 0 AND cod <> 0", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_SOCIAL, "SELECT DISTINCT guid FROM character_social WHERE friend = ?", CONNECTION_SYNCH);
@ -437,7 +438,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_GUID, "DELETE FROM petition_sign WHERE petitionguid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_DECLINED_NAME, "DELETE FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHAR_DECLINED_NAME, "INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_FACTION_OR_RACE, "UPDATE characters SET name = ?, race = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_RACE, "UPDATE characters SET race = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_SKILL_LANGUAGES, "DELETE FROM character_skills WHERE skill IN (98, 113, 759, 111, 313, 109, 115, 315, 673, 137) AND guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHAR_SKILL_LANGUAGE, "INSERT INTO `character_skills` (guid, skill, value, max) VALUES (?, ?, 300, 300)", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_TAXI_PATH, "UPDATE characters SET taxi_path = '' WHERE guid = ?", CONNECTION_ASYNC);
@ -546,6 +547,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
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_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);
@ -578,3 +580,15 @@ void CharacterDatabaseConnection::DoPrepareStatements()
// Character names
PrepareStatement(CHAR_INS_RESERVED_PLAYER_NAME, "INSERT IGNORE INTO reserved_name (name) VALUES (?)", CONNECTION_ASYNC);
}
CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
{
}
CharacterDatabaseConnection::CharacterDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo)
{
}
CharacterDatabaseConnection::~CharacterDatabaseConnection()
{
}

View file

@ -1,29 +1,14 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _CHARACTERDATABASE_H
#define _CHARACTERDATABASE_H
#include "DatabaseWorkerPool.h"
#include "MySQLConnection.h"
class CharacterDatabaseConnection : public MySQLConnection
{
public:
//- Constructors for sync and async connections
CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) {}
CharacterDatabaseConnection(ACE_Activation_Queue* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) {}
//- Loads database type specific prepared statements
void DoPrepareStatements() override;
};
typedef DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabaseWorkerPool;
enum CharacterDatabaseStatements
enum CharacterDatabaseStatements : uint32
{
/* Naming standard for defines:
{DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed}
@ -137,7 +122,6 @@ enum CharacterDatabaseStatements
CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES,
CHAR_SEL_MATCH_MAKER_RATING,
CHAR_SEL_CHARACTER_COUNT,
CHAR_UPD_NAME,
CHAR_DEL_DECLINED_NAME,
CHAR_INS_GUILD,
@ -342,6 +326,8 @@ enum CharacterDatabaseStatements
CHAR_SEL_POOL_QUEST_SAVE,
CHAR_SEL_CHARACTER_AT_LOGIN,
CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN,
CHAR_SEL_CHAR_CUSTOMIZE_INFO,
CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS,
CHAR_SEL_CHAR_AT_LOGIN_TITLES_MONEY,
CHAR_SEL_CHAR_COD_ITEM_MAIL,
CHAR_SEL_CHAR_SOCIAL,
@ -378,7 +364,7 @@ enum CharacterDatabaseStatements
CHAR_DEL_PETITION_SIGNATURE_BY_GUID,
CHAR_DEL_CHAR_DECLINED_NAME,
CHAR_INS_CHAR_DECLINED_NAME,
CHAR_UPD_FACTION_OR_RACE,
CHAR_UPD_CHAR_RACE,
CHAR_DEL_CHAR_SKILL_LANGUAGES,
CHAR_INS_CHAR_SKILL_LANGUAGE,
CHAR_UPD_CHAR_TAXI_PATH,
@ -466,6 +452,7 @@ enum CharacterDatabaseStatements
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,
@ -512,4 +499,18 @@ enum CharacterDatabaseStatements
MAX_CHARACTERDATABASE_STATEMENTS
};
class AC_DATABASE_API CharacterDatabaseConnection : public MySQLConnection
{
public:
typedef CharacterDatabaseStatements Statements;
//- Constructors for sync and async connections
CharacterDatabaseConnection(MySQLConnectionInfo& connInfo);
CharacterDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo);
~CharacterDatabaseConnection();
//- Loads database type specific prepared statements
void DoPrepareStatements() override;
};
#endif

View file

@ -1,10 +1,10 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "LoginDatabase.h"
#include "MySQLPreparedStatement.h"
void LoginDatabaseConnection::DoPrepareStatements()
{
@ -110,3 +110,15 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_ACCOUNT_TOTP_SECRET, "SELECT totp_secret FROM account WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_UPD_ACCOUNT_TOTP_SECRET, "UPDATE account SET totp_secret = ? WHERE id = ?", CONNECTION_ASYNC);
}
LoginDatabaseConnection::LoginDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
{
}
LoginDatabaseConnection::LoginDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo)
{
}
LoginDatabaseConnection::~LoginDatabaseConnection()
{
}

View file

@ -1,29 +1,14 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _LOGINDATABASE_H
#define _LOGINDATABASE_H
#include "DatabaseWorkerPool.h"
#include "MySQLConnection.h"
class LoginDatabaseConnection : public MySQLConnection
{
public:
//- Constructors for sync and async connections
LoginDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { }
LoginDatabaseConnection(ACE_Activation_Queue* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { }
//- Loads database type specific prepared statements
void DoPrepareStatements() override;
};
typedef DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabaseWorkerPool;
enum LoginDatabaseStatements
enum LoginDatabaseStatements : uint32
{
/* Naming standard for defines:
{DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed}
@ -121,4 +106,18 @@ enum LoginDatabaseStatements
MAX_LOGINDATABASE_STATEMENTS
};
class AC_DATABASE_API LoginDatabaseConnection : public MySQLConnection
{
public:
typedef LoginDatabaseStatements Statements;
//- Constructors for sync and async connections
LoginDatabaseConnection(MySQLConnectionInfo& connInfo);
LoginDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo);
~LoginDatabaseConnection();
//- Loads database type specific prepared statements
void DoPrepareStatements() override;
};
#endif

View file

@ -1,10 +1,10 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "WorldDatabase.h"
#include "MySQLPreparedStatement.h"
void WorldDatabaseConnection::DoPrepareStatements()
{
@ -84,3 +84,15 @@ void WorldDatabaseConnection::DoPrepareStatements()
// 0: uint8
PrepareStatement(WORLD_SEL_REQ_XP, "SELECT Experience FROM player_xp_for_level WHERE Level = ?", CONNECTION_SYNCH);
}
WorldDatabaseConnection::WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
{
}
WorldDatabaseConnection::WorldDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo)
{
}
WorldDatabaseConnection::~WorldDatabaseConnection()
{
}

View file

@ -1,29 +1,14 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _WORLDDATABASE_H
#define _WORLDDATABASE_H
#include "DatabaseWorkerPool.h"
#include "MySQLConnection.h"
class WorldDatabaseConnection : public MySQLConnection
{
public:
//- Constructors for sync and async connections
WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { }
WorldDatabaseConnection(ACE_Activation_Queue* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { }
//- Loads database type specific prepared statements
void DoPrepareStatements() override;
};
typedef DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabaseWorkerPool;
enum WorldDatabaseStatements
enum WorldDatabaseStatements : uint32
{
/* Naming standard for defines:
{DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed}
@ -107,4 +92,18 @@ enum WorldDatabaseStatements
MAX_WORLDDATABASE_STATEMENTS
};
class AC_DATABASE_API WorldDatabaseConnection : public MySQLConnection
{
public:
typedef WorldDatabaseStatements Statements;
//- Constructors for sync and async connections
WorldDatabaseConnection(MySQLConnectionInfo& connInfo);
WorldDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo);
~WorldDatabaseConnection();
//- Loads database type specific prepared statements
void DoPrepareStatements() override;
};
#endif

View file

@ -1,54 +1,71 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "Common.h"
#include "MySQLConnection.h"
#include "MySQLThreading.h"
#include "QueryResult.h"
#include "SQLOperation.h"
#include "PreparedStatement.h"
#include "Common.h"
#include "DatabaseWorker.h"
#include "Timer.h"
#include "Log.h"
#include "Duration.h"
#include <mysql.h>
#include <mysqld_error.h>
#include "MySQLHacks.h"
#include "MySQLPreparedStatement.h"
#include "MySQLWorkaround.h"
#include "PreparedStatement.h"
#include "QueryResult.h"
#include "Timer.h"
#include "Tokenize.h"
#include "Transaction.h"
#include "Util.h"
#include <errmsg.h>
#include <thread>
#include <mysqld_error.h>
#ifdef _WIN32
#include <winsock2.h>
#endif
MySQLConnection::MySQLConnection(MySQLConnectionInfo& connInfo) :
m_reconnecting(false),
m_prepareError(false),
m_queue(nullptr),
m_worker(nullptr),
m_Mysql(nullptr),
m_connectionInfo(connInfo),
m_connectionFlags(CONNECTION_SYNCH)
MySQLConnectionInfo::MySQLConnectionInfo(std::string const& 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]);
if (tokens.size() == 6)
ssl.assign(tokens[5]);
}
MySQLConnection::MySQLConnection(ACE_Activation_Queue* queue, MySQLConnectionInfo& connInfo) :
m_reconnecting(false),
m_prepareError(false),
m_queue(queue),
m_Mysql(nullptr),
m_connectionInfo(connInfo),
m_connectionFlags(CONNECTION_ASYNC)
MySQLConnection::MySQLConnection(MySQLConnectionInfo& connInfo) :
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_worker = new DatabaseWorker(m_queue, this);
m_worker = std::make_unique<DatabaseWorker>(m_queue, this);
}
MySQLConnection::~MySQLConnection()
{
for (auto stmt : m_stmts)
delete stmt;
Close();
}
void MySQLConnection::Close()
{
// Stop the worker thread before the statements are cleared
m_worker.reset();
m_stmts.clear();
if (m_Mysql)
{
@ -57,19 +74,14 @@ MySQLConnection::~MySQLConnection()
}
}
void MySQLConnection::Close()
{
/// Only close us if we're not operating
delete this;
}
uint32 MySQLConnection::Open()
{
MYSQL* mysqlInit = mysql_init(nullptr);
MYSQL *mysqlInit;
mysqlInit = mysql_init(nullptr);
if (!mysqlInit)
{
LOG_ERROR("sql.sql", "Could not initialize Mysql connection to database `%s`", m_connectionInfo.database.c_str());
return false;
return CR_UNKNOWN_ERROR;
}
int port;
@ -78,7 +90,7 @@ uint32 MySQLConnection::Open()
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;
@ -91,7 +103,7 @@ uint32 MySQLConnection::Open()
port = atoi(m_connectionInfo.port_or_socket.c_str());
unix_socket = 0;
}
#else
#else
if (m_connectionInfo.host == ".") // socket use option (Unix/Linux)
{
unsigned int opt = MYSQL_PROTOCOL_SOCKET;
@ -103,19 +115,31 @@ uint32 MySQLConnection::Open()
else // generic case
{
port = atoi(m_connectionInfo.port_or_socket.c_str());
unix_socket = 0;
unix_socket = nullptr;
}
#endif
#endif
m_Mysql = mysql_real_connect(
mysqlInit,
m_connectionInfo.host.c_str(),
m_connectionInfo.user.c_str(),
m_connectionInfo.password.c_str(),
m_connectionInfo.database.c_str(),
port,
unix_socket,
0);
if (m_connectionInfo.ssl != "")
{
#if !defined(MARIADB_VERSION_ID) && MYSQL_VERSION_ID >= 80000
mysql_ssl_mode opt_use_ssl = SSL_MODE_DISABLED;
if (m_connectionInfo.ssl == "ssl")
{
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);
if (m_connectionInfo.ssl == "ssl")
{
opt_use_ssl = MySQLBool(1);
}
mysql_options(mysqlInit, MYSQL_OPT_SSL_ENFORCE, (char const*)&opt_use_ssl);
#endif
}
m_Mysql = reinterpret_cast<MySQLHandle*>(mysql_real_connect(mysqlInit, m_connectionInfo.host.c_str(), m_connectionInfo.user.c_str(),
m_connectionInfo.password.c_str(), m_connectionInfo.database.c_str(), port, unix_socket, 0));
if (m_Mysql)
{
@ -123,15 +147,12 @@ uint32 MySQLConnection::Open()
{
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));
if (mysql_get_server_version(m_Mysql) != mysql_get_client_version())
{
LOG_WARN("sql.sql", "[WARNING] MySQL client/server version mismatch; may conflict with behaviour of prepared statements.");
}
// 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", "Connected to MySQL database at %s", m_connectionInfo.host.c_str());
mysql_autocommit(m_Mysql, 1);
// set connection properties to UTF8 to properly handle locales for different
@ -139,11 +160,13 @@ uint32 MySQLConnection::Open()
mysql_set_character_set(m_Mysql, "utf8");
return 0;
}
LOG_ERROR("sql.sql", "Could not connect to MySQL database at %s: %s", m_connectionInfo.host.c_str(), mysql_error(mysqlInit));
uint32 errorCode = mysql_errno(mysqlInit);
mysql_close(mysqlInit);
return errorCode;
else
{
LOG_ERROR("sql.sql", "Could not connect to MySQL database at %s: %s", m_connectionInfo.host.c_str(), mysql_error(mysqlInit));
uint32 errorCode = mysql_errno(mysqlInit);
mysql_close(mysqlInit);
return errorCode;
}
}
bool MySQLConnection::PrepareStatements()
@ -152,154 +175,7 @@ bool MySQLConnection::PrepareStatements()
return !m_prepareError;
}
bool MySQLConnection::Execute(const char* sql)
{
if (!m_Mysql)
return false;
uint32 _s = getMSTime();
if (mysql_query(m_Mysql, sql))
{
uint32 lErrno = mysql_errno(m_Mysql);
LOG_ERROR("sql.sql", "SQL: %s", sql);
LOG_ERROR("sql.sql", "ERROR: [%u] %s", 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
return false;
}
LOG_DEBUG("sql.sql", "[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql);
return true;
}
bool MySQLConnection::Execute(PreparedStatement* stmt)
{
if (!m_Mysql)
return false;
uint32 index = stmt->m_index;
{
MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index);
ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query
m_mStmt->m_stmt = stmt; // Cross reference them for debug output
stmt->m_stmt = m_mStmt; // TODO: Cleaner way
stmt->BindParameters();
MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT();
MYSQL_BIND* msql_BIND = m_mStmt->GetBind();
uint32 _s = getMSTime();
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(m_queries[index].first).c_str(), 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
m_mStmt->ClearParameters();
return false;
}
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(m_queries[index].first).c_str(), 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
m_mStmt->ClearParameters();
return false;
}
LOG_DEBUG("sql.sql", "[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString(m_queries[index].first).c_str());
m_mStmt->ClearParameters();
return true;
}
}
bool MySQLConnection::_Query(PreparedStatement* stmt, MYSQL_RES** pResult, uint64* pRowCount, uint32* pFieldCount)
{
if (!m_Mysql)
return false;
uint32 index = stmt->m_index;
{
MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index);
ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query
m_mStmt->m_stmt = stmt; // Cross reference them for debug output
stmt->m_stmt = m_mStmt; // TODO: Cleaner way
stmt->BindParameters();
MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT();
MYSQL_BIND* msql_BIND = m_mStmt->GetBind();
uint32 _s = getMSTime();
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(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT));
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
return _Query(stmt, pResult, pRowCount, pFieldCount); // Try again
m_mStmt->ClearParameters();
return false;
}
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(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT));
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
return _Query(stmt, pResult, pRowCount, pFieldCount); // Try again
m_mStmt->ClearParameters();
return false;
}
LOG_DEBUG("sql.sql", "[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString(m_queries[index].first).c_str());
m_mStmt->ClearParameters();
*pResult = mysql_stmt_result_metadata(msql_STMT);
*pRowCount = mysql_stmt_num_rows(msql_STMT);
*pFieldCount = mysql_stmt_field_count(msql_STMT);
return true;
}
}
ResultSet* MySQLConnection::Query(const char* sql)
{
if (!sql)
return nullptr;
MYSQL_RES* result = nullptr;
MYSQL_FIELD* fields = nullptr;
uint64 rowCount = 0;
uint32 fieldCount = 0;
if (!_Query(sql, &result, &fields, &rowCount, &fieldCount))
return nullptr;
return new ResultSet(result, fields, rowCount, fieldCount);
}
bool MySQLConnection::_Query(const char* sql, MYSQL_RES** pResult, MYSQL_FIELD** pFields, uint64* pRowCount, uint32* pFieldCount)
bool MySQLConnection::Execute(char const* sql)
{
if (!m_Mysql)
return false;
@ -310,23 +186,167 @@ bool MySQLConnection::_Query(const char* sql, MYSQL_RES** pResult, MYSQL_FIELD**
if (mysql_query(m_Mysql, sql))
{
uint32 lErrno = mysql_errno(m_Mysql);
LOG_ERROR("sql.sql", "SQL: %s", sql);
LOG_ERROR("sql.sql", "ERROR: [%u] %s", lErrno, mysql_error(m_Mysql));
LOG_INFO("sql.sql", "SQL: %s", sql);
LOG_ERROR("sql.sql", "[%u] %s", 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
return false;
}
else
LOG_DEBUG("sql.sql", "[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql);
}
return true;
}
bool MySQLConnection::Execute(PreparedStatementBase* stmt)
{
if (!m_Mysql)
return false;
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
m_mStmt->BindParameters(stmt);
MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT();
MYSQL_BIND* msql_BIND = m_mStmt->GetBind();
uint32 _s = getMSTime();
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));
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
return Execute(stmt); // Try again
m_mStmt->ClearParameters();
return false;
}
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));
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
return Execute(stmt); // Try again
m_mStmt->ClearParameters();
return false;
}
LOG_DEBUG("sql.sql", "[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString().c_str());
m_mStmt->ClearParameters();
return true;
}
bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLPreparedStatement** mysqlStmt, MySQLResult** pResult, uint64* pRowCount, uint32* pFieldCount)
{
if (!m_Mysql)
return false;
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
m_mStmt->BindParameters(stmt);
*mysqlStmt = m_mStmt;
MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT();
MYSQL_BIND* msql_BIND = m_mStmt->GetBind();
uint32 _s = getMSTime();
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));
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
return _Query(stmt, mysqlStmt, pResult, pRowCount, pFieldCount); // Try again
m_mStmt->ClearParameters();
return false;
}
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));
if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
return _Query(stmt, mysqlStmt, pResult, pRowCount, pFieldCount); // Try again
m_mStmt->ClearParameters();
return false;
}
LOG_DEBUG("sql.sql", "[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString().c_str());
m_mStmt->ClearParameters();
*pResult = reinterpret_cast<MySQLResult*>(mysql_stmt_result_metadata(msql_STMT));
*pRowCount = mysql_stmt_num_rows(msql_STMT);
*pFieldCount = mysql_stmt_field_count(msql_STMT);
return true;
}
ResultSet* MySQLConnection::Query(char const* sql)
{
if (!sql)
return nullptr;
MySQLResult* result = nullptr;
MySQLField* fields = nullptr;
uint64 rowCount = 0;
uint32 fieldCount = 0;
if (!_Query(sql, &result, &fields, &rowCount, &fieldCount))
return nullptr;
return new ResultSet(result, fields, rowCount, fieldCount);
}
bool MySQLConnection::_Query(const char* sql, MySQLResult** pResult, MySQLField** pFields, uint64* pRowCount, uint32* pFieldCount)
{
if (!m_Mysql)
return false;
{
uint32 _s = getMSTime();
if (mysql_query(m_Mysql, sql))
{
uint32 lErrno = mysql_errno(m_Mysql);
LOG_INFO("sql.sql", "SQL: %s", sql);
LOG_ERROR("sql.sql", "[%u] %s", lErrno, mysql_error(m_Mysql));
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", "[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql);
*pResult = mysql_store_result(m_Mysql);
*pResult = reinterpret_cast<MySQLResult*>(mysql_store_result(m_Mysql));
*pRowCount = mysql_affected_rows(m_Mysql);
*pFieldCount = mysql_field_count(m_Mysql);
}
if (!*pResult)
if (!*pResult )
return false;
if (!*pRowCount)
@ -335,7 +355,7 @@ bool MySQLConnection::_Query(const char* sql, MYSQL_RES** pResult, MYSQL_FIELD**
return false;
}
*pFields = mysql_fetch_fields(*pResult);
*pFields = reinterpret_cast<MySQLField*>(mysql_fetch_fields(*pResult));
return true;
}
@ -355,27 +375,26 @@ void MySQLConnection::CommitTransaction()
Execute("COMMIT");
}
int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction)
int MySQLConnection::ExecuteTransaction(std::shared_ptr<TransactionBase> transaction)
{
std::list<SQLElementData> const& queries = transaction->m_queries;
std::vector<SQLElementData> const& queries = transaction->m_queries;
if (queries.empty())
return -1;
BeginTransaction();
std::list<SQLElementData>::const_iterator itr;
for (itr = queries.begin(); itr != queries.end(); ++itr)
for (auto itr = queries.begin(); itr != queries.end(); ++itr)
{
SQLElementData const& data = *itr;
switch (itr->type)
{
case SQL_ELEMENT_PREPARED:
{
PreparedStatement* stmt = data.element.stmt;
PreparedStatementBase* stmt = data.element.stmt;
ASSERT(stmt);
if (!Execute(stmt))
{
LOG_INFO("sql.driver", "[Warning] Transaction aborted. %u queries not executed.", (uint32)queries.size());
LOG_WARN("sql.sql", "Transaction aborted. %u queries not executed.", (uint32)queries.size());
int errorCode = GetLastError();
RollbackTransaction();
return errorCode;
@ -384,11 +403,11 @@ int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction)
break;
case SQL_ELEMENT_RAW:
{
const char* sql = data.element.query;
char const* sql = data.element.query;
ASSERT(sql);
if (!Execute(sql))
{
LOG_INFO("sql.driver", "[Warning] Transaction aborted. %u queries not executed.", (uint32)queries.size());
LOG_WARN("sql.sql", "Transaction aborted. %u queries not executed.", (uint32)queries.size());
int errorCode = GetLastError();
RollbackTransaction();
return errorCode;
@ -407,103 +426,157 @@ int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction)
return 0;
}
size_t MySQLConnection::EscapeString(char* to, const char* from, size_t length)
{
return mysql_real_escape_string(m_Mysql, to, from, length);
}
void MySQLConnection::Ping()
{
mysql_ping(m_Mysql);
}
uint32 MySQLConnection::GetLastError()
{
return mysql_errno(m_Mysql);
}
bool MySQLConnection::LockIfReady()
{
return m_Mutex.try_lock();
}
void MySQLConnection::Unlock()
{
m_Mutex.unlock();
}
uint32 MySQLConnection::GetServerVersion() const
{
return mysql_get_server_version(m_Mysql);
}
MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index)
{
ASSERT(index < m_stmts.size());
MySQLPreparedStatement* ret = m_stmts[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");
MySQLPreparedStatement* ret = m_stmts[index].get();
if (!ret)
LOG_INFO("sql.driver", "ERROR: 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 %u on database `%s`, connection type: %s.",
index, m_connectionInfo.database.c_str(), (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
return ret;
}
void MySQLConnection::PrepareStatement(uint32 index, const char* sql, ConnectionFlags flags)
void MySQLConnection::PrepareStatement(uint32 index, std::string const& sql, ConnectionFlags flags)
{
m_queries.insert(PreparedStatementMap::value_type(index, std::make_pair(sql, flags)));
// For reconnection case
if (m_reconnecting)
delete m_stmts[index];
// Check if specified query should be prepared on this connection
// i.e. don't prepare async statements on synchronous connections
// to save memory that will not be used.
if (!(m_connectionFlags & flags))
{
m_stmts[index] = nullptr;
m_stmts[index].reset();
return;
}
MYSQL_STMT* stmt = mysql_stmt_init(m_Mysql);
if (!stmt)
{
LOG_INFO("sql.driver", "[ERROR]: In mysql_stmt_init() id: %u, sql: \"%s\"", index, sql);
LOG_INFO("sql.driver", "[ERROR]: %s", mysql_error(m_Mysql));
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));
m_prepareError = true;
}
else
{
if (mysql_stmt_prepare(stmt, sql, static_cast<unsigned long>(strlen(sql))))
if (mysql_stmt_prepare(stmt, sql.c_str(), static_cast<unsigned long>(sql.size())))
{
LOG_INFO("sql.driver", "[ERROR]: In mysql_stmt_prepare() id: %u, sql: \"%s\"", index, sql);
LOG_INFO("sql.driver", "[ERROR]: %s", mysql_stmt_error(stmt));
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));
mysql_stmt_close(stmt);
m_prepareError = true;
}
else
{
MySQLPreparedStatement* mStmt = new MySQLPreparedStatement(stmt);
m_stmts[index] = mStmt;
}
m_stmts[index] = std::make_unique<MySQLPreparedStatement>(reinterpret_cast<MySQLStmt*>(stmt), sql);
}
}
PreparedResultSet* MySQLConnection::Query(PreparedStatement* stmt)
PreparedResultSet* MySQLConnection::Query(PreparedStatementBase* stmt)
{
MYSQL_RES* result = nullptr;
MySQLPreparedStatement* mysqlStmt = nullptr;
MySQLResult* result = nullptr;
uint64 rowCount = 0;
uint32 fieldCount = 0;
if (!_Query(stmt, &result, &rowCount, &fieldCount))
if (!_Query(stmt, &mysqlStmt, &result, &rowCount, &fieldCount))
return nullptr;
if (mysql_more_results(m_Mysql))
{
mysql_next_result(m_Mysql);
}
return new PreparedResultSet(stmt->m_stmt->GetSTMT(), result, rowCount, fieldCount);
return new PreparedResultSet(mysqlStmt->GetSTMT(), result, rowCount, fieldCount);
}
bool MySQLConnection::_HandleMySQLErrno(uint32 errNo)
bool MySQLConnection::_HandleMySQLErrno(uint32 errNo, uint8 attempts /*= 5*/)
{
switch (errNo)
{
case CR_SERVER_GONE_ERROR:
case CR_SERVER_LOST:
case CR_SERVER_LOST_EXTENDED:
#if !(MARIADB_VERSION_ID >= 100200)
case CR_INVALID_CONN_HANDLE:
#endif
{
m_reconnecting = true;
uint64 oldThreadId = mysql_thread_id(GetHandle());
mysql_close(GetHandle());
if (this->Open()) // Don't remove 'this' pointer unless you want to skip loading all prepared statements....
if (m_Mysql)
{
LOG_INFO("sql.driver", "Connection to the MySQL server is active.");
if (oldThreadId != mysql_thread_id(GetHandle()))
LOG_INFO("sql.driver", "Successfully reconnected to %s @%s:%s (%s).",
m_connectionInfo.database.c_str(), m_connectionInfo.host.c_str(), m_connectionInfo.port_or_socket.c_str(),
(m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
LOG_ERROR("sql.sql", "Lost the connection to the MySQL server!");
mysql_close(m_Mysql);
m_Mysql = nullptr;
}
[[fallthrough]];
}
case CR_CONN_HOST_ERROR:
{
LOG_INFO("sql.sql", "Attempting to reconnect to the MySQL server...");
m_reconnecting = true;
uint32 const lErrno = Open();
if (!lErrno)
{
// Don't remove 'this' pointer unless you want to skip loading all prepared statements...
if (!this->PrepareStatements())
{
LOG_FATAL("sql.sql", "Could not re-prepare statements!");
std::this_thread::sleep_for(std::chrono::seconds(10));
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(),
(m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
m_reconnecting = false;
return true;
}
uint32 lErrno = mysql_errno(GetHandle()); // It's possible this attempted reconnect throws 2006 at us. To prevent crazy recursive calls, sleep here.
std::this_thread::sleep_for(3s); // Sleep 3 seconds
return _HandleMySQLErrno(lErrno); // Call self (recursive)
if ((--attempts) == 0)
{
// 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!");
// We could also initiate a shutdown through using std::raise(SIGTERM)
std::this_thread::sleep_for(std::chrono::seconds(10));
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
return _HandleMySQLErrno(lErrno, attempts); // Call self (recursive)
}
}
case ER_LOCK_DEADLOCK:
@ -516,17 +589,17 @@ bool MySQLConnection::_HandleMySQLErrno(uint32 errNo)
// Outdated table or database structure - terminate core
case ER_BAD_FIELD_ERROR:
case ER_NO_SUCH_TABLE:
LOG_ERROR("server", "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(10s);
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::abort();
return false;
case ER_PARSE_ERROR:
LOG_ERROR("server", "Error while parsing SQL. Core fix required.");
std::this_thread::sleep_for(10s);
LOG_ERROR("sql.sql", "Error while parsing SQL. Core fix required.");
std::this_thread::sleep_for(std::chrono::seconds(10));
std::abort();
return false;
default:
LOG_ERROR("server", "Unhandled MySQL errno %u. Unexpected behaviour possible.", errNo);
LOG_ERROR("sql.sql", "Unhandled MySQL errno %u. Unexpected behaviour possible.", errNo);
return false;
}
}

View file

@ -1,22 +1,25 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include <ace/Activation_Queue.h>
#include "DatabaseWorkerPool.h"
#include "Transaction.h"
#include "Util.h"
#ifndef _MYSQLCONNECTION_H
#define _MYSQLCONNECTION_H
#include "DatabaseEnvFwd.h"
#include "Define.h"
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
template <typename T>
class ProducerConsumerQueue;
class DatabaseWorker;
class PreparedStatement;
class MySQLPreparedStatement;
class PingOperation;
class SQLOperation;
enum ConnectionFlags
{
@ -25,99 +28,75 @@ enum ConnectionFlags
CONNECTION_BOTH = CONNECTION_ASYNC | CONNECTION_SYNCH
};
struct MySQLConnectionInfo
struct AC_DATABASE_API MySQLConnectionInfo
{
MySQLConnectionInfo() = default;
MySQLConnectionInfo(const std::string& infoString)
{
Tokenizer tokens(infoString, ';');
if (tokens.size() != 5)
return;
uint8 i = 0;
host.assign(tokens[i++]);
port_or_socket.assign(tokens[i++]);
user.assign(tokens[i++]);
password.assign(tokens[i++]);
database.assign(tokens[i++]);
}
explicit MySQLConnectionInfo(std::string const& infoString);
std::string user;
std::string password;
std::string database;
std::string host;
std::string port_or_socket;
std::string ssl;
};
typedef std::map<uint32 /*index*/, std::pair<std::string /*query*/, ConnectionFlags /*sync/async*/>> PreparedStatementMap;
class MySQLConnection
class AC_DATABASE_API MySQLConnection
{
template <class T> friend class DatabaseWorkerPool;
friend class PingOperation;
template <class T> friend class DatabaseWorkerPool;
friend class PingOperation;
public:
MySQLConnection(MySQLConnectionInfo& connInfo); //! Constructor for synchronous connections.
MySQLConnection(ACE_Activation_Queue* queue, MySQLConnectionInfo& connInfo); //! Constructor for asynchronous connections.
MySQLConnection(ProducerConsumerQueue<SQLOperation*>* queue, MySQLConnectionInfo& connInfo); //! Constructor for asynchronous connections.
virtual ~MySQLConnection();
virtual uint32 Open();
void Close();
bool PrepareStatements();
public:
bool Execute(const char* sql);
bool Execute(PreparedStatement* stmt);
ResultSet* Query(const char* sql);
PreparedResultSet* Query(PreparedStatement* stmt);
bool _Query(const char* sql, MYSQL_RES** pResult, MYSQL_FIELD** pFields, uint64* pRowCount, uint32* pFieldCount);
bool _Query(PreparedStatement* stmt, MYSQL_RES** pResult, uint64* pRowCount, uint32* pFieldCount);
bool Execute(char const* sql);
bool Execute(PreparedStatementBase* stmt);
ResultSet* Query(char const* sql);
PreparedResultSet* Query(PreparedStatementBase* stmt);
bool _Query(char const* sql, MySQLResult** pResult, MySQLField** pFields, uint64* pRowCount, uint32* pFieldCount);
bool _Query(PreparedStatementBase* stmt, MySQLPreparedStatement** mysqlStmt, MySQLResult** pResult, uint64* pRowCount, uint32* pFieldCount);
void BeginTransaction();
void RollbackTransaction();
void CommitTransaction();
int ExecuteTransaction(SQLTransaction& transaction);
int ExecuteTransaction(std::shared_ptr<TransactionBase> transaction);
size_t EscapeString(char* to, const char* from, size_t length);
void Ping();
operator bool () const { return m_Mysql != nullptr; }
void Ping() { mysql_ping(m_Mysql); }
uint32 GetLastError() { return mysql_errno(m_Mysql); }
uint32 GetLastError();
protected:
bool LockIfReady()
{
/// Tries to acquire lock. If lock is acquired by another thread
/// the calling parent will just try another connection
return m_Mutex.try_lock();
}
/// Tries to acquire lock. If lock is acquired by another thread
/// the calling parent will just try another connection
bool LockIfReady();
void Unlock()
{
/// Called by parent databasepool. Will let other threads access this connection
m_Mutex.unlock();
}
/// Called by parent databasepool. Will let other threads access this connection
void Unlock();
MYSQL* GetHandle() { return m_Mysql; }
uint32 GetServerVersion() const;
MySQLPreparedStatement* GetPreparedStatement(uint32 index);
void PrepareStatement(uint32 index, const char* sql, ConnectionFlags flags);
void PrepareStatement(uint32 index, std::string const& sql, ConnectionFlags flags);
virtual void DoPrepareStatements() = 0;
protected:
std::vector<MySQLPreparedStatement*> m_stmts; //! PreparedStatements storage
PreparedStatementMap m_queries; //! Query storage
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?
private:
bool _HandleMySQLErrno(uint32 errNo);
bool _HandleMySQLErrno(uint32 errNo, uint8 attempts = 5);
private:
ACE_Activation_Queue* m_queue; //! Queue shared with other asynchronous connections.
DatabaseWorker* m_worker; //! Core worker task.
MYSQL* m_Mysql; //! MySQL Handle.
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;

View file

@ -0,0 +1,22 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef MySQLHacks_h__
#define MySQLHacks_h__
#include "MySQLWorkaround.h"
#include <type_traits>
struct MySQLHandle : MYSQL { };
struct MySQLResult : MYSQL_RES { };
struct MySQLField : MYSQL_FIELD { };
struct MySQLBind : MYSQL_BIND { };
struct MySQLStmt : MYSQL_STMT { };
// mysql 8 removed my_bool typedef (it was char) and started using bools directly
// to maintain compatibility we use this trick to retrieve which type is being used
using MySQLBool = std::remove_pointer_t<decltype(std::declval<MYSQL_BIND>().is_null)>;
#endif // MySQLHacks_h__

View file

@ -0,0 +1,188 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "MySQLPreparedStatement.h"
#include "Errors.h"
#include "Log.h"
#include "MySQLHacks.h"
#include "PreparedStatement.h"
template<typename T>
struct MySQLType { };
template<> struct MySQLType<uint8> : std::integral_constant<enum_field_types, MYSQL_TYPE_TINY> { };
template<> struct MySQLType<uint16> : std::integral_constant<enum_field_types, MYSQL_TYPE_SHORT> { };
template<> struct MySQLType<uint32> : std::integral_constant<enum_field_types, MYSQL_TYPE_LONG> { };
template<> struct MySQLType<uint64> : std::integral_constant<enum_field_types, MYSQL_TYPE_LONGLONG> { };
template<> struct MySQLType<int8> : std::integral_constant<enum_field_types, MYSQL_TYPE_TINY> { };
template<> struct MySQLType<int16> : std::integral_constant<enum_field_types, MYSQL_TYPE_SHORT> { };
template<> struct MySQLType<int32> : std::integral_constant<enum_field_types, MYSQL_TYPE_LONG> { };
template<> struct MySQLType<int64> : std::integral_constant<enum_field_types, MYSQL_TYPE_LONGLONG> { };
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))
{
/// Initialize variable parameters
m_paramCount = mysql_stmt_param_count(stmt);
m_paramsSet.assign(m_paramCount, false);
m_bind = new MySQLBind[m_paramCount];
memset(m_bind, 0, sizeof(MySQLBind) * m_paramCount);
/// "If set to 1, causes mysql_stmt_store_result() to update the metadata MYSQL_FIELD->max_length value."
MySQLBool bool_tmp = MySQLBool(1);
mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &bool_tmp);
}
MySQLPreparedStatement::~MySQLPreparedStatement()
{
ClearParameters();
if (m_Mstmt->bind_result_done)
{
delete[] m_Mstmt->bind->length;
delete[] m_Mstmt->bind->is_null;
}
mysql_stmt_close(m_Mstmt);
delete[] m_bind;
}
void MySQLPreparedStatement::BindParameters(PreparedStatementBase* stmt)
{
m_stmt = stmt; // Cross reference them for debug output
uint8 pos = 0;
for (PreparedStatementData const& data : stmt->GetParameters())
{
std::visit([&](auto&& param)
{
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());
#endif
}
void MySQLPreparedStatement::ClearParameters()
{
for (uint32 i=0; i < m_paramCount; ++i)
{
delete m_bind[i].length;
m_bind[i].length = nullptr;
delete[] (char*) m_bind[i].buffer;
m_bind[i].buffer = nullptr;
m_paramsSet[i] = false;
}
}
static bool ParamenterIndexAssertFail(uint32 stmtIndex, uint8 index, uint32 paramCount)
{
LOG_ERROR("sql.driver", "Attempted to bind parameter %u%s on a PreparedStatement %u (statement has only %u parameters)", uint32(index) + 1, (index == 1 ? "st" : (index == 2 ? "nd" : (index == 3 ? "rd" : "nd"))), stmtIndex, paramCount);
return false;
}
//- Bind on mysql level
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);
}
void MySQLPreparedStatement::SetParameter(uint8 index, std::nullptr_t)
{
AssertValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
param->buffer_type = MYSQL_TYPE_NULL;
delete[] static_cast<char*>(param->buffer);
param->buffer = nullptr;
param->buffer_length = 0;
param->is_null_value = 1;
delete param->length;
param->length = nullptr;
}
void MySQLPreparedStatement::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);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
uint32 len = uint32(value.size());
param->buffer_type = MYSQL_TYPE_VAR_STRING;
delete [] static_cast<char*>(param->buffer);
param->buffer = new char[len];
param->buffer_length = len;
param->is_null_value = 0;
delete param->length;
param->length = new unsigned long(len);
memcpy(param->buffer, value.c_str(), len);
}
void MySQLPreparedStatement::SetParameter(uint8 index, std::vector<uint8> const& value)
{
AssertValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
uint32 len = uint32(value.size());
param->buffer_type = MYSQL_TYPE_BLOB;
delete [] static_cast<char*>(param->buffer);
param->buffer = new char[len];
param->buffer_length = len;
param->is_null_value = 0;
delete param->length;
param->length = new unsigned long(len);
memcpy(param->buffer, value.data(), len);
}
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);
std::string replaceStr = std::visit([&](auto&& data)
{
return PreparedStatementData::ToString(data);
}, data.data);
queryString.replace(pos, 1, replaceStr);
pos += replaceStr.length();
}
return queryString;
}

View file

@ -0,0 +1,60 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef MySQLPreparedStatement_h__
#define MySQLPreparedStatement_h__
#include "DatabaseEnvFwd.h"
#include "Define.h"
#include "MySQLWorkaround.h"
#include <string>
#include <vector>
class MySQLConnection;
class PreparedStatementBase;
//- Class of which the instances are unique per MySQLConnection
//- access to these class objects is only done when a prepared statement task
//- is executed.
class AC_DATABASE_API MySQLPreparedStatement
{
friend class MySQLConnection;
friend class PreparedStatementBase;
public:
MySQLPreparedStatement(MySQLStmt* stmt, std::string queryString);
~MySQLPreparedStatement();
void BindParameters(PreparedStatementBase* stmt);
uint32 GetParameterCount() const { return m_paramCount; }
protected:
void SetParameter(uint8 index, std::nullptr_t);
void SetParameter(uint8 index, bool 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);
MySQLStmt* GetSTMT() { return m_Mstmt; }
MySQLBind* GetBind() { return m_bind; }
PreparedStatementBase* m_stmt;
void ClearParameters();
void AssertValidIndex(uint8 index);
std::string getQueryString() const;
private:
MySQLStmt* m_Mstmt;
uint32 m_paramCount;
std::vector<bool> m_paramsSet;
MySQLBind* m_bind;
std::string const m_queryString;
MySQLPreparedStatement(MySQLPreparedStatement const& right) = delete;
MySQLPreparedStatement& operator=(MySQLPreparedStatement const& right) = delete;
};
#endif // MySQLPreparedStatement_h__

View file

@ -1,11 +1,10 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2021 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "MySQLThreading.h"
#include "Log.h"
#include <mysql.h>
#include "MySQLWorkaround.h"
void MySQL::Library_Init()
{

View file

@ -1,19 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _MYSQLTHREADING_H
#define _MYSQLTHREADING_H
#include "Log.h"
#include "Define.h"
namespace MySQL
{
void Library_Init();
void Library_End();
uint32 GetLibraryVersion();
AC_DATABASE_API void Library_Init();
AC_DATABASE_API void Library_End();
AC_DATABASE_API uint32 GetLibraryVersion();
}
#endif

View file

@ -0,0 +1,9 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7
#include <winsock2.h>
#endif
#include <mysql.h>

View file

@ -1,481 +1,126 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "PreparedStatement.h"
#include "MySQLConnection.h"
#include "Errors.h"
#include "Log.h"
#include <sstream>
#include "MySQLConnection.h"
#include "MySQLPreparedStatement.h"
#include "MySQLWorkaround.h"
#include "QueryResult.h"
PreparedStatement::PreparedStatement(uint32 index) :
m_stmt(nullptr),
m_index(index)
{
}
PreparedStatementBase::PreparedStatementBase(uint32 index, uint8 capacity) :
m_index(index), statement_data(capacity) { }
PreparedStatement::~PreparedStatement()
{
}
void PreparedStatement::BindParameters()
{
ASSERT (m_stmt);
uint8 i = 0;
for (; i < statement_data.size(); i++)
{
switch (statement_data[i].type)
{
case TYPE_BOOL:
m_stmt->setBool(i, statement_data[i].data.boolean);
break;
case TYPE_UI8:
m_stmt->setUInt8(i, statement_data[i].data.ui8);
break;
case TYPE_UI16:
m_stmt->setUInt16(i, statement_data[i].data.ui16);
break;
case TYPE_UI32:
m_stmt->setUInt32(i, statement_data[i].data.ui32);
break;
case TYPE_I8:
m_stmt->setInt8(i, statement_data[i].data.i8);
break;
case TYPE_I16:
m_stmt->setInt16(i, statement_data[i].data.i16);
break;
case TYPE_I32:
m_stmt->setInt32(i, statement_data[i].data.i32);
break;
case TYPE_UI64:
m_stmt->setUInt64(i, statement_data[i].data.ui64);
break;
case TYPE_I64:
m_stmt->setInt64(i, statement_data[i].data.i64);
break;
case TYPE_FLOAT:
m_stmt->setFloat(i, statement_data[i].data.f);
break;
case TYPE_DOUBLE:
m_stmt->setDouble(i, statement_data[i].data.d);
break;
case TYPE_STRING:
m_stmt->setBinary(i, statement_data[i].binary, true);
break;
case TYPE_BINARY:
m_stmt->setBinary(i, statement_data[i].binary, false);
break;
case TYPE_NULL:
m_stmt->setNull(i);
break;
}
}
#ifdef _DEBUG
if (i < m_stmt->m_paramCount)
LOG_INFO("sql.driver", "[WARNING]: BindParameters() for statement %u did not bind all allocated parameters", m_index);
#endif
}
PreparedStatementBase::~PreparedStatementBase() { }
//- Bind to buffer
void PreparedStatement::setBool(const uint8 index, const bool value)
void PreparedStatementBase::setBool(const uint8 index, const bool value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].data.boolean = value;
statement_data[index].type = TYPE_BOOL;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatement::setUInt8(const uint8 index, const uint8 value)
void PreparedStatementBase::setUInt8(const uint8 index, const uint8 value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].data.ui8 = value;
statement_data[index].type = TYPE_UI8;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatement::setUInt16(const uint8 index, const uint16 value)
void PreparedStatementBase::setUInt16(const uint8 index, const uint16 value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].data.ui16 = value;
statement_data[index].type = TYPE_UI16;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatement::setUInt32(const uint8 index, const uint32 value)
void PreparedStatementBase::setUInt32(const uint8 index, const uint32 value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].data.ui32 = value;
statement_data[index].type = TYPE_UI32;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatement::setUInt64(const uint8 index, const uint64 value)
void PreparedStatementBase::setUInt64(const uint8 index, const uint64 value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].data.ui64 = value;
statement_data[index].type = TYPE_UI64;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatement::setInt8(const uint8 index, const int8 value)
void PreparedStatementBase::setInt8(const uint8 index, const int8 value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].data.i8 = value;
statement_data[index].type = TYPE_I8;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatement::setInt16(const uint8 index, const int16 value)
void PreparedStatementBase::setInt16(const uint8 index, const int16 value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].data.i16 = value;
statement_data[index].type = TYPE_I16;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatement::setInt32(const uint8 index, const int32 value)
void PreparedStatementBase::setInt32(const uint8 index, const int32 value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].data.i32 = value;
statement_data[index].type = TYPE_I32;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatement::setInt64(const uint8 index, const int64 value)
void PreparedStatementBase::setInt64(const uint8 index, const int64 value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].data.i64 = value;
statement_data[index].type = TYPE_I64;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatement::setFloat(const uint8 index, const float value)
void PreparedStatementBase::setFloat(const uint8 index, const float value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].data.f = value;
statement_data[index].type = TYPE_FLOAT;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatement::setDouble(const uint8 index, const double value)
void PreparedStatementBase::setDouble(const uint8 index, const double value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].data.d = value;
statement_data[index].type = TYPE_DOUBLE;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatement::setString(const uint8 index, const std::string& value)
void PreparedStatementBase::setString(const uint8 index, const std::string& value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].binary.resize(value.length() + 1);
memcpy(statement_data[index].binary.data(), value.c_str(), value.length() + 1);
statement_data[index].type = TYPE_STRING;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
void PreparedStatement::setBinary(const uint8 index, const std::vector<uint8>& value)
void PreparedStatementBase::setStringView(const uint8 index, const std::string_view value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].binary = value;
statement_data[index].type = TYPE_BINARY;
ASSERT(index < statement_data.size());
statement_data[index].data.emplace<std::string>(value);
}
void PreparedStatement::setNull(const uint8 index)
void PreparedStatementBase::setBinary(const uint8 index, const std::vector<uint8>& value)
{
if (index >= statement_data.size())
statement_data.resize(index + 1);
statement_data[index].type = TYPE_NULL;
ASSERT(index < statement_data.size());
statement_data[index].data = value;
}
MySQLPreparedStatement::MySQLPreparedStatement(MYSQL_STMT* stmt) :
m_stmt(nullptr),
m_Mstmt(stmt),
m_bind(nullptr)
void PreparedStatementBase::setNull(const uint8 index)
{
/// Initialize variable parameters
m_paramCount = mysql_stmt_param_count(stmt);
m_paramsSet.assign(m_paramCount, false);
m_bind = new MYSQL_BIND[m_paramCount];
memset(m_bind, 0, sizeof(MYSQL_BIND)*m_paramCount);
/// "If set to 1, causes mysql_stmt_store_result() to update the metadata MYSQL_FIELD->max_length value."
my_bool bool_tmp = 1;
mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &bool_tmp);
}
MySQLPreparedStatement::~MySQLPreparedStatement()
{
ClearParameters();
if (m_Mstmt->bind_result_done)
{
delete[] m_Mstmt->bind->length;
delete[] m_Mstmt->bind->is_null;
}
mysql_stmt_close(m_Mstmt);
delete[] m_bind;
}
void MySQLPreparedStatement::ClearParameters()
{
for (uint32 i = 0; i < m_paramCount; ++i)
{
delete m_bind[i].length;
m_bind[i].length = nullptr;
delete[] (char*) m_bind[i].buffer;
m_bind[i].buffer = nullptr;
m_paramsSet[i] = false;
}
}
static bool ParamenterIndexAssertFail(uint32 stmtIndex, uint8 index, uint32 paramCount)
{
LOG_ERROR("server", "Attempted to bind parameter %u%s on a PreparedStatement %u (statement has only %u parameters)", uint32(index) + 1, (index == 1 ? "st" : (index == 2 ? "nd" : (index == 3 ? "rd" : "nd"))), stmtIndex, paramCount);
return false;
}
//- Bind on mysql level
bool MySQLPreparedStatement::CheckValidIndex(uint8 index)
{
ASSERT(index < m_paramCount || ParamenterIndexAssertFail(m_stmt->m_index, index, m_paramCount));
if (m_paramsSet[index])
LOG_INFO("sql.driver", "[WARNING] Prepared Statement (id: %u) trying to bind value on already bound index (%u).", m_stmt->m_index, index);
return true;
}
void MySQLPreparedStatement::setBool(const uint8 index, const bool value)
{
setUInt8(index, value ? 1 : 0);
}
void MySQLPreparedStatement::setUInt8(const uint8 index, const uint8 value)
{
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
setValue(param, MYSQL_TYPE_TINY, &value, sizeof(uint8), true);
}
void MySQLPreparedStatement::setUInt16(const uint8 index, const uint16 value)
{
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
setValue(param, MYSQL_TYPE_SHORT, &value, sizeof(uint16), true);
}
void MySQLPreparedStatement::setUInt32(const uint8 index, const uint32 value)
{
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
setValue(param, MYSQL_TYPE_LONG, &value, sizeof(uint32), true);
}
void MySQLPreparedStatement::setUInt64(const uint8 index, const uint64 value)
{
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
setValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(uint64), true);
}
void MySQLPreparedStatement::setInt8(const uint8 index, const int8 value)
{
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
setValue(param, MYSQL_TYPE_TINY, &value, sizeof(int8), false);
}
void MySQLPreparedStatement::setInt16(const uint8 index, const int16 value)
{
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
setValue(param, MYSQL_TYPE_SHORT, &value, sizeof(int16), false);
}
void MySQLPreparedStatement::setInt32(const uint8 index, const int32 value)
{
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
setValue(param, MYSQL_TYPE_LONG, &value, sizeof(int32), false);
}
void MySQLPreparedStatement::setInt64(const uint8 index, const int64 value)
{
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
setValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(int64), false);
}
void MySQLPreparedStatement::setFloat(const uint8 index, const float value)
{
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
setValue(param, MYSQL_TYPE_FLOAT, &value, sizeof(float), (value > 0.0f));
}
void MySQLPreparedStatement::setDouble(const uint8 index, const double value)
{
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
setValue(param, MYSQL_TYPE_DOUBLE, &value, sizeof(double), (value > 0.0f));
}
void MySQLPreparedStatement::setBinary(const uint8 index, const std::vector<uint8>& value, bool isString)
{
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
uint32 len = uint32(value.size());
param->buffer_type = MYSQL_TYPE_BLOB;
delete [] static_cast<char*>(param->buffer);
param->buffer = new char[len];
param->buffer_length = len;
param->is_null_value = 0;
delete param->length;
param->length = new unsigned long(len);
if (isString)
{
*param->length -= 1;
param->buffer_type = MYSQL_TYPE_VAR_STRING;
}
memcpy(param->buffer, value.data(), len);
}
void MySQLPreparedStatement::setNull(const uint8 index)
{
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
param->buffer_type = MYSQL_TYPE_NULL;
delete [] static_cast<char*>(param->buffer);
param->buffer = nullptr;
param->buffer_length = 0;
param->is_null_value = 1;
delete param->length;
param->length = nullptr;
}
void MySQLPreparedStatement::setValue(MYSQL_BIND* param, enum_field_types type, const void* value, uint32 len, bool isUnsigned)
{
param->buffer_type = type;
delete [] static_cast<char*>(param->buffer);
param->buffer = new char[len];
param->buffer_length = 0;
param->is_null_value = 0;
param->length = nullptr; // Only != nullptr for strings
param->is_unsigned = isUnsigned;
memcpy(param->buffer, value, len);
}
std::string MySQLPreparedStatement::getQueryString(std::string const& sqlPattern) const
{
std::string queryString = sqlPattern;
size_t pos = 0;
for (uint32 i = 0; i < m_stmt->statement_data.size(); i++)
{
pos = queryString.find('?', pos);
std::stringstream ss;
switch (m_stmt->statement_data[i].type)
{
case TYPE_BOOL:
ss << uint16(m_stmt->statement_data[i].data.boolean);
break;
case TYPE_UI8:
ss << uint16(m_stmt->statement_data[i].data.ui8); // stringstream will append a character with that code instead of numeric representation
break;
case TYPE_UI16:
ss << m_stmt->statement_data[i].data.ui16;
break;
case TYPE_UI32:
ss << m_stmt->statement_data[i].data.ui32;
break;
case TYPE_I8:
ss << int16(m_stmt->statement_data[i].data.i8); // stringstream will append a character with that code instead of numeric representation
break;
case TYPE_I16:
ss << m_stmt->statement_data[i].data.i16;
break;
case TYPE_I32:
ss << m_stmt->statement_data[i].data.i32;
break;
case TYPE_UI64:
ss << m_stmt->statement_data[i].data.ui64;
break;
case TYPE_I64:
ss << m_stmt->statement_data[i].data.i64;
break;
case TYPE_FLOAT:
ss << m_stmt->statement_data[i].data.f;
break;
case TYPE_DOUBLE:
ss << m_stmt->statement_data[i].data.d;
break;
case TYPE_STRING:
ss << '\'' << (char const*)m_stmt->statement_data[i].binary.data() << '\'';
break;
case TYPE_BINARY:
ss << "BINARY";
break;
case TYPE_NULL:
ss << "nullptr";
break;
}
std::string replaceStr = ss.str();
queryString.replace(pos, 1, replaceStr);
pos += replaceStr.length();
}
return queryString;
ASSERT(index < statement_data.size());
statement_data[index].data = nullptr;
}
//- Execution
PreparedStatementTask::PreparedStatementTask(PreparedStatement* stmt) :
m_stmt(stmt),
m_has_result(false)
{
}
PreparedStatementTask::PreparedStatementTask(PreparedStatement* stmt, PreparedQueryResultFuture result) :
m_stmt(stmt),
m_has_result(true),
m_result(result)
PreparedStatementTask::PreparedStatementTask(PreparedStatementBase* stmt, bool async) :
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();
}
PreparedStatementTask::~PreparedStatementTask()
{
delete m_stmt;
if (m_has_result && m_result != nullptr)
delete m_result;
}
bool PreparedStatementTask::Execute()
@ -486,12 +131,58 @@ bool PreparedStatementTask::Execute()
if (!result || !result->GetRowCount())
{
delete result;
m_result.set(PreparedQueryResult(nullptr));
m_result->set_value(PreparedQueryResult(nullptr));
return false;
}
m_result.set(PreparedQueryResult(result));
m_result->set_value(PreparedQueryResult(result));
return true;
}
return m_conn->Execute(m_stmt);
}
template<typename T>
std::string PreparedStatementData::ToString(T value)
{
return fmt::format("{}", 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*/)
{
return "BINARY";
}
std::string PreparedStatementData::ToString(std::nullptr_t)
{
return "NULL";
}

View file

@ -1,75 +1,57 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _PREPAREDSTATEMENT_H
#define _PREPAREDSTATEMENT_H
#include "Define.h"
#include "SQLOperation.h"
#include <ace/Future.h>
#ifdef __APPLE__
#undef TYPE_BOOL
#endif
//- Union for data buffer (upper-level bind -> queue -> lower-level bind)
union PreparedStatementDataUnion
{
bool boolean;
uint8 ui8;
int8 i8;
uint16 ui16;
int16 i16;
uint32 ui32;
int32 i32;
uint64 ui64;
int64 i64;
float f;
double d;
};
//- This enum helps us differ data held in above union
enum PreparedStatementValueType
{
TYPE_BOOL,
TYPE_UI8,
TYPE_UI16,
TYPE_UI32,
TYPE_UI64,
TYPE_I8,
TYPE_I16,
TYPE_I32,
TYPE_I64,
TYPE_FLOAT,
TYPE_DOUBLE,
TYPE_STRING,
TYPE_BINARY,
TYPE_NULL
};
#include <future>
#include <variant>
#include <vector>
struct PreparedStatementData
{
PreparedStatementDataUnion data;
PreparedStatementValueType type;
std::vector<uint8> binary;
std::variant<
bool,
uint8,
uint16,
uint32,
uint64,
int8,
int16,
int32,
int64,
float,
double,
std::string,
std::vector<uint8>,
std::nullptr_t
> data;
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);
};
//- Forward declare
class MySQLPreparedStatement;
//- Upper-level class that is used in code
class PreparedStatement
class AC_DATABASE_API PreparedStatementBase
{
friend class PreparedStatementTask;
friend class MySQLPreparedStatement;
friend class MySQLConnection;
friend class PreparedStatementTask;
public:
explicit PreparedStatement(uint32 index);
~PreparedStatement();
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);
@ -82,84 +64,55 @@ public:
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>
template <size_t Size>
void setBinary(const uint8 index, std::array<uint8, Size> const& value)
{
std::vector<uint8> vec(value.begin(), value.end());
setBinary(index, vec);
}
void setNull(const uint8 index);
uint32 GetIndex() const { return m_index; }
std::vector<PreparedStatementData> const& GetParameters() const { return statement_data; }
protected:
void BindParameters();
protected:
MySQLPreparedStatement* m_stmt;
uint32 m_index;
std::vector<PreparedStatementData> statement_data; //- Buffer of parameters, not tied to MySQL in any way yet
//- Buffer of parameters, not tied to MySQL in any way yet
std::vector<PreparedStatementData> statement_data;
PreparedStatementBase(PreparedStatementBase const& right) = delete;
PreparedStatementBase& operator=(PreparedStatementBase const& right) = delete;
};
//- Class of which the instances are unique per MySQLConnection
//- access to these class objects is only done when a prepared statement task
//- is executed.
class MySQLPreparedStatement
template<typename T>
class PreparedStatement : public PreparedStatementBase
{
friend class MySQLConnection;
friend class PreparedStatement;
public:
MySQLPreparedStatement(MYSQL_STMT* stmt);
~MySQLPreparedStatement();
void setBool(const uint8 index, const bool value);
void setUInt8(const uint8 index, const uint8 value);
void setUInt16(const uint8 index, const uint16 value);
void setUInt32(const uint8 index, const uint32 value);
void setUInt64(const uint8 index, const uint64 value);
void setInt8(const uint8 index, const int8 value);
void setInt16(const uint8 index, const int16 value);
void setInt32(const uint8 index, const int32 value);
void setInt64(const uint8 index, const int64 value);
void setFloat(const uint8 index, const float value);
void setDouble(const uint8 index, const double value);
void setBinary(const uint8 index, const std::vector<uint8>& value, bool isString);
void setNull(const uint8 index);
protected:
MYSQL_STMT* GetSTMT() { return m_Mstmt; }
MYSQL_BIND* GetBind() { return m_bind; }
PreparedStatement* m_stmt;
void ClearParameters();
bool CheckValidIndex(uint8 index);
[[nodiscard]] std::string getQueryString(std::string const& sqlPattern) const;
explicit PreparedStatement(uint32 index, uint8 capacity) : PreparedStatementBase(index, capacity)
{
}
private:
void setValue(MYSQL_BIND* param, enum_field_types type, const void* value, uint32 len, bool isUnsigned);
private:
MYSQL_STMT* m_Mstmt;
uint32 m_paramCount;
std::vector<bool> m_paramsSet;
MYSQL_BIND* m_bind;
PreparedStatement(PreparedStatement const& right) = delete;
PreparedStatement& operator=(PreparedStatement const& right) = delete;
};
typedef ACE_Future<PreparedQueryResult> PreparedQueryResultFuture;
//- Lower-level class, enqueuable operation
class PreparedStatementTask : public SQLOperation
class AC_DATABASE_API PreparedStatementTask : public SQLOperation
{
public:
PreparedStatementTask(PreparedStatement* stmt);
PreparedStatementTask(PreparedStatement* stmt, PreparedQueryResultFuture result);
~PreparedStatementTask() override;
PreparedStatementTask(PreparedStatementBase* stmt, bool async = false);
~PreparedStatementTask();
bool Execute() override;
PreparedQueryResultFuture GetFuture() { return m_result->get_future(); }
protected:
PreparedStatement* m_stmt;
PreparedStatementBase* m_stmt;
bool m_has_result;
PreparedQueryResultFuture m_result;
PreparedQueryResultPromise* m_result;
};
#endif

View file

@ -0,0 +1,209 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "QueryCallback.h"
#include "Errors.h"
template<typename T, typename... Args>
inline void Construct(T& t, Args&&... args)
{
new (&t) T(std::forward<Args>(args)...);
}
template<typename T>
inline void Destroy(T& t)
{
t.~T();
}
template<typename T>
inline void ConstructActiveMember(T* obj)
{
if (!obj->_isPrepared)
Construct(obj->_string);
else
Construct(obj->_prepared);
}
template<typename T>
inline void DestroyActiveMember(T* obj)
{
if (!obj->_isPrepared)
Destroy(obj->_string);
else
Destroy(obj->_prepared);
}
template<typename T>
inline void MoveFrom(T* to, T&& from)
{
ASSERT(to->_isPrepared == from._isPrepared);
if (!to->_isPrepared)
to->_string = std::move(from._string);
else
to->_prepared = std::move(from._prepared);
}
struct QueryCallback::QueryCallbackData
{
public:
friend class QueryCallback;
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)
{
_isPrepared = right._isPrepared;
ConstructActiveMember(this);
MoveFrom(this, std::move(right));
}
QueryCallbackData& operator=(QueryCallbackData&& right)
{
if (this != &right)
{
if (_isPrepared != right._isPrepared)
{
DestroyActiveMember(this);
_isPrepared = right._isPrepared;
ConstructActiveMember(this);
}
MoveFrom(this, std::move(right));
}
return *this;
}
~QueryCallbackData() { DestroyActiveMember(this); }
private:
QueryCallbackData(QueryCallbackData const&) = delete;
QueryCallbackData& operator=(QueryCallbackData const&) = delete;
template<typename T> friend void ConstructActiveMember(T* obj);
template<typename T> friend void DestroyActiveMember(T* obj);
template<typename T> friend void MoveFrom(T* to, T&& from);
union
{
std::function<void(QueryCallback&, QueryResult)> _string;
std::function<void(QueryCallback&, PreparedQueryResult)> _prepared;
};
bool _isPrepared;
};
// Not using initialization lists to work around segmentation faults when compiling with clang without precompiled headers
QueryCallback::QueryCallback(std::future<QueryResult>&& result)
{
_isPrepared = false;
Construct(_string, std::move(result));
}
QueryCallback::QueryCallback(std::future<PreparedQueryResult>&& result)
{
_isPrepared = true;
Construct(_prepared, std::move(result));
}
QueryCallback::QueryCallback(QueryCallback&& right)
{
_isPrepared = right._isPrepared;
ConstructActiveMember(this);
MoveFrom(this, std::move(right));
_callbacks = std::move(right._callbacks);
}
QueryCallback& QueryCallback::operator=(QueryCallback&& right)
{
if (this != &right)
{
if (_isPrepared != right._isPrepared)
{
DestroyActiveMember(this);
_isPrepared = right._isPrepared;
ConstructActiveMember(this);
}
MoveFrom(this, std::move(right));
_callbacks = std::move(right._callbacks);
}
return *this;
}
QueryCallback::~QueryCallback()
{
DestroyActiveMember(this);
}
QueryCallback&& QueryCallback::WithCallback(std::function<void(QueryResult)>&& callback)
{
return WithChainingCallback([callback](QueryCallback& /*this*/, QueryResult result) { callback(std::move(result)); });
}
QueryCallback&& QueryCallback::WithPreparedCallback(std::function<void(PreparedQueryResult)>&& callback)
{
return WithChainingPreparedCallback([callback](QueryCallback& /*this*/, PreparedQueryResult result) { callback(std::move(result)); });
}
QueryCallback&& QueryCallback::WithChainingCallback(std::function<void(QueryCallback&, QueryResult)>&& callback)
{
ASSERT(!_callbacks.empty() || !_isPrepared, "Attempted to set callback function for string query on a prepared async query");
_callbacks.emplace(std::move(callback));
return std::move(*this);
}
QueryCallback&& QueryCallback::WithChainingPreparedCallback(std::function<void(QueryCallback&, PreparedQueryResult)>&& callback)
{
ASSERT(!_callbacks.empty() || _isPrepared, "Attempted to set callback function for prepared query on a string async query");
_callbacks.emplace(std::move(callback));
return std::move(*this);
}
void QueryCallback::SetNextQuery(QueryCallback&& next)
{
MoveFrom(this, std::move(next));
}
bool QueryCallback::InvokeIfReady()
{
QueryCallbackData& callback = _callbacks.front();
auto checkStateAndReturnCompletion = [this]()
{
_callbacks.pop();
bool hasNext = !_isPrepared ? _string.valid() : _prepared.valid();
if (_callbacks.empty())
{
ASSERT(!hasNext);
return true;
}
// abort chain
if (!hasNext)
return true;
ASSERT(_isPrepared == _callbacks.front()._isPrepared);
return false;
};
if (!_isPrepared)
{
if (_string.valid() && _string.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
{
QueryResultFuture f(std::move(_string));
std::function<void(QueryCallback&, QueryResult)> cb(std::move(callback._string));
cb(*this, f.get());
return checkStateAndReturnCompletion();
}
}
else
{
if (_prepared.valid() && _prepared.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
{
PreparedQueryResultFuture f(std::move(_prepared));
std::function<void(QueryCallback&, PreparedQueryResult)> cb(std::move(callback._prepared));
cb(*this, f.get());
return checkStateAndReturnCompletion();
}
}
return false;
}

View file

@ -0,0 +1,57 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _QUERY_CALLBACK_H
#define _QUERY_CALLBACK_H
#include "DatabaseEnvFwd.h"
#include "Define.h"
#include <functional>
#include <future>
#include <list>
#include <queue>
#include <utility>
class AC_DATABASE_API QueryCallback
{
public:
explicit QueryCallback(QueryResultFuture&& result);
explicit QueryCallback(PreparedQueryResultFuture&& result);
QueryCallback(QueryCallback&& right);
QueryCallback& operator=(QueryCallback&& right);
~QueryCallback();
QueryCallback&& WithCallback(std::function<void(QueryResult)>&& callback);
QueryCallback&& WithPreparedCallback(std::function<void(PreparedQueryResult)>&& callback);
QueryCallback&& WithChainingCallback(std::function<void(QueryCallback&, QueryResult)>&& callback);
QueryCallback&& WithChainingPreparedCallback(std::function<void(QueryCallback&, PreparedQueryResult)>&& callback);
// Moves std::future from next to this object
void SetNextQuery(QueryCallback&& next);
// returns true when completed
bool InvokeIfReady();
private:
QueryCallback(QueryCallback const& right) = delete;
QueryCallback& operator=(QueryCallback const& right) = delete;
template<typename T> friend void ConstructActiveMember(T* obj);
template<typename T> friend void DestroyActiveMember(T* obj);
template<typename T> friend void MoveFrom(T* to, T&& from);
union
{
QueryResultFuture _string;
PreparedQueryResultFuture _prepared;
};
bool _isPrepared;
struct QueryCallbackData;
std::queue<QueryCallbackData, std::list<QueryCallbackData>> _callbacks;
};
#endif // _QUERY_CALLBACK_H

View file

@ -1,109 +1,37 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "MySQLConnection.h"
#include "QueryHolder.h"
#include "PreparedStatement.h"
#include "Errors.h"
#include "Log.h"
#include "MySQLConnection.h"
#include "PreparedStatement.h"
#include "QueryResult.h"
bool SQLQueryHolder::SetQuery(size_t index, const char* sql)
bool SQLQueryHolderBase::SetPreparedQueryImpl(size_t index, PreparedStatementBase* stmt)
{
if (m_queries.size() <= index)
{
LOG_ERROR("server", "Query index (%u) out of range (size: %u) for query: %s", uint32(index), (uint32)m_queries.size(), sql);
LOG_ERROR("sql.sql", "Query index (%u) out of range (size: %u) for prepared statement", uint32(index), (uint32)m_queries.size());
return false;
}
/// not executed yet, just stored (it's not called a holder for nothing)
SQLElementData element;
element.type = SQL_ELEMENT_RAW;
element.element.query = strdup(sql);
SQLResultSetUnion result;
result.qresult = nullptr;
m_queries[index] = SQLResultPair(element, result);
m_queries[index].first = stmt;
return true;
}
bool SQLQueryHolder::SetPQuery(size_t index, const char* format, ...)
{
if (!format)
{
LOG_ERROR("server", "Query (index: %u) is empty.", uint32(index));
return false;
}
va_list ap;
char szQuery [MAX_QUERY_LEN];
va_start(ap, format);
int res = vsnprintf(szQuery, MAX_QUERY_LEN, format, ap);
va_end(ap);
if (res == -1)
{
LOG_ERROR("server", "SQL Query truncated (and not execute) for format: %s", format);
return false;
}
return SetQuery(index, szQuery);
}
bool SQLQueryHolder::SetPreparedQuery(size_t index, PreparedStatement* stmt)
{
if (m_queries.size() <= index)
{
LOG_ERROR("server", "Query index (%u) out of range (size: %u) for prepared statement", uint32(index), (uint32)m_queries.size());
return false;
}
/// not executed yet, just stored (it's not called a holder for nothing)
SQLElementData element;
element.type = SQL_ELEMENT_PREPARED;
element.element.stmt = stmt;
SQLResultSetUnion result;
result.presult = nullptr;
m_queries[index] = SQLResultPair(element, result);
return true;
}
QueryResult SQLQueryHolder::GetResult(size_t index)
{
// Don't call to this function if the index is of an ad-hoc statement
if (index < m_queries.size())
{
ResultSet* result = m_queries[index].second.qresult;
if (!result || !result->GetRowCount())
return QueryResult(nullptr);
result->NextRow();
return QueryResult(result);
}
else
return QueryResult(nullptr);
}
PreparedQueryResult SQLQueryHolder::GetPreparedResult(size_t index)
PreparedQueryResult SQLQueryHolderBase::GetPreparedResult(size_t index) const
{
// Don't call to this function if the index is of a prepared statement
if (index < m_queries.size())
{
PreparedResultSet* result = m_queries[index].second.presult;
if (!result || !result->GetRowCount())
return PreparedQueryResult(nullptr);
ASSERT(index < m_queries.size(), "Query holder result index out of range, tried to access index " SZFMTD " but there are only " SZFMTD " results",
index, m_queries.size());
return PreparedQueryResult(result);
}
else
return PreparedQueryResult(nullptr);
return m_queries[index].second;
}
void SQLQueryHolder::SetResult(size_t index, ResultSet* result)
void SQLQueryHolderBase::SetPreparedResult(size_t index, PreparedResultSet* result)
{
if (result && !result->GetRowCount())
{
@ -113,85 +41,45 @@ void SQLQueryHolder::SetResult(size_t index, ResultSet* result)
/// store the result in the holder
if (index < m_queries.size())
m_queries[index].second.qresult = result;
m_queries[index].second = PreparedQueryResult(result);
}
void SQLQueryHolder::SetPreparedResult(size_t index, PreparedResultSet* result)
SQLQueryHolderBase::~SQLQueryHolderBase()
{
if (result && !result->GetRowCount())
{
delete result;
result = nullptr;
}
/// store the result in the holder
if (index < m_queries.size())
m_queries[index].second.presult = result;
}
SQLQueryHolder::~SQLQueryHolder()
{
for (size_t i = 0; i < m_queries.size(); i++)
for (std::pair<PreparedStatementBase*, PreparedQueryResult>& query : m_queries)
{
/// if the result was never used, free the resources
/// results used already (getresult called) are expected to be deleted
if (SQLElementData* data = &m_queries[i].first)
{
switch (data->type)
{
case SQL_ELEMENT_RAW:
free((void*)(const_cast<char*>(data->element.query)));
break;
case SQL_ELEMENT_PREPARED:
delete data->element.stmt;
break;
}
}
delete query.first;
}
}
void SQLQueryHolder::SetSize(size_t size)
void SQLQueryHolderBase::SetSize(size_t size)
{
/// to optimize push_back, reserve the number of queries about to be executed
m_queries.resize(size);
}
SQLQueryHolderTask::~SQLQueryHolderTask() = default;
bool SQLQueryHolderTask::Execute()
{
//the result can't be ready as we are processing it right now
ASSERT(!m_result.ready());
/// execute all queries in the holder and pass the results
for (size_t i = 0; i < m_holder->m_queries.size(); ++i)
if (PreparedStatementBase* stmt = m_holder->m_queries[i].first)
m_holder->SetPreparedResult(i, m_conn->Query(stmt));
if (!m_holder)
return false;
/// we can do this, we are friends
std::vector<SQLQueryHolder::SQLResultPair>& queries = m_holder->m_queries;
for (size_t i = 0; i < queries.size(); i++)
{
/// execute all queries in the holder and pass the results
if (SQLElementData* data = &queries[i].first)
{
switch (data->type)
{
case SQL_ELEMENT_RAW:
{
char const* sql = data->element.query;
if (sql)
m_holder->SetResult(i, m_conn->Query(sql));
break;
}
case SQL_ELEMENT_PREPARED:
{
PreparedStatement* stmt = data->element.stmt;
if (stmt)
m_holder->SetPreparedResult(i, m_conn->Query(stmt));
break;
}
}
}
}
m_result.set(m_holder);
m_result.set_value();
return true;
}
bool SQLQueryHolderCallback::InvokeIfReady()
{
if (m_future.valid() && m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
{
m_callback(*m_holder);
return true;
}
return false;
}

View file

@ -1,45 +1,76 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _QUERYHOLDER_H
#define _QUERYHOLDER_H
#include <ace/Future.h>
#include "SQLOperation.h"
#include <vector>
class SQLQueryHolder
class AC_DATABASE_API SQLQueryHolderBase
{
friend class SQLQueryHolderTask;
friend class SQLQueryHolderTask;
private:
typedef std::pair<SQLElementData, SQLResultSetUnion> SQLResultPair;
std::vector<SQLResultPair> m_queries;
std::vector<std::pair<PreparedStatementBase*, PreparedQueryResult>> m_queries;
public:
SQLQueryHolder() = default;
~SQLQueryHolder();
bool SetQuery(size_t index, const char* sql);
bool SetPQuery(size_t index, const char* format, ...) ATTR_PRINTF(3, 4);
bool SetPreparedQuery(size_t index, PreparedStatement* stmt);
SQLQueryHolderBase() = default;
virtual ~SQLQueryHolderBase();
void SetSize(size_t size);
QueryResult GetResult(size_t index);
PreparedQueryResult GetPreparedResult(size_t index);
void SetResult(size_t index, ResultSet* result);
PreparedQueryResult GetPreparedResult(size_t index) const;
void SetPreparedResult(size_t index, PreparedResultSet* result);
protected:
bool SetPreparedQueryImpl(size_t index, PreparedStatementBase* stmt);
};
typedef ACE_Future<SQLQueryHolder*> QueryResultHolderFuture;
template<typename T>
class SQLQueryHolder : public SQLQueryHolderBase
{
public:
bool SetPreparedQuery(size_t index, PreparedStatement<T>* stmt)
{
return SetPreparedQueryImpl(index, stmt);
}
};
class SQLQueryHolderTask : public SQLOperation
class AC_DATABASE_API SQLQueryHolderTask : public SQLOperation
{
private:
SQLQueryHolder* m_holder;
QueryResultHolderFuture m_result;
std::shared_ptr<SQLQueryHolderBase> m_holder;
QueryResultHolderPromise m_result;
public:
SQLQueryHolderTask(SQLQueryHolder* holder, QueryResultHolderFuture res)
: m_holder(holder), m_result(res) { };
explicit SQLQueryHolderTask(std::shared_ptr<SQLQueryHolderBase> holder)
: m_holder(std::move(holder)) { }
~SQLQueryHolderTask();
bool Execute() override;
QueryResultHolderFuture GetFuture() { return m_result.get_future(); }
};
class AC_DATABASE_API SQLQueryHolderCallback
{
public:
SQLQueryHolderCallback(std::shared_ptr<SQLQueryHolderBase>&& holder, QueryResultHolderFuture&& future)
: 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) &
{
m_callback = std::move(callback);
}
bool InvokeIfReady();
std::shared_ptr<SQLQueryHolderBase> m_holder;
QueryResultHolderFuture m_future;
std::function<void(SQLQueryHolderBase const&)> m_callback;
};
#endif

View file

@ -1,33 +1,181 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "DatabaseEnv.h"
#include "QueryResult.h"
#include "Errors.h"
#include "Field.h"
#include "Log.h"
#include "MySQLHacks.h"
#include "MySQLWorkaround.h"
ResultSet::ResultSet(MYSQL_RES* result, MYSQL_FIELD* fields, uint64 rowCount, uint32 fieldCount) :
_rowCount(rowCount),
_fieldCount(fieldCount),
_result(result),
_fields(fields)
namespace
{
_currentRow = new Field[_fieldCount];
ASSERT(_currentRow);
static uint32 SizeForType(MYSQL_FIELD* field)
{
switch (field->type)
{
case MYSQL_TYPE_NULL:
return 0;
case MYSQL_TYPE_TINY:
return 1;
case MYSQL_TYPE_YEAR:
case MYSQL_TYPE_SHORT:
return 2;
case MYSQL_TYPE_INT24:
case MYSQL_TYPE_LONG:
case MYSQL_TYPE_FLOAT:
return 4;
case MYSQL_TYPE_DOUBLE:
case MYSQL_TYPE_LONGLONG:
case MYSQL_TYPE_BIT:
return 8;
case MYSQL_TYPE_TIMESTAMP:
case MYSQL_TYPE_DATE:
case MYSQL_TYPE_TIME:
case MYSQL_TYPE_DATETIME:
return sizeof(MYSQL_TIME);
case MYSQL_TYPE_TINY_BLOB:
case MYSQL_TYPE_MEDIUM_BLOB:
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_STRING:
case MYSQL_TYPE_VAR_STRING:
return field->max_length + 1;
case MYSQL_TYPE_DECIMAL:
case MYSQL_TYPE_NEWDECIMAL:
return 64;
case MYSQL_TYPE_GEOMETRY:
/*
Following types are not sent over the wire:
MYSQL_TYPE_ENUM:
MYSQL_TYPE_SET:
*/
default:
LOG_WARN("sql.sql", "SQL::SizeForType(): invalid field type %u", uint32(field->type));
return 0;
}
}
PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64 rowCount, uint32 fieldCount) :
m_rowCount(rowCount),
m_rowPosition(0),
m_fieldCount(fieldCount),
m_rBind(nullptr),
m_stmt(stmt),
m_res(result),
m_isNull(nullptr),
m_length(nullptr)
DatabaseFieldTypes MysqlTypeToFieldType(enum_field_types type)
{
if (!m_res)
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;
}
return DatabaseFieldTypes::Null;
}
static char const* FieldTypeToString(enum_field_types type)
{
switch (type)
{
case MYSQL_TYPE_BIT: return "BIT";
case MYSQL_TYPE_BLOB: return "BLOB";
case MYSQL_TYPE_DATE: return "DATE";
case MYSQL_TYPE_DATETIME: return "DATETIME";
case MYSQL_TYPE_NEWDECIMAL: return "NEWDECIMAL";
case MYSQL_TYPE_DECIMAL: return "DECIMAL";
case MYSQL_TYPE_DOUBLE: return "DOUBLE";
case MYSQL_TYPE_ENUM: return "ENUM";
case MYSQL_TYPE_FLOAT: return "FLOAT";
case MYSQL_TYPE_GEOMETRY: return "GEOMETRY";
case MYSQL_TYPE_INT24: return "INT24";
case MYSQL_TYPE_LONG: return "LONG";
case MYSQL_TYPE_LONGLONG: return "LONGLONG";
case MYSQL_TYPE_LONG_BLOB: return "LONG_BLOB";
case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB";
case MYSQL_TYPE_NEWDATE: return "NEWDATE";
case MYSQL_TYPE_NULL: return "NULL";
case MYSQL_TYPE_SET: return "SET";
case MYSQL_TYPE_SHORT: return "SHORT";
case MYSQL_TYPE_STRING: return "STRING";
case MYSQL_TYPE_TIME: return "TIME";
case MYSQL_TYPE_TIMESTAMP: return "TIMESTAMP";
case MYSQL_TYPE_TINY: return "TINY";
case MYSQL_TYPE_TINY_BLOB: return "TINY_BLOB";
case MYSQL_TYPE_VAR_STRING: return "VAR_STRING";
case MYSQL_TYPE_YEAR: return "YEAR";
default: return "-Unknown-";
}
}
void 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)
{
_fieldMetadata.resize(_fieldCount);
_currentRow = new Field[_fieldCount];
for (uint32 i = 0; i < _fieldCount; i++)
{
InitializeDatabaseFieldMetadata(&_fieldMetadata[i], &_fields[i], i);
_currentRow[i].SetMetadata(&_fieldMetadata[i]);
}
}
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)
{
if (!m_metadataResult)
return;
if (m_stmt->bind_result_done)
@ -36,48 +184,22 @@ PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64
delete[] m_stmt->bind->is_null;
}
m_rBind = new MYSQL_BIND[m_fieldCount];
m_isNull = new my_bool[m_fieldCount];
m_length = new unsigned long[m_fieldCount];
m_rBind = new MySQLBind[m_fieldCount];
memset(m_isNull, 0, sizeof(my_bool) * m_fieldCount);
memset(m_rBind, 0, sizeof(MYSQL_BIND) * m_fieldCount);
//- for future readers wondering where the fuck this is freed - mysql_stmt_bind_result moves pointers to these
// from m_rBind to m_stmt->bind and it is later freed by the `if (m_stmt->bind_result_done)` block just above here
// MYSQL_STMT lifetime is equal to connection lifetime
MySQLBool* m_isNull = new MySQLBool[m_fieldCount];
unsigned long* m_length = new unsigned long[m_fieldCount];
memset(m_isNull, 0, sizeof(MySQLBool) * m_fieldCount);
memset(m_rBind, 0, sizeof(MySQLBind) * m_fieldCount);
memset(m_length, 0, sizeof(unsigned long) * m_fieldCount);
//- This is where we store the (entire) resultset
if (mysql_stmt_store_result(m_stmt))
{
LOG_INFO("sql.driver", "%s:mysql_stmt_store_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt));
delete[] m_rBind;
delete[] m_isNull;
delete[] m_length;
return;
}
//- This is where we prepare the buffer based on metadata
uint32 i = 0;
MYSQL_FIELD* field = mysql_fetch_field(m_res);
while (field)
{
size_t size = Field::SizeForType(field);
m_rBind[i].buffer_type = field->type;
m_rBind[i].buffer = malloc(size);
memset(m_rBind[i].buffer, 0, size);
m_rBind[i].buffer_length = size;
m_rBind[i].length = &m_length[i];
m_rBind[i].is_null = &m_isNull[i];
m_rBind[i].error = nullptr;
m_rBind[i].is_unsigned = field->flags & UNSIGNED_FLAG;
++i;
field = mysql_fetch_field(m_res);
}
//- This is where we bind the bind the buffer to the statement
if (mysql_stmt_bind_result(m_stmt, m_rBind))
{
LOG_INFO("sql.driver", "%s:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(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));
delete[] m_rBind;
delete[] m_isNull;
delete[] m_length;
@ -86,18 +208,55 @@ PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64
m_rowCount = mysql_stmt_num_rows(m_stmt);
m_rows.resize(uint32(m_rowCount));
//- This is where we prepare the buffer based on metadata
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]);
rowSize += size;
InitializeDatabaseFieldMetadata(&m_fieldMetadata[i], &field[i], i);
m_rBind[i].buffer_type = field[i].type;
m_rBind[i].buffer_length = size;
m_rBind[i].length = &m_length[i];
m_rBind[i].is_null = &m_isNull[i];
m_rBind[i].error = nullptr;
m_rBind[i].is_unsigned = field[i].flags & UNSIGNED_FLAG;
}
char* dataBuffer = new char[rowSize * m_rowCount];
for (uint32 i = 0, offset = 0; i < m_fieldCount; ++i)
{
m_rBind[i].buffer = dataBuffer + offset;
offset += m_rBind[i].buffer_length;
}
//- 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));
mysql_stmt_free_result(m_stmt);
CleanUp();
delete[] m_isNull;
delete[] m_length;
return;
}
m_rows.resize(uint32(m_rowCount) * m_fieldCount);
while (_NextRow())
{
m_rows[uint32(m_rowPosition)] = new Field[m_fieldCount];
for (uint64 fIndex = 0; fIndex < m_fieldCount; ++fIndex)
for (uint32 fIndex = 0; fIndex < m_fieldCount; ++fIndex)
{
m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetMetadata(&m_fieldMetadata[fIndex]);
unsigned long buffer_length = m_rBind[fIndex].buffer_length;
unsigned long fetched_length = *m_rBind[fIndex].length;
if (!*m_rBind[fIndex].is_null)
m_rows[uint32(m_rowPosition)][fIndex].SetByteValue( m_rBind[fIndex].buffer,
m_rBind[fIndex].buffer_length,
m_rBind[fIndex].buffer_type,
*m_rBind[fIndex].length );
else
{
void* buffer = m_stmt->bind[fIndex].buffer;
switch (m_rBind[fIndex].buffer_type)
{
case MYSQL_TYPE_TINY_BLOB:
@ -106,24 +265,38 @@ PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64
case MYSQL_TYPE_BLOB:
case MYSQL_TYPE_STRING:
case MYSQL_TYPE_VAR_STRING:
m_rows[uint32(m_rowPosition)][fIndex].SetByteValue( "",
m_rBind[fIndex].buffer_length,
m_rBind[fIndex].buffer_type,
*m_rBind[fIndex].length );
// 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:
m_rows[uint32(m_rowPosition)][fIndex].SetByteValue( 0,
m_rBind[fIndex].buffer_length,
m_rBind[fIndex].buffer_type,
*m_rBind[fIndex].length );
break;
}
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_rowPosition++;
}
m_rowPosition = 0;
/// All data is buffered, let go of mysql c api structures
CleanUp();
mysql_stmt_free_result(m_stmt);
}
ResultSet::~ResultSet()
@ -133,8 +306,7 @@ ResultSet::~ResultSet()
PreparedResultSet::~PreparedResultSet()
{
for (uint32 i = 0; i < uint32(m_rowCount); ++i)
delete[] m_rows[i];
CleanUp();
}
bool ResultSet::NextRow()
@ -154,16 +326,23 @@ bool ResultSet::NextRow()
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], _fields[i].type, lengths[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
@ -181,25 +360,10 @@ bool PreparedResultSet::_NextRow()
if (m_rowPosition >= m_rowCount)
return false;
int retval = mysql_stmt_fetch( m_stmt );
if (!retval || retval == MYSQL_DATA_TRUNCATED)
retval = true;
if (retval == MYSQL_NO_DATA)
retval = false;
return retval;
int retval = mysql_stmt_fetch(m_stmt);
return retval == 0 || retval == MYSQL_DATA_TRUNCATED;
}
#ifdef ELUNA
std::string ResultSet::GetFieldName(uint32 index) const
{
ASSERT(index < _fieldCount);
return _fields[index].name;
}
#endif
void ResultSet::CleanUp()
{
if (_currentRow)
@ -215,20 +379,34 @@ void ResultSet::CleanUp()
}
}
Field const& ResultSet::operator[](std::size_t index) const
{
ASSERT(index < _fieldCount);
return _currentRow[index];
}
Field* PreparedResultSet::Fetch() const
{
ASSERT(m_rowPosition < m_rowCount);
return const_cast<Field*>(&m_rows[uint32(m_rowPosition) * m_fieldCount]);
}
Field const& PreparedResultSet::operator[](std::size_t index) const
{
ASSERT(m_rowPosition < m_rowCount);
ASSERT(index < m_fieldCount);
return m_rows[uint32(m_rowPosition) * m_fieldCount + index];
}
void PreparedResultSet::CleanUp()
{
/// More of the in our code allocated sources are deallocated by the poorly documented mysql c api
if (m_res)
mysql_free_result(m_res);
if (m_metadataResult)
mysql_free_result(m_metadataResult);
FreeBindBuffer();
mysql_stmt_free_result(m_stmt);
delete[] m_rBind;
}
void PreparedResultSet::FreeBindBuffer()
{
for (uint32 i = 0; i < m_fieldCount; ++i)
free (m_rBind[i].buffer);
if (m_rBind)
{
delete[](char*)m_rBind->buffer;
delete[] m_rBind;
m_rBind = nullptr;
}
}

View file

@ -1,99 +1,74 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef QUERYRESULT_H
#define QUERYRESULT_H
#include "Errors.h"
#include "Field.h"
#include <mutex>
#include "DatabaseEnvFwd.h"
#include "Define.h"
#include <vector>
#ifdef _WIN32
#include <winsock2.h>
#endif
#include <mysql.h>
#if !defined(MARIADB_VERSION_ID) && MYSQL_VERSION_ID >= 80001
typedef bool my_bool;
#endif
class ResultSet
class AC_DATABASE_API ResultSet
{
public:
ResultSet(MYSQL_RES* result, MYSQL_FIELD* fields, uint64 rowCount, uint32 fieldCount);
ResultSet(MySQLResult* result, MySQLField* fields, uint64 rowCount, uint32 fieldCount);
~ResultSet();
bool NextRow();
[[nodiscard]] uint64 GetRowCount() const { return _rowCount; }
[[nodiscard]] uint32 GetFieldCount() const { return _fieldCount; }
#ifdef ELUNA
uint64 GetRowCount() const { return _rowCount; }
uint32 GetFieldCount() const { return _fieldCount; }
std::string GetFieldName(uint32 index) const;
#endif
[[nodiscard]] Field* Fetch() const { return _currentRow; }
const Field& operator [] (uint32 index) const
{
ASSERT(index < _fieldCount);
return _currentRow[index];
}
Field* Fetch() const { return _currentRow; }
Field const& operator[](std::size_t index) const;
protected:
std::vector<QueryResultFieldMetadata> _fieldMetadata;
uint64 _rowCount;
Field* _currentRow;
uint32 _fieldCount;
private:
void CleanUp();
MYSQL_RES* _result;
MYSQL_FIELD* _fields;
MySQLResult* _result;
MySQLField* _fields;
ResultSet(ResultSet const& right) = delete;
ResultSet& operator=(ResultSet const& right) = delete;
};
typedef std::shared_ptr<ResultSet> QueryResult;
class PreparedResultSet
class AC_DATABASE_API PreparedResultSet
{
public:
PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64 rowCount, uint32 fieldCount);
PreparedResultSet(MySQLStmt* stmt, MySQLResult* result, uint64 rowCount, uint32 fieldCount);
~PreparedResultSet();
bool NextRow();
[[nodiscard]] uint64 GetRowCount() const { return m_rowCount; }
[[nodiscard]] uint32 GetFieldCount() const { return m_fieldCount; }
uint64 GetRowCount() const { return m_rowCount; }
uint32 GetFieldCount() const { return m_fieldCount; }
[[nodiscard]] Field* Fetch() const
{
ASSERT(m_rowPosition < m_rowCount);
return m_rows[uint32(m_rowPosition)];
}
const Field& operator [] (uint32 index) const
{
ASSERT(m_rowPosition < m_rowCount);
ASSERT(index < m_fieldCount);
return m_rows[uint32(m_rowPosition)][index];
}
Field* Fetch() const;
Field const& operator[](std::size_t index) const;
protected:
std::vector<Field*> m_rows;
std::vector<QueryResultFieldMetadata> m_fieldMetadata;
std::vector<Field> m_rows;
uint64 m_rowCount;
uint64 m_rowPosition;
uint32 m_fieldCount;
private:
MYSQL_BIND* m_rBind;
MYSQL_STMT* m_stmt;
MYSQL_RES* m_res;
MySQLBind* m_rBind;
MySQLStmt* m_stmt;
MySQLResult* m_metadataResult; ///< Field metadata, returned by mysql_stmt_result_metadata
my_bool* m_isNull;
unsigned long* m_length;
void FreeBindBuffer();
void CleanUp();
bool _NextRow();
PreparedResultSet(PreparedResultSet const& right) = delete;
PreparedResultSet& operator=(PreparedResultSet const& right) = delete;
};
typedef std::shared_ptr<PreparedResultSet> PreparedQueryResult;
#endif

View file

@ -1,25 +1,19 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _SQLOPERATION_H
#define _SQLOPERATION_H
#include <ace/Method_Request.h>
#include <ace/Activation_Queue.h>
#include "QueryResult.h"
//- Forward declare (don't include header to prevent circular includes)
class PreparedStatement;
#include "DatabaseEnvFwd.h"
#include "Define.h"
//- Union that holds element data
union SQLElementUnion
{
PreparedStatement* stmt;
const char* query;
PreparedStatementBase* stmt;
char const* query;
};
//- Type specifier of our element data
@ -36,20 +30,15 @@ struct SQLElementData
SQLElementDataType type;
};
//- For ambigious resultsets
union SQLResultSetUnion
{
PreparedResultSet* presult;
ResultSet* qresult;
};
class MySQLConnection;
class SQLOperation : public ACE_Method_Request
class AC_DATABASE_API SQLOperation
{
public:
SQLOperation(): m_conn(nullptr) { }
int call() override
virtual ~SQLOperation() { }
virtual int call()
{
Execute();
return 0;
@ -58,6 +47,10 @@ public:
virtual void SetConnection(MySQLConnection* con) { m_conn = con; }
MySQLConnection* m_conn;
private:
SQLOperation(SQLOperation const& right) = delete;
SQLOperation& operator=(SQLOperation const& right) = delete;
};
#endif

View file

@ -1,16 +1,23 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "DatabaseEnv.h"
#include "Transaction.h"
#include "Log.h"
#include "MySQLConnection.h"
#include "PreparedStatement.h"
#include "Timer.h"
#include <mysqld_error.h>
#include <sstream>
#include <thread>
std::mutex TransactionTask::_deadlockLock;
#define DEADLOCK_MAX_RETRY_TIME_MS 60000
//- Append a raw ad-hoc query to the transaction
void Transaction::Append(const char* sql)
void TransactionBase::Append(char const* sql)
{
SQLElementData data;
data.type = SQL_ELEMENT_RAW;
@ -18,19 +25,8 @@ void Transaction::Append(const char* sql)
m_queries.push_back(data);
}
void Transaction::PAppend(const char* sql, ...)
{
va_list ap;
char szQuery [MAX_QUERY_LEN];
va_start(ap, sql);
vsnprintf(szQuery, MAX_QUERY_LEN, sql, ap);
va_end(ap);
Append(szQuery);
}
//- Append a prepared statement to the transaction
void Transaction::Append(PreparedStatement* stmt)
void TransactionBase::AppendPreparedStatement(PreparedStatementBase* stmt)
{
SQLElementData data;
data.type = SQL_ELEMENT_PREPARED;
@ -38,47 +34,116 @@ void Transaction::Append(PreparedStatement* stmt)
m_queries.push_back(data);
}
void Transaction::Cleanup()
void TransactionBase::Cleanup()
{
// This might be called by explicit calls to Cleanup or by the auto-destructor
if (_cleanedUp)
return;
while (!m_queries.empty())
for (SQLElementData const& data : m_queries)
{
SQLElementData const& data = m_queries.front();
switch (data.type)
{
case SQL_ELEMENT_PREPARED:
delete data.element.stmt;
break;
break;
case SQL_ELEMENT_RAW:
free((void*)(data.element.query));
break;
break;
}
m_queries.pop_front();
}
m_queries.clear();
_cleanedUp = true;
}
bool TransactionTask::Execute()
{
int errorCode = m_conn->ExecuteTransaction(m_trans);
int errorCode = TryExecute();
if (!errorCode)
return true;
if (errorCode == ER_LOCK_DEADLOCK)
{
uint8 loopBreaker = 5; // Handle MySQL Errno 1213 without extending deadlock to the core itself
for (uint8 i = 0; i < loopBreaker; ++i)
if (!m_conn->ExecuteTransaction(m_trans))
std::ostringstream threadIdStream;
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;
LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: %u ms, Thread Id: %s", loopDuration, threadId.c_str());
}
LOG_ERROR("sql.sql", "Fatal deadlocked SQL Transaction, it will not be retried anymore. Thread Id: %s", threadId.c_str());
}
// Clean up now.
m_trans->Cleanup();
CleanupOnFailure();
return false;
}
int TransactionTask::TryExecute()
{
return m_conn->ExecuteTransaction(m_trans);
}
void TransactionTask::CleanupOnFailure()
{
m_trans->Cleanup();
}
bool TransactionWithResultTask::Execute()
{
int errorCode = TryExecute();
if (!errorCode)
{
m_result.set_value(true);
return true;
}
if (errorCode == ER_LOCK_DEADLOCK)
{
std::ostringstream threadIdStream;
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;
}
LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: %u ms, Thread Id: %s", loopDuration, threadId.c_str());
}
LOG_ERROR("sql.sql", "Fatal deadlocked SQL Transaction, it will not be retried anymore. Thread Id: %s", threadId.c_str());
}
// Clean up now.
CleanupOnFailure();
m_result.set_value(false);
return false;
}
bool TransactionCallback::InvokeIfReady()
{
if (m_future.valid() && m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
{
m_callback(m_future.get());
return true;
}
return false;
}

View file

@ -1,62 +1,111 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef _TRANSACTION_H
#define _TRANSACTION_H
#include "DatabaseEnvFwd.h"
#include "Define.h"
#include "SQLOperation.h"
#include <list>
#include <utility>
//- Forward declare (don't include header to prevent circular includes)
class PreparedStatement;
#include "StringFormat.h"
#include <functional>
#include <mutex>
#include <vector>
/*! Transactions, high level class. */
class Transaction
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:
Transaction() { }
~Transaction() { Cleanup(); }
TransactionBase() : _cleanedUp(false) { }
virtual ~TransactionBase() { Cleanup(); }
void Append(PreparedStatement* statement);
void Append(const char* sql);
void PAppend(const char* sql, ...);
void Append(char const* sql);
template<typename Format, typename... Args>
void PAppend(Format&& sql, Args&&... args)
{
Append(Acore::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
}
[[nodiscard]] size_t GetSize() const { return m_queries.size(); }
std::size_t GetSize() const { return m_queries.size(); }
protected:
void AppendPreparedStatement(PreparedStatementBase* statement);
void Cleanup();
std::list<SQLElementData> m_queries;
std::vector<SQLElementData> m_queries;
private:
bool _cleanedUp{false};
bool _cleanedUp;
};
typedef std::shared_ptr<Transaction> SQLTransaction;
template<typename T>
class Transaction : public TransactionBase
{
public:
using TransactionBase::Append;
void Append(PreparedStatement<T>* statement)
{
AppendPreparedStatement(statement);
}
};
/*! Low level class*/
class TransactionTask : public SQLOperation
class AC_DATABASE_API TransactionTask : public SQLOperation
{
template <class T> friend class DatabaseWorkerPool;
friend class DatabaseWorker;
template <class T> friend class DatabaseWorkerPool;
friend class DatabaseWorker;
friend class TransactionCallback;
public:
TransactionTask(SQLTransaction trans) : m_trans(std::move(trans)) { } ;
~TransactionTask() override = default;
TransactionTask(std::shared_ptr<TransactionBase> trans) : m_trans(trans) { }
~TransactionTask() { }
protected:
bool Execute() override;
int TryExecute();
void CleanupOnFailure();
std::shared_ptr<TransactionBase> m_trans;
static std::mutex _deadlockLock;
};
class AC_DATABASE_API TransactionWithResultTask : public TransactionTask
{
public:
TransactionWithResultTask(std::shared_ptr<TransactionBase> trans) : TransactionTask(trans) { }
TransactionFuture GetFuture() { return m_result.get_future(); }
protected:
bool Execute() override;
SQLTransaction m_trans;
TransactionPromise m_result;
};
class AC_DATABASE_API TransactionCallback
{
public:
TransactionCallback(TransactionFuture&& future) : m_future(std::move(future)) { }
TransactionCallback(TransactionCallback&&) = default;
TransactionCallback& operator=(TransactionCallback&&) = default;
void AfterComplete(std::function<void(bool)> callback) &
{
m_callback = std::move(callback);
}
bool InvokeIfReady();
TransactionFuture m_future;
std::function<void(bool)> m_callback;
};
#endif

View file

@ -19,7 +19,7 @@ void AppenderDB::_write(LogMessage const* message)
if (!enabled || (message->type.find("sql") != std::string::npos))
return;
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_LOG);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_LOG);
stmt->setUInt64(0, message->mtime);
stmt->setUInt32(1, realmId);
stmt->setString(2, message->type);

View file

@ -1,19 +1,23 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "Define.h"
// #include "DatabaseEnvFwd.h"
#include "DatabaseEnvFwd.h"
#include "Errors.h"
#include "Field.h"
#include "Log.h"
#include "MySQLConnection.h"
// #include "MySQLPreparedStatement.h"
// #include "MySQLWorkaround.h"
#include "MySQLPreparedStatement.h"
#include "MySQLWorkaround.h"
#include "PreparedStatement.h"
#include "QueryResult.h"
#include "SQLOperation.h"
#include "Transaction.h"
#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7
#include <winsock2.h>
#endif
#include <mysql.h>
#include <string>
#include <vector>

View file

@ -0,0 +1,433 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "DBUpdater.h"
#include "BuiltInConfig.h"
#include "Config.h"
#include "DatabaseEnv.h"
#include "DatabaseLoader.h"
#include "GitRevision.h"
#include "Log.h"
#include "QueryResult.h"
#include "StartProcess.h"
#include "UpdateFetcher.h"
#include <boost/filesystem/operations.hpp>
#include <fstream>
#include <iostream>
std::string DBUpdaterUtil::GetCorrectedMySQLExecutable()
{
if (!corrected_path().empty())
return corrected_path();
else
return BuiltInConfig::GetMySQLExecutable();
}
bool DBUpdaterUtil::CheckExecutable()
{
boost::filesystem::path exe(GetCorrectedMySQLExecutable());
if (!is_regular_file(exe))
{
exe = Warhead::SearchExecutableInPath("mysql");
if (!exe.empty() && is_regular_file(exe))
{
// Correct the path to the cli
corrected_path() = absolute(exe).generic_string();
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());
return false;
}
return true;
}
std::string& DBUpdaterUtil::corrected_path()
{
static std::string path;
return path;
}
// Auth Database
template<>
std::string DBUpdater<LoginDatabaseConnection>::GetConfigEntry()
{
return "Updates.Auth";
}
template<>
std::string DBUpdater<LoginDatabaseConnection>::GetTableName()
{
return "Auth";
}
template<>
std::string DBUpdater<LoginDatabaseConnection>::GetBaseFilesDirectory()
{
return BuiltInConfig::GetSourceDirectory() + "/data/sql/base/db_auth/";
}
template<>
bool DBUpdater<LoginDatabaseConnection>::IsEnabled(uint32 const updateMask)
{
// This way silences warnings under msvc
return (updateMask & DatabaseLoader::DATABASE_LOGIN) ? true : false;
}
template<>
std::string DBUpdater<LoginDatabaseConnection>::GetDBModuleName()
{
return "db_auth";
}
// World Database
template<>
std::string DBUpdater<WorldDatabaseConnection>::GetConfigEntry()
{
return "Updates.World";
}
template<>
std::string DBUpdater<WorldDatabaseConnection>::GetTableName()
{
return "World";
}
template<>
std::string DBUpdater<WorldDatabaseConnection>::GetBaseFilesDirectory()
{
return BuiltInConfig::GetSourceDirectory() + "/data/sql/base/db_world/";
}
template<>
bool DBUpdater<WorldDatabaseConnection>::IsEnabled(uint32 const updateMask)
{
// This way silences warnings under msvc
return (updateMask & DatabaseLoader::DATABASE_WORLD) ? true : false;
}
template<>
std::string DBUpdater<WorldDatabaseConnection>::GetDBModuleName()
{
return "db_world";
}
// Character Database
template<>
std::string DBUpdater<CharacterDatabaseConnection>::GetConfigEntry()
{
return "Updates.Character";
}
template<>
std::string DBUpdater<CharacterDatabaseConnection>::GetTableName()
{
return "Character";
}
template<>
std::string DBUpdater<CharacterDatabaseConnection>::GetBaseFilesDirectory()
{
return BuiltInConfig::GetSourceDirectory() + "/data/sql/base/db_characters/";
}
template<>
bool DBUpdater<CharacterDatabaseConnection>::IsEnabled(uint32 const updateMask)
{
// This way silences warnings under msvc
return (updateMask & DatabaseLoader::DATABASE_CHARACTER) ? true : false;
}
template<>
std::string DBUpdater<CharacterDatabaseConnection>::GetDBModuleName()
{
return "db_characters";
}
// All
template<class T>
BaseLocation DBUpdater<T>::GetBaseLocationType()
{
return LOCATION_REPOSITORY;
}
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());
std::string answer;
std::getline(std::cin, answer);
if (!sConfigMgr->isDryRun() && !answer.empty() && !(answer.substr(0, 1) == "y"))
return false;
LOG_INFO("sql.updates", "Creating database \"%s\"...", pool.GetConnectionInfo()->database.c_str());
// Path of temp file
static Path const temp("create_table.sql");
// Create temporary query to use external MySQL CLi
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());
return false;
}
file << "CREATE DATABASE `" << pool.GetConnectionInfo()->database << "` DEFAULT CHARACTER SET UTF8MB4 COLLATE utf8mb4_general_ci;\n\n";
file.close();
try
{
DBUpdater<T>::ApplyFile(pool, pool.GetConnectionInfo()->host, pool.GetConnectionInfo()->user, pool.GetConnectionInfo()->password,
pool.GetConnectionInfo()->port_or_socket, "", pool.GetConnectionInfo()->ssl, temp);
}
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());
boost::filesystem::remove(temp);
return false;
}
LOG_INFO("sql.updates", "Done.");
LOG_INFO("sql.updates", " ");
boost::filesystem::remove(temp);
return true;
}
template<class T>
bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool)
{
if (!DBUpdaterUtil::CheckExecutable())
return false;
LOG_INFO("sql.updates", "Updating %s database...", DBUpdater<T>::GetTableName().c_str());
Path const sourceDirectory(BuiltInConfig::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());
return false;
}
auto CheckUpdateTable = [&](std::string const& tableName)
{
auto checkTable = DBUpdater<T>::Retrieve(pool, Warhead::StringFormat("SHOW TABLES LIKE '%s'", tableName.c_str()));
if (!checkTable)
{
LOG_WARN("sql.updates", "> Table '%s' not exist! Try add based table", tableName.c_str());
Path const temp(GetBaseFilesDirectory() + tableName + ".sql");
try
{
DBUpdater<T>::ApplyFile(pool, temp);
}
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());
return false;
}
return true;
}
return true;
};
if (!CheckUpdateTable("updates") || !CheckUpdateTable("updates_include"))
return false;
UpdateFetcher updateFetcher(sourceDirectory, [&](std::string const & query) { DBUpdater<T>::Apply(pool, query); },
[&](Path const & file) { DBUpdater<T>::ApplyFile(pool, file); },
[&](std::string const & query) -> QueryResult { return DBUpdater<T>::Retrieve(pool, query); }, DBUpdater<T>::GetDBModuleName());
UpdateResult result;
try
{
result = updateFetcher.Update(
sConfigMgr->GetBoolDefault("Updates.Redundancy", true),
sConfigMgr->GetBoolDefault("Updates.AllowRehash", true),
sConfigMgr->GetBoolDefault("Updates.ArchivedRedundancy", false),
sConfigMgr->GetIntDefault("Updates.CleanDeadRefMaxCount", 3));
}
catch (UpdateException&)
{
return false;
}
std::string const info = Warhead::StringFormat("Containing " SZFMTD " new and " SZFMTD " 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());
else
LOG_INFO("sql.updates", ">> Applied " SZFMTD " %s. %s", result.updated, result.updated == 1 ? "query" : "queries", info.c_str());
LOG_INFO("sql.updates", " ");
return true;
}
template<class T>
bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
{
{
QueryResult const result = Retrieve(pool, "SHOW TABLES");
if (result && (result->GetRowCount() > 0))
return true;
}
if (!DBUpdaterUtil::CheckExecutable())
return false;
LOG_INFO("sql.updates", "Database %s is empty, auto populating it...", DBUpdater<T>::GetTableName().c_str());
std::string const DirPathStr = DBUpdater<T>::GetBaseFilesDirectory();
Path const DirPath(DirPathStr);
if (!boost::filesystem::is_directory(DirPath))
{
LOG_ERROR("sql.updates", ">> Directory \"%s\" not exist", DirPath.generic_string().c_str());
return false;
}
if (DirPath.empty())
{
LOG_ERROR("sql.updates", ">> Directory \"%s\" is empty", DirPath.generic_string().c_str());
return false;
}
boost::filesystem::directory_iterator const DirItr;
uint32 FilesCount = 0;
for (boost::filesystem::directory_iterator itr(DirPath); itr != DirItr; ++itr)
{
if (itr->path().extension() == ".sql")
FilesCount++;
}
if (!FilesCount)
{
LOG_ERROR("sql.updates", ">> In directory \"%s\" not exist '*.sql' files", DirPath.generic_string().c_str());
return false;
}
for (boost::filesystem::directory_iterator itr(DirPath); itr != DirItr; ++itr)
{
if (itr->path().extension() != ".sql")
continue;
LOG_INFO("sql.updates", ">> Applying \'%s\'...", itr->path().filename().generic_string().c_str());
try
{
ApplyFile(pool, itr->path());
}
catch (UpdateException&)
{
return false;
}
}
LOG_INFO("sql.updates", ">> Done!");
LOG_INFO("sql.updates", " ");
return true;
}
template<class T>
QueryResult DBUpdater<T>::Retrieve(DatabaseWorkerPool<T>& pool, std::string const& query)
{
return pool.Query(query.c_str());
}
template<class T>
void DBUpdater<T>::Apply(DatabaseWorkerPool<T>& pool, std::string const& query)
{
pool.DirectExecute(query.c_str());
}
template<class T>
void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, Path const& path)
{
DBUpdater<T>::ApplyFile(pool, pool.GetConnectionInfo()->host, pool.GetConnectionInfo()->user, pool.GetConnectionInfo()->password,
pool.GetConnectionInfo()->port_or_socket, pool.GetConnectionInfo()->database, pool.GetConnectionInfo()->ssl, path);
}
template<class T>
void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& host, std::string const& user,
std::string const& password, std::string const& port_or_socket, std::string const& database, std::string const& ssl, Path const& path)
{
std::vector<std::string> args;
args.reserve(7);
// CLI Client connection info
args.emplace_back("-h" + host);
args.emplace_back("-u" + user);
if (!password.empty())
args.emplace_back("-p" + password);
// Check if we want to connect through ip or socket (Unix only)
#ifdef _WIN32
if (host == ".")
args.emplace_back("--protocol=PIPE");
else
args.emplace_back("-P" + port_or_socket);
#else
if (!std::isdigit(port_or_socket[0]))
{
// We can't check if host == "." here, because it is named localhost if socket option is enabled
args.emplace_back("-P0");
args.emplace_back("--protocol=SOCKET");
args.emplace_back("-S" + port_or_socket);
}
else
// generic case
args.emplace_back("-P" + port_or_socket);
#endif
// Set the default charset to utf8
args.emplace_back("--default-character-set=utf8");
// Set max allowed packet to 1 GB
args.emplace_back("--max-allowed-packet=1GB");
if (ssl == "ssl")
args.emplace_back("--ssl");
// Database
if (!database.empty())
args.emplace_back(database);
// Invokes a mysql process which doesn't leak credentials to logs
int const ret = Warhead::StartProcess(DBUpdaterUtil::GetCorrectedMySQLExecutable(), args,
"sql.updates", path.generic_string(), true);
if (ret != EXIT_SUCCESS)
{
LOG_FATAL("sql.updates", "Applying of file \'%s\' to database \'%s\' 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 WarheadCore 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());
throw UpdateException("update failed");
}
}
template class AC_DATABASE_API DBUpdater<LoginDatabaseConnection>;
template class AC_DATABASE_API DBUpdater<WorldDatabaseConnection>;
template class AC_DATABASE_API DBUpdater<CharacterDatabaseConnection>;

View file

@ -0,0 +1,79 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef DBUpdater_h__
#define DBUpdater_h__
#include "DatabaseEnv.h"
#include "Define.h"
#include <string>
template <class T>
class DatabaseWorkerPool;
namespace boost
{
namespace filesystem
{
class path;
}
}
class AC_DATABASE_API UpdateException : public std::exception
{
public:
UpdateException(std::string const& msg) : _msg(msg) { }
~UpdateException() throw() { }
char const* what() const throw() override { return _msg.c_str(); }
private:
std::string const _msg;
};
enum BaseLocation
{
LOCATION_REPOSITORY,
LOCATION_DOWNLOAD
};
class AC_DATABASE_API DBUpdaterUtil
{
public:
static std::string GetCorrectedMySQLExecutable();
static bool CheckExecutable();
private:
static std::string& corrected_path();
};
template <class T>
class AC_DATABASE_API DBUpdater
{
public:
using Path = boost::filesystem::path;
static inline std::string GetConfigEntry();
static inline std::string GetTableName();
static std::string GetBaseFilesDirectory();
static bool IsEnabled(uint32 const updateMask);
static BaseLocation GetBaseLocationType();
static bool Create(DatabaseWorkerPool<T>& pool);
static bool Update(DatabaseWorkerPool<T>& pool);
static bool Populate(DatabaseWorkerPool<T>& pool);
// module
static std::string GetDBModuleName();
private:
static QueryResult Retrieve(DatabaseWorkerPool<T>& pool, std::string const& query);
static void Apply(DatabaseWorkerPool<T>& pool, std::string const& query);
static void ApplyFile(DatabaseWorkerPool<T>& pool, Path const& path);
static void ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& host, std::string const& user,
std::string const& password, std::string const& port_or_socket, std::string const& database, std::string const& ssl, Path const& path);
};
#endif // DBUpdater_h__

View file

@ -0,0 +1,433 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#include "UpdateFetcher.h"
#include "Common.h"
#include "CryptoHash.h"
#include "DBUpdater.h"
#include "Field.h"
#include "Log.h"
#include "QueryResult.h"
#include "Tokenize.h"
#include "Util.h"
#include <boost/filesystem/operations.hpp>
#include <fstream>
#include <sstream>
using namespace boost::filesystem;
struct UpdateFetcher::DirectoryEntry
{
DirectoryEntry(Path const& path_, State state_) : path(path_), state(state_) { }
Path const path;
State const state;
};
UpdateFetcher::UpdateFetcher(Path const& sourceDirectory,
std::function<void(std::string const&)> const& apply,
std::function<void(Path const& path)> const& applyFile,
std::function<QueryResult(std::string const&)> const& retrieve, std::string const& dbModuleName_) :
_sourceDirectory(std::make_unique<Path>(sourceDirectory)), _apply(apply), _applyFile(applyFile),
_retrieve(retrieve), _dbModuleName(dbModuleName_)
{
}
UpdateFetcher::~UpdateFetcher()
{
}
UpdateFetcher::LocaleFileStorage UpdateFetcher::GetFileList() const
{
LocaleFileStorage files;
DirectoryStorage directories = ReceiveIncludedDirectories();
for (auto const& entry : directories)
FillFileListRecursively(entry.path, files, entry.state, 1);
return files;
}
void UpdateFetcher::FillFileListRecursively(Path const& path, LocaleFileStorage& storage, State const state, uint32 const depth) const
{
static uint32 const MAX_DEPTH = 10;
static directory_iterator const end;
for (directory_iterator itr(path); itr != end; ++itr)
{
if (is_directory(itr->path()))
{
if (depth < MAX_DEPTH)
FillFileListRecursively(itr->path(), storage, state, depth + 1);
}
else if (itr->path().extension() == ".sql")
{
LOG_TRACE("sql.updates", "Added locale file \"%s\".", itr->path().filename().generic_string().c_str());
LocaleFileEntry const entry = { itr->path(), state };
// Check for doubled filenames
// 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());
throw UpdateException("Updating failed, see the log for details.");
}
storage.insert(entry);
}
}
}
UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories() const
{
DirectoryStorage directories;
QueryResult const result = _retrieve("SELECT `path`, `state` FROM `updates_include`");
if (!result)
return directories;
do
{
Field* fields = result->Fetch();
std::string path = fields[0].GetString();
if (path.substr(0, 1) == "$")
path = _sourceDirectory->generic_string() + path.substr(1);
Path const p(path);
if (!is_directory(p))
{
LOG_WARN("sql.updates", "DBUpdater: Given update include directory \"%s\" does not exist, skipped!", p.generic_string().c_str());
continue;
}
DirectoryEntry const entry = { p, AppliedFileEntry::StateConvert(fields[1].GetString()) };
directories.push_back(entry);
LOG_TRACE("sql.updates", "Added applied file \"%s\" from remote.", p.filename().generic_string().c_str());
} while (result->NextRow());
std::vector<std::string> moduleList;
auto const& _modulesTokens = Warhead::Tokenize(WH_MODULES_LIST, ',', true);
for (auto const& itr : _modulesTokens)
moduleList.push_back(std::string(itr));
for (auto const& itr : moduleList)
{
std::string path = _sourceDirectory->generic_string() + "/modules/" + itr + "/sql/" + _dbModuleName; // module/mod-name/sql/db_world
Path const p(path);
if (!is_directory(p))
continue;
DirectoryEntry const entry = { p, AppliedFileEntry::StateConvert("RELEASED") };
directories.push_back(entry);
LOG_TRACE("sql.updates", "Added applied modules file \"%s\" from remote.", p.filename().generic_string().c_str());
}
return directories;
}
UpdateFetcher::AppliedFileStorage UpdateFetcher::ReceiveAppliedFiles() const
{
AppliedFileStorage map;
QueryResult result = _retrieve("SELECT `name`, `hash`, `state`, UNIX_TIMESTAMP(`timestamp`) FROM `updates` ORDER BY `name` ASC");
if (!result)
return map;
do
{
Field* fields = result->Fetch();
AppliedFileEntry const entry = { fields[0].GetString(), fields[1].GetString(),
AppliedFileEntry::StateConvert(fields[2].GetString()), fields[3].GetUInt64()
};
map.insert(std::make_pair(entry.name, entry));
} while (result->NextRow());
return map;
}
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! "
"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());
throw UpdateException("Opening the sql update failed!");
}
auto update = [&in]
{
std::ostringstream ss;
ss << in.rdbuf();
return ss.str();
}();
in.close();
return update;
}
UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
bool const allowRehash,
bool const archivedRedundancy,
int32 const cleanDeadReferencesMaxCount) const
{
LocaleFileStorage const available = GetFileList();
AppliedFileStorage applied = ReceiveAppliedFiles();
size_t countRecentUpdates = 0;
size_t countArchivedUpdates = 0;
// Count updates
for (auto const& entry : applied)
if (entry.second.state == RELEASED)
++countRecentUpdates;
else
++countArchivedUpdates;
// Fill hash to name cache
HashToFileNameStorage hashToName;
for (auto entry : applied)
hashToName.insert(std::make_pair(entry.second.hash, entry.first));
size_t importedUpdates = 0;
for (auto const& availableQuery : available)
{
LOG_DEBUG("sql.updates", "Checking update \"%s\"...", availableQuery.first.filename().generic_string().c_str());
AppliedFileStorage::const_iterator iter = applied.find(availableQuery.first.filename().string());
if (iter != applied.end())
{
// If redundancy is disabled, skip it, because the update is already applied.
if (!redundancyChecks)
{
LOG_DEBUG("sql.updates", ">> Update is already applied, skipping redundancy checks.");
applied.erase(iter);
continue;
}
// If the update is in an archived directory and is marked as archived in our database, skip redundancy checks (archived updates never change).
if (!archivedRedundancy && (iter->second.state == ARCHIVED) && (availableQuery.second == ARCHIVED))
{
LOG_DEBUG("sql.updates", ">> Update is archived and marked as archived in database, skipping redundancy checks.");
applied.erase(iter);
continue;
}
}
std::string const hash = ByteArrayToHexStr(Warhead::Crypto::SHA1::GetDigestOf(ReadSQLUpdate(availableQuery.first)));
UpdateMode mode = MODE_APPLY;
// Update is not in our applied list
if (iter == applied.end())
{
// Catch renames (different filename, but same hash)
HashToFileNameStorage::const_iterator const hashIter = hashToName.find(hash);
if (hashIter != hashToName.end())
{
// Check if the original file was removed. If not, we've got a problem.
LocaleFileStorage::const_iterator localeIter;
// Push localeIter forward
for (localeIter = available.begin(); (localeIter != available.end()) &&
(localeIter->first.filename().string() != hashIter->second); ++localeIter);
// 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\")",
availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str(),
localeIter->first.filename().string().c_str());
}
// It is safe to treat the file as renamed here
else
{
LOG_INFO("sql.updates", ">> Renaming update \"%s\" to \"%s\" \'%s\'.",
hashIter->second.c_str(), availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str());
RenameEntry(hashIter->second, availableQuery.first.filename().string());
applied.erase(hashIter->second);
continue;
}
}
// Apply the update if it was never seen before.
else
{
LOG_INFO("sql.updates", ">> Applying update \"%s\" \'%s\'...",
availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str());
}
}
// Rehash the update entry if it exists in our database with an empty hash.
else if (allowRehash && iter->second.hash.empty())
{
mode = MODE_REHASH;
LOG_INFO("sql.updates", ">> Re-hashing update \"%s\" \'%s\'...", availableQuery.first.filename().string().c_str(),
hash.substr(0, 7).c_str());
}
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)...", availableQuery.first.filename().string().c_str(),
iter->second.hash.substr(0, 7).c_str(), hash.substr(0, 7).c_str());
}
else
{
// If the file wasn't changed and just moved, update its state (if necessary).
if (iter->second.state != availableQuery.second)
{
LOG_DEBUG("sql.updates", ">> Updating the state of \"%s\" to \'%s\'...",
availableQuery.first.filename().string().c_str(), AppliedFileEntry::StateConvert(availableQuery.second).c_str());
UpdateState(availableQuery.first.filename().string(), availableQuery.second);
}
LOG_DEBUG("sql.updates", ">> Update is already applied and matches the hash \'%s\'.", hash.substr(0, 7).c_str());
applied.erase(iter);
continue;
}
}
uint32 speed = 0;
AppliedFileEntry const file = { availableQuery.first.filename().string(), hash, availableQuery.second, 0 };
switch (mode)
{
case MODE_APPLY:
speed = Apply(availableQuery.first);
/* fallthrough */
case MODE_REHASH:
UpdateEntry(file, speed);
break;
}
if (iter != applied.end())
applied.erase(iter);
if (mode == MODE_APPLY)
++importedUpdates;
}
// Cleanup up orphaned entries (if enabled)
if (!applied.empty())
{
bool const doCleanup = (cleanDeadReferencesMaxCount < 0) || (applied.size() <= static_cast<size_t>(cleanDeadReferencesMaxCount));
for (auto const& entry : applied)
{
LOG_WARN("sql.updates", ">> The file \'%s\' was applied to the database, but is missing in" \
" your update directory now!", entry.first.c_str());
if (doCleanup)
LOG_INFO("sql.updates", "Deleting orphaned entry \'%s\'...", entry.first.c_str());
}
if (doCleanup)
CleanUp(applied);
else
{
LOG_ERROR("sql.updates", "Cleanup is disabled! There were " SZFMTD " dirty files applied to your database, " \
"but they are now missing in your source directory!", applied.size());
}
}
return UpdateResult(importedUpdates, countRecentUpdates, countArchivedUpdates);
}
uint32 UpdateFetcher::Apply(Path const& path) const
{
using Time = std::chrono::high_resolution_clock;
// Benchmark query speed
auto const begin = Time::now();
// Update database
_applyFile(path);
// Return the time it took the query to apply
return uint32(std::chrono::duration_cast<std::chrono::milliseconds>(Time::now() - begin).count());
}
void UpdateFetcher::UpdateEntry(AppliedFileEntry const& entry, uint32 const speed) const
{
std::string const update = "REPLACE INTO `updates` (`name`, `hash`, `state`, `speed`) VALUES (\"" +
entry.name + "\", \"" + entry.hash + "\", \'" + entry.GetStateAsString() + "\', " + std::to_string(speed) + ")";
// Update database
_apply(update);
}
void UpdateFetcher::RenameEntry(std::string const& from, std::string const& to) const
{
// Delete the target if it exists
{
std::string const update = "DELETE FROM `updates` WHERE `name`=\"" + to + "\"";
// Update database
_apply(update);
}
// Rename
{
std::string const update = "UPDATE `updates` SET `name`=\"" + to + "\" WHERE `name`=\"" + from + "\"";
// Update database
_apply(update);
}
}
void UpdateFetcher::CleanUp(AppliedFileStorage const& storage) const
{
if (storage.empty())
return;
std::stringstream update;
size_t remaining = storage.size();
update << "DELETE FROM `updates` WHERE `name` IN(";
for (auto const& entry : storage)
{
update << "\"" << entry.first << "\"";
if ((--remaining) > 0)
update << ", ";
}
update << ")";
// Update database
_apply(update.str());
}
void UpdateFetcher::UpdateState(std::string const& name, State const state) const
{
std::string const update = "UPDATE `updates` SET `state`=\'" + AppliedFileEntry::StateConvert(state) + "\' WHERE `name`=\"" + name + "\"";
// Update database
_apply(update);
}
bool UpdateFetcher::PathCompare::operator()(LocaleFileEntry const& left, LocaleFileEntry const& right) const
{
return left.first.filename().string() < right.first.filename().string();
}

View file

@ -0,0 +1,131 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2021+ WarheadCore <https://github.com/WarheadCore>
*/
#ifndef UpdateFetcher_h__
#define UpdateFetcher_h__
#include "DatabaseEnv.h"
#include "Define.h"
#include <set>
#include <string>
#include <unordered_map>
#include <vector>
namespace boost::filesystem
{
class path;
}
struct AC_DATABASE_API UpdateResult
{
UpdateResult()
: updated(0), recent(0), archived(0) { }
UpdateResult(size_t const updated_, size_t const recent_, size_t const archived_)
: updated(updated_), recent(recent_), archived(archived_) { }
size_t updated;
size_t recent;
size_t archived;
};
class AC_DATABASE_API UpdateFetcher
{
typedef boost::filesystem::path Path;
public:
UpdateFetcher(Path const& updateDirectory,
std::function<void(std::string const&)> const& apply,
std::function<void(Path const& path)> const& applyFile,
std::function<QueryResult(std::string const&)> const& retrieve, std::string const& dbModuleName);
~UpdateFetcher();
UpdateResult Update(bool const redundancyChecks, bool const allowRehash,
bool const archivedRedundancy, int32 const cleanDeadReferencesMaxCount) const;
private:
enum UpdateMode
{
MODE_APPLY,
MODE_REHASH
};
enum State
{
RELEASED,
ARCHIVED
};
struct AppliedFileEntry
{
AppliedFileEntry(std::string const& name_, std::string const& hash_, State state_, uint64 timestamp_)
: name(name_), hash(hash_), state(state_), timestamp(timestamp_) { }
std::string const name;
std::string const hash;
State const state;
uint64 const timestamp;
static inline State StateConvert(std::string const& state)
{
return (state == "RELEASED") ? RELEASED : ARCHIVED;
}
static inline std::string StateConvert(State const state)
{
return (state == RELEASED) ? "RELEASED" : "ARCHIVED";
}
std::string GetStateAsString() const
{
return StateConvert(state);
}
};
struct DirectoryEntry;
typedef std::pair<Path, State> LocaleFileEntry;
struct PathCompare
{
bool operator()(LocaleFileEntry const& left, LocaleFileEntry const& right) const;
};
typedef std::set<LocaleFileEntry, PathCompare> LocaleFileStorage;
typedef std::unordered_map<std::string, std::string> HashToFileNameStorage;
typedef std::unordered_map<std::string, AppliedFileEntry> AppliedFileStorage;
typedef std::vector<UpdateFetcher::DirectoryEntry> DirectoryStorage;
LocaleFileStorage GetFileList() const;
void FillFileListRecursively(Path const& path, LocaleFileStorage& storage,
State const state, uint32 const depth) const;
DirectoryStorage ReceiveIncludedDirectories() const;
AppliedFileStorage ReceiveAppliedFiles() const;
std::string ReadSQLUpdate(Path const& file) const;
uint32 Apply(Path const& path) const;
void UpdateEntry(AppliedFileEntry const& entry, uint32 const speed = 0) const;
void RenameEntry(std::string const& from, std::string const& to) const;
void CleanUp(AppliedFileStorage const& storage) const;
void UpdateState(std::string const& name, State const state) const;
std::unique_ptr<Path> const _sourceDirectory;
std::function<void(std::string const&)> const _apply;
std::function<void(Path const& path)> const _applyFile;
std::function<QueryResult(std::string const&)> const _retrieve;
// modules
std::string const _dbModuleName;
};
#endif // UpdateFetcher_h__

View file

@ -39,7 +39,7 @@ void SmartWaypointMgr::LoadFromDB()
waypoint_map.clear();
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMARTAI_WP);
PreparedQueryResult result = WorldDatabase.Query(stmt);
if (!result)
@ -109,7 +109,7 @@ void SmartAIMgr::LoadSmartAIFromDB()
for (uint8 i = 0; i < SMART_SCRIPT_TYPE_MAX; i++)
mEventMap[i].clear(); //Drop Existing SmartAI List
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMART_SCRIPTS);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_SMART_SCRIPTS);
PreparedQueryResult result = WorldDatabase.Query(stmt);
if (!result)

View file

@ -31,7 +31,7 @@ namespace AccountMgr
if (GetId(username))
return AOR_NAME_ALREADY_EXIST; // username does already exist
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT);
stmt->setString(0, username);
auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(username, password);
@ -51,16 +51,15 @@ namespace AccountMgr
AccountOpResult DeleteAccount(uint32 accountId)
{
// Check if accounts exists
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID);
stmt->setUInt32(0, accountId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
LoginDatabasePreparedStatement* loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID);
loginStmt->setUInt32(0, accountId);
PreparedQueryResult result = LoginDatabase.Query(loginStmt);
if (!result)
return AOR_NAME_NOT_EXIST;
// Obtain accounts characters
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID);
stmt->setUInt32(0, accountId);
result = CharacterDatabase.Query(stmt);
@ -96,27 +95,27 @@ namespace AccountMgr
stmt->setUInt32(0, accountId);
CharacterDatabase.Execute(stmt);
SQLTransaction trans = LoginDatabase.BeginTransaction();
LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT);
stmt->setUInt32(0, accountId);
trans->Append(stmt);
loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT);
loginStmt->setUInt32(0, accountId);
trans->Append(loginStmt);
stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS);
stmt->setUInt32(0, accountId);
trans->Append(stmt);
loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS);
loginStmt->setUInt32(0, accountId);
trans->Append(loginStmt);
stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS);
stmt->setUInt32(0, accountId);
trans->Append(stmt);
loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS);
loginStmt->setUInt32(0, accountId);
trans->Append(loginStmt);
stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_BANNED);
stmt->setUInt32(0, accountId);
trans->Append(stmt);
loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_BANNED);
loginStmt->setUInt32(0, accountId);
trans->Append(loginStmt);
stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_MUTED);
stmt->setUInt32(0, accountId);
trans->Append(stmt);
loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_MUTED);
loginStmt->setUInt32(0, accountId);
trans->Append(loginStmt);
LoginDatabase.CommitTransaction(trans);
@ -126,7 +125,7 @@ namespace AccountMgr
AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword)
{
// Check if accounts exists
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID);
stmt->setUInt32(0, accountId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
@ -178,7 +177,7 @@ namespace AccountMgr
auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(username, newPassword);
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON);
stmt->setBinary(0, salt);
stmt->setBinary(1, verifier);
stmt->setUInt32(2, accountId);
@ -190,7 +189,7 @@ namespace AccountMgr
uint32 GetId(std::string const& username)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME);
stmt->setString(0, username);
PreparedQueryResult result = LoginDatabase.Query(stmt);
@ -199,7 +198,7 @@ namespace AccountMgr
uint32 GetSecurity(uint32 accountId)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL);
stmt->setUInt32(0, accountId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
@ -208,7 +207,7 @@ namespace AccountMgr
uint32 GetSecurity(uint32 accountId, int32 realmId)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID);
stmt->setUInt32(0, accountId);
stmt->setInt32(1, realmId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
@ -218,7 +217,7 @@ namespace AccountMgr
bool GetName(uint32 accountId, std::string& name)
{
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_USERNAME_BY_ID);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_USERNAME_BY_ID);
stmt->setUInt32(0, accountId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
@ -241,7 +240,7 @@ namespace AccountMgr
Utf8ToUpperOnlyLatin(username);
Utf8ToUpperOnlyLatin(password);
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD);
stmt->setUInt32(0, accountId);
if (PreparedQueryResult result = LoginDatabase.Query(stmt))
{
@ -257,7 +256,7 @@ namespace AccountMgr
uint32 GetCharactersCount(uint32 accountId)
{
// check character count
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS);
stmt->setUInt32(0, accountId);
PreparedQueryResult result = CharacterDatabase.Query(stmt);

View file

@ -521,9 +521,9 @@ void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaCondition condi
void AchievementMgr::DeleteFromDB(ObjectGuid::LowType lowguid)
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT);
stmt->setUInt32(0, lowguid);
trans->Append(stmt);
@ -534,7 +534,7 @@ void AchievementMgr::DeleteFromDB(ObjectGuid::LowType lowguid)
CharacterDatabase.CommitTransaction(trans);
}
void AchievementMgr::SaveToDB(SQLTransaction& trans)
void AchievementMgr::SaveToDB(CharacterDatabaseTransaction trans)
{
if (!m_completedAchievements.empty())
{
@ -543,7 +543,7 @@ void AchievementMgr::SaveToDB(SQLTransaction& trans)
if (!iter->second.changed)
continue;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT);
stmt->setUInt16(0, iter->first);
stmt->setUInt32(1, GetPlayer()->GetGUID().GetCounter());
trans->Append(stmt);
@ -567,7 +567,7 @@ void AchievementMgr::SaveToDB(SQLTransaction& trans)
if (!iter->second.changed)
continue;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS_BY_CRITERIA);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS_BY_CRITERIA);
stmt->setUInt32(0, GetPlayer()->GetGUID().GetCounter());
stmt->setUInt16(1, iter->first);
trans->Append(stmt);
@ -632,7 +632,7 @@ void AchievementMgr::LoadFromDB(PreparedQueryResult achievementResult, PreparedQ
// we will remove not existed criteria for all characters
LOG_ERROR("achievement", "Non-existing achievement criteria %u data removed from table `character_achievement_progress`.", id);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA);
stmt->setUInt16(0, uint16(id));
@ -2264,7 +2264,7 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
draft = MailDraft(subject, text);
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
Item* item = reward->itemId ? Item::CreateItem(reward->itemId, 1, GetPlayer()) : nullptr;
if (item)
@ -2788,7 +2788,8 @@ void AchievementGlobalMgr::LoadCompletedAchievements()
// Remove non existent achievements from all characters
LOG_ERROR("achievement", "Non-existing achievement %u data removed from table `character_achievement`.", achievementId);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEVMENT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEVMENT);
stmt->setUInt16(0, uint16(achievementId));
CharacterDatabase.Execute(stmt);

View file

@ -261,7 +261,7 @@ public:
void Reset();
static void DeleteFromDB(ObjectGuid::LowType lowguid);
void LoadFromDB(PreparedQueryResult achievementResult, PreparedQueryResult criteriaResult);
void SaveToDB(SQLTransaction& trans);
void SaveToDB(CharacterDatabaseTransaction trans);
void ResetAchievementCriteria(AchievementCriteriaCondition condition, uint32 value, bool evenIfCriteriaComplete = false);
void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscValue1 = 0, uint32 miscValue2 = 0, Unit* unit = nullptr);
void CompletedAchievement(AchievementEntry const* entry);

View file

@ -90,7 +90,7 @@ namespace AddonMgr
{
std::string name = addon.Name;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ADDON);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ADDON);
stmt->setString(0, name);
stmt->setUInt32(1, addon.CRC);

View file

@ -101,7 +101,7 @@ uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32
}
//does not clear ram
void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, SQLTransaction& trans, bool sendNotification, bool updateAchievementCriteria, bool sendMail)
void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification, bool updateAchievementCriteria, bool sendMail)
{
Item* pItem = GetAItem(auction->item_guid);
if (!pItem)
@ -120,7 +120,7 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, SQLTransaction&
sScriptMgr->OnBeforeAuctionHouseMgrSendAuctionWonMail(this, auction, bidder, bidder_accId, sendNotification, updateAchievementCriteria, sendMail);
// set owner to bidder (to prevent delete item with sender char deleting)
// owner in `data` will set at mail receive and item extracting
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_OWNER);
stmt->setUInt32(0, auction->bidder.GetCounter());
stmt->setUInt32(1, pItem->GetGUID().GetCounter());
trans->Append(stmt);
@ -143,7 +143,7 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry* auction, SQLTransaction&
sAuctionMgr->RemoveAItem(auction->item_guid, true, &trans);
}
void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, SQLTransaction& trans, bool sendMail)
void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail)
{
Player* owner = ObjectAccessor::FindConnectedPlayer(auction->owner);
uint32 owner_accId = sObjectMgr->GetPlayerAccountIdByGUID(auction->owner.GetCounter());
@ -158,7 +158,7 @@ void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry* auction, SQLTrans
}
//call this method to send mail to auction owner, when auction is successful, it does not clear ram
void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, SQLTransaction& trans, bool sendNotification, bool updateAchievementCriteria, bool sendMail)
void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification, bool updateAchievementCriteria, bool sendMail)
{
Player* owner = ObjectAccessor::FindConnectedPlayer(auction->owner);
uint32 owner_accId = sObjectMgr->GetPlayerAccountIdByGUID(auction->owner.GetCounter());
@ -203,7 +203,7 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry* auction, SQLTransa
}
//does not clear ram
void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, SQLTransaction& trans, bool sendNotification, bool sendMail)
void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification, bool sendMail)
{
//return an item in auction to its owner by mail
Item* pItem = GetAItem(auction->item_guid);
@ -231,7 +231,7 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry* auction, SQLTransacti
}
//this function sends mail to old bidder
void AuctionHouseMgr::SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 newPrice, Player* newBidder, SQLTransaction& trans, bool sendNotification, bool sendMail)
void AuctionHouseMgr::SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 newPrice, Player* newBidder, CharacterDatabaseTransaction trans, bool sendNotification, bool sendMail)
{
Player* oldBidder = ObjectAccessor::FindConnectedPlayer(auction->bidder);
@ -255,7 +255,7 @@ void AuctionHouseMgr::SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 new
}
//this function sends mail, when auction is cancelled to old bidder
void AuctionHouseMgr::SendAuctionCancelledToBidderMail(AuctionEntry* auction, SQLTransaction& trans, bool sendMail)
void AuctionHouseMgr::SendAuctionCancelledToBidderMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail)
{
Player* bidder = ObjectAccessor::FindConnectedPlayer(auction->bidder);
@ -288,7 +288,7 @@ void AuctionHouseMgr::LoadAuctionItems()
}
// data needs to be at first place for Item::LoadFromDB
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_AUCTION_ITEMS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_AUCTION_ITEMS);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
@ -333,7 +333,7 @@ void AuctionHouseMgr::LoadAuctions()
{
uint32 oldMSTime = getMSTime();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_AUCTIONS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_AUCTIONS);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
@ -345,7 +345,7 @@ void AuctionHouseMgr::LoadAuctions()
uint32 count = 0;
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
do
{
Field* fields = result->Fetch();
@ -375,7 +375,7 @@ void AuctionHouseMgr::AddAItem(Item* it)
mAitems[it->GetGUID()] = it;
}
bool AuctionHouseMgr::RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB, SQLTransaction* trans /*= nullptr*/)
bool AuctionHouseMgr::RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB, CharacterDatabaseTransaction* trans /*= nullptr*/)
{
ItemMap::iterator i = mAitems.find(itemGuid);
if (i == mAitems.end())
@ -458,7 +458,7 @@ void AuctionHouseObject::Update()
if (AuctionsMap.empty())
return;
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
for (AuctionEntryMap::iterator itr, iter = AuctionsMap.begin(); iter != AuctionsMap.end(); )
{
@ -715,16 +715,16 @@ uint32 AuctionEntry::GetAuctionOutBid() const
return outbid ? outbid : 1;
}
void AuctionEntry::DeleteFromDB(SQLTransaction& trans) const
void AuctionEntry::DeleteFromDB(CharacterDatabaseTransaction trans) const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_AUCTION);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_AUCTION);
stmt->setUInt32(0, Id);
trans->Append(stmt);
}
void AuctionEntry::SaveToDB(SQLTransaction& trans) const
void AuctionEntry::SaveToDB(CharacterDatabaseTransaction trans) const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AUCTION);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AUCTION);
stmt->setUInt32(0, Id);
stmt->setUInt8(1, houseId);
stmt->setUInt32(2, item_guid.GetCounter());

View file

@ -79,8 +79,8 @@ struct AuctionEntry
[[nodiscard]] uint32 GetAuctionCut() const;
[[nodiscard]] uint32 GetAuctionOutBid() const;
bool BuildAuctionInfo(WorldPacket& data) const;
void DeleteFromDB(SQLTransaction& trans) const;
void SaveToDB(SQLTransaction& trans) const;
void DeleteFromDB(CharacterDatabaseTransaction trans) const;
void SaveToDB(CharacterDatabaseTransaction trans) const;
bool LoadFromDB(Field* fields);
[[nodiscard]] std::string BuildAuctionMailSubject(MailAuctionAnswers response) const;
static std::string BuildAuctionMailBody(ObjectGuid guid, uint32 bid, uint32 buyout, uint32 deposit, uint32 cut);
@ -102,8 +102,9 @@ public:
[[nodiscard]] uint32 Getcount() const { return AuctionsMap.size(); }
AuctionEntryMap::iterator GetAuctionsBegin() {return AuctionsMap.begin();}
AuctionEntryMap::iterator GetAuctionsEnd() {return AuctionsMap.end();}
AuctionEntryMap::iterator GetAuctionsBegin() { return AuctionsMap.begin(); }
AuctionEntryMap::iterator GetAuctionsEnd() { return AuctionsMap.end(); }
AuctionEntryMap const& GetAuctions() { return AuctionsMap; }
[[nodiscard]] AuctionEntry* GetAuction(uint32 id) const
{
@ -156,12 +157,12 @@ public:
}
//auction messages
void SendAuctionWonMail(AuctionEntry* auction, SQLTransaction& trans, bool sendNotification = true, bool updateAchievementCriteria = true, bool sendMail = true);
void SendAuctionSalePendingMail(AuctionEntry* auction, SQLTransaction& trans, bool sendMail = true);
void SendAuctionSuccessfulMail(AuctionEntry* auction, SQLTransaction& trans, bool sendNotification = true, bool updateAchievementCriteria = true, bool sendMail = true);
void SendAuctionExpiredMail(AuctionEntry* auction, SQLTransaction& trans, bool sendNotification = true, bool sendMail = true);
void SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 newPrice, Player* newBidder, SQLTransaction& trans, bool sendNotification = true, bool sendMail = true);
void SendAuctionCancelledToBidderMail(AuctionEntry* auction, SQLTransaction& trans, bool sendMail = true);
void SendAuctionWonMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification = true, bool updateAchievementCriteria = true, bool sendMail = true);
void SendAuctionSalePendingMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail = true);
void SendAuctionSuccessfulMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification = true, bool updateAchievementCriteria = true, bool sendMail = true);
void SendAuctionExpiredMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendNotification = true, bool sendMail = true);
void SendAuctionOutbiddedMail(AuctionEntry* auction, uint32 newPrice, Player* newBidder, CharacterDatabaseTransaction trans, bool sendNotification = true, bool sendMail = true);
void SendAuctionCancelledToBidderMail(AuctionEntry* auction, CharacterDatabaseTransaction trans, bool sendMail = true);
static uint32 GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item* pItem, uint32 count);
static AuctionHouseEntry const* GetAuctionHouseEntry(uint32 factionTemplateId);
@ -173,7 +174,7 @@ public:
void LoadAuctions();
void AddAItem(Item* it);
bool RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB = false, SQLTransaction* trans = nullptr);
bool RemoveAItem(ObjectGuid itemGuid, bool deleteFromDB = false, CharacterDatabaseTransaction* trans = nullptr);
void Update();

View file

@ -9,6 +9,7 @@
#include "Battlefield.h"
#include "ObjectAccessor.h"
#include "Log.h"
#include "World.h"
#include "WorldPacket.h"

View file

@ -56,7 +56,7 @@ bool ArenaTeam::Create(ObjectGuid captainGuid, uint8 type, std::string const& te
BorderColor = borderColor;
// Save arena team to db
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_TEAM);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_TEAM);
stmt->setUInt32(0, TeamId);
stmt->setString(1, TeamName);
stmt->setUInt32(2, captainGuid.GetCounter());
@ -120,7 +120,7 @@ bool ArenaTeam::AddMember(ObjectGuid playerGuid)
// xinef: zomg! sync query
// Try to get player's match maker rating from db and fall back to config setting if not found
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MATCH_MAKER_RATING);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MATCH_MAKER_RATING);
stmt->setUInt32(0, playerGuid.GetCounter());
stmt->setUInt8(1, GetSlot());
PreparedQueryResult result = CharacterDatabase.Query(stmt);
@ -271,7 +271,7 @@ bool ArenaTeam::SetName(std::string const& name)
return false;
TeamName = name;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_NAME);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_NAME);
stmt->setString(0, TeamName);
stmt->setUInt32(1, GetId());
CharacterDatabase.Execute(stmt);
@ -289,7 +289,7 @@ void ArenaTeam::SetCaptain(ObjectGuid guid)
CaptainGuid = guid;
// Update database
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_CAPTAIN);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_CAPTAIN);
stmt->setUInt32(0, guid.GetCounter());
stmt->setUInt32(1, GetId());
CharacterDatabase.Execute(stmt);
@ -359,7 +359,7 @@ void ArenaTeam::DelMember(ObjectGuid guid, bool cleanDb)
// Only used for single member deletion, for arena team disband we use a single query for more efficiency
if (cleanDb)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM_MEMBER);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM_MEMBER);
stmt->setUInt32(0, GetId());
stmt->setUInt32(1, guid.GetCounter());
CharacterDatabase.Execute(stmt);
@ -379,9 +379,9 @@ void ArenaTeam::Disband(WorldSession* session)
}
// Update database
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM);
stmt->setUInt32(0, TeamId);
trans->Append(stmt);
@ -402,9 +402,9 @@ void ArenaTeam::Disband()
DelMember(Members.front().Guid, false);
// Update database
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ARENA_TEAM);
stmt->setUInt32(0, TeamId);
trans->Append(stmt);
@ -921,9 +921,9 @@ void ArenaTeam::SaveToDB()
// Save team and member stats to db
// Called after a match has ended or when calculating arena_points
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_STATS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ARENA_TEAM_STATS);
stmt->setUInt16(0, Stats.Rating);
stmt->setUInt16(1, Stats.WeekGames);
stmt->setUInt16(2, Stats.WeekWins);

View file

@ -198,16 +198,16 @@ void ArenaTeamMgr::DistributeArenaPoints()
sScriptMgr->OnBeforeUpdateArenaPoints(at, PlayerPoints);
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt;
CharacterDatabasePreparedStatement* stmt;
// Cycle that gives points to all players
for (std::map<ObjectGuid, uint32>::iterator playerItr = PlayerPoints.begin(); playerItr != PlayerPoints.end(); ++playerItr)
{
// Add points to player if online
if (Player* player = ObjectAccessor::FindPlayer(playerItr->first))
player->ModifyArenaPoints(playerItr->second, &trans);
player->ModifyArenaPoints(playerItr->second, trans);
else // Update database
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_ARENA_POINTS);

View file

@ -768,7 +768,7 @@ void Battleground::EndBattleground(TeamId winnerTeamId)
else
SetWinner(TEAM_NEUTRAL);
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
uint64 battlegroundId = 1;
if (isBattleground() && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_STORE_STATISTICS_ENABLE))
{
@ -819,8 +819,8 @@ void Battleground::EndBattleground(TeamId winnerTeamId)
uint32 fightId = sArenaTeamMgr->GetNextArenaLogId();
uint32 currOnline = (uint32)(sWorld->GetActiveSessionCount());
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_LOG_FIGHT);
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_LOG_FIGHT);
stmt2->setUInt32(0, fightId);
stmt2->setUInt8(1, m_ArenaType);
stmt2->setUInt32(2, ((GetStartTime() <= startDelay ? 0 : GetStartTime() - startDelay) / 1000));
@ -878,8 +878,8 @@ void Battleground::EndBattleground(TeamId winnerTeamId)
uint32 fightId = sArenaTeamMgr->GetNextArenaLogId();
uint32 currOnline = (uint32)(sWorld->GetActiveSessionCount());
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt3 = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_LOG_FIGHT);
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt3 = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_LOG_FIGHT);
stmt3->setUInt32(0, fightId);
stmt3->setUInt8(1, m_ArenaType);
stmt3->setUInt32(2, ((GetStartTime() <= startDelay ? 0 : GetStartTime() - startDelay) / 1000));

View file

@ -1165,7 +1165,7 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS))
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK);
stmt->setUInt32(0, player->GetGUID().GetCounter());
stmt->setUInt8(1, BG_DESERTION_TYPE_NO_ENTER_BUTTON);
CharacterDatabase.Execute(stmt);

View file

@ -121,13 +121,7 @@ void CalendarMgr::AddEvent(CalendarEvent* calendarEvent, CalendarSendEventType s
SendCalendarEvent(calendarEvent->GetCreatorGUID(), *calendarEvent, sendType);
}
void CalendarMgr::AddInvite(CalendarEvent* calendarEvent, CalendarInvite* invite)
{
SQLTransaction dummy;
AddInvite(calendarEvent, invite, dummy);
}
void CalendarMgr::AddInvite(CalendarEvent* calendarEvent, CalendarInvite* invite, SQLTransaction& trans)
void CalendarMgr::AddInvite(CalendarEvent* calendarEvent, CalendarInvite* invite, CharacterDatabaseTransaction trans)
{
if (!calendarEvent->IsGuildAnnouncement())
SendCalendarEventInvite(*invite);
@ -165,8 +159,8 @@ void CalendarMgr::RemoveEvent(CalendarEvent* calendarEvent, ObjectGuid remover)
SendCalendarEventRemovedAlert(*calendarEvent);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt;
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt;
MailDraft mail(calendarEvent->BuildCalendarMailSubject(remover), calendarEvent->BuildCalendarMailBody());
CalendarInviteStore& eventInvites = _invites[calendarEvent->GetEventId()];
@ -212,8 +206,8 @@ void CalendarMgr::RemoveInvite(uint64 inviteId, uint64 eventId, ObjectGuid /*rem
if (itr == _invites[eventId].end())
return;
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CALENDAR_INVITE);
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CALENDAR_INVITE);
stmt->setUInt64(0, (*itr)->GetInviteId());
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);
@ -234,7 +228,7 @@ void CalendarMgr::RemoveInvite(uint64 inviteId, uint64 eventId, ObjectGuid /*rem
void CalendarMgr::UpdateEvent(CalendarEvent* calendarEvent)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CALENDAR_EVENT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CALENDAR_EVENT);
stmt->setUInt64(0, calendarEvent->GetEventId());
stmt->setUInt32(1, calendarEvent->GetCreatorGUID().GetCounter());
stmt->setString(2, calendarEvent->GetTitle());
@ -247,15 +241,9 @@ void CalendarMgr::UpdateEvent(CalendarEvent* calendarEvent)
CharacterDatabase.Execute(stmt);
}
void CalendarMgr::UpdateInvite(CalendarInvite* invite)
void CalendarMgr::UpdateInvite(CalendarInvite* invite, CharacterDatabaseTransaction trans)
{
SQLTransaction dummy;
UpdateInvite(invite, dummy);
}
void CalendarMgr::UpdateInvite(CalendarInvite* invite, SQLTransaction& trans)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CALENDAR_INVITE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CALENDAR_INVITE);
stmt->setUInt64(0, invite->GetInviteId());
stmt->setUInt64(1, invite->GetEventId());
stmt->setUInt32(2, invite->GetInviteeGUID().GetCounter());

View file

@ -307,11 +307,10 @@ public:
void RemoveEvent(CalendarEvent* calendarEvent, ObjectGuid remover);
void UpdateEvent(CalendarEvent* calendarEvent);
void AddInvite(CalendarEvent* calendarEvent, CalendarInvite* invite);
void AddInvite(CalendarEvent* calendarEvent, CalendarInvite* invite, SQLTransaction& trans);
void AddInvite(CalendarEvent* calendarEvent, CalendarInvite* invite, CharacterDatabaseTransaction trans = nullptr);
void RemoveInvite(uint64 inviteId, uint64 eventId, ObjectGuid remover);
void UpdateInvite(CalendarInvite* invite);
void UpdateInvite(CalendarInvite* invite, SQLTransaction& trans);
void RemoveInvite(uint64 inviteId, uint64 eventId, uint64 remover);
void UpdateInvite(CalendarInvite* invite, CharacterDatabaseTransaction trans = nullptr);
void RemoveAllPlayerEventsAndInvites(ObjectGuid guid);
void RemovePlayerGuildEventsAndSignups(ObjectGuid guid, uint32 guildId);

View file

@ -70,7 +70,7 @@ Channel::Channel(std::string const& name, uint32 channelId, uint32 channelDBId,
{
_channelDBId = ++ChannelMgr::_channelIdMax;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHANNEL);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHANNEL);
stmt->setUInt32(0, _channelDBId);
stmt->setString(1, name);
stmt->setUInt32(2, _teamId);
@ -90,7 +90,7 @@ void Channel::UpdateChannelInDB() const
{
if (_IsSaved)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL);
stmt->setBool(0, _announce);
stmt->setString(1, _password);
stmt->setUInt32(2, _channelDBId);
@ -102,14 +102,14 @@ void Channel::UpdateChannelInDB() const
void Channel::UpdateChannelUseageInDB() const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL_USAGE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL_USAGE);
stmt->setUInt32(0, _channelDBId);
CharacterDatabase.Execute(stmt);
}
void Channel::AddChannelBanToDB(ObjectGuid guid, uint32 time) const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHANNEL_BAN);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHANNEL_BAN);
stmt->setUInt32(0, _channelDBId);
stmt->setUInt32(1, guid.GetCounter());
stmt->setUInt32(2, time);
@ -118,7 +118,7 @@ void Channel::AddChannelBanToDB(ObjectGuid guid, uint32 time) const
void Channel::RemoveChannelBanFromDB(ObjectGuid guid) const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHANNEL_BAN);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHANNEL_BAN);
stmt->setUInt32(0, _channelDBId);
stmt->setUInt32(1, guid.GetCounter());
CharacterDatabase.Execute(stmt);
@ -128,9 +128,9 @@ void Channel::CleanOldChannelsInDB()
{
if (sWorld->getIntConfig(CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION) > 0)
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_OLD_CHANNELS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_OLD_CHANNELS);
stmt->setUInt32(0, sWorld->getIntConfig(CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION) * DAY);
trans->Append(stmt);

View file

@ -94,7 +94,7 @@ void ChannelMgr::LoadChannels()
for (auto pair : toDelete)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHANNEL);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHANNEL);
stmt->setString(0, pair.first);
stmt->setUInt32(1, pair.second);
CharacterDatabase.Execute(stmt);

View file

@ -41,7 +41,7 @@ std::vector<ChatCommand> const& ChatHandler::getCommandTable()
std::vector<ChatCommand> cmds = sScriptMgr->GetChatCommands();
commandTableCache.swap(cmds);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_COMMANDS);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_COMMANDS);
PreparedQueryResult result = WorldDatabase.Query(stmt);
if (result)
{

View file

@ -85,7 +85,7 @@ namespace lfg
if (!guid.IsGroup())
return;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_LFG_DATA);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_LFG_DATA);
stmt->setUInt32(0, guid.GetCounter());
stmt->setUInt32(1, GetDungeon(guid));
stmt->setUInt32(2, GetState(guid));

View file

@ -81,10 +81,10 @@ bool Corpse::Create(ObjectGuid::LowType guidlow, Player* owner)
void Corpse::SaveToDB()
{
// prevent DB data inconsistence problems and duplicates
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
DeleteFromDB(trans);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CORPSE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CORPSE);
stmt->setUInt32(0, GetOwnerGUID().GetCounter()); // guid
stmt->setFloat (1, GetPositionX()); // posX
stmt->setFloat (2, GetPositionY()); // posY
@ -107,14 +107,14 @@ void Corpse::SaveToDB()
CharacterDatabase.CommitTransaction(trans);
}
void Corpse::DeleteFromDB(SQLTransaction& trans)
void Corpse::DeleteFromDB(CharacterDatabaseTransaction trans)
{
DeleteFromDB(GetOwnerGUID(), trans);
}
void Corpse::DeleteFromDB(ObjectGuid const ownerGuid, SQLTransaction& trans)
void Corpse::DeleteFromDB(ObjectGuid const ownerGuid, CharacterDatabaseTransaction trans)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSE);
stmt->setUInt32(0, ownerGuid.GetCounter());
CharacterDatabase.ExecuteOrAppend(trans, stmt);
}

View file

@ -49,8 +49,8 @@ public:
void SaveToDB();
bool LoadCorpseFromDB(ObjectGuid::LowType guid, Field* fields);
void DeleteFromDB(SQLTransaction& trans);
static void DeleteFromDB(ObjectGuid const ownerGuid, SQLTransaction& trans);
void DeleteFromDB(CharacterDatabaseTransaction trans);
static void DeleteFromDB(ObjectGuid const ownerGuid, CharacterDatabaseTransaction trans);
[[nodiscard]] ObjectGuid GetOwnerGUID() const { return GetGuidValue(CORPSE_FIELD_OWNER); }

View file

@ -1254,9 +1254,9 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
data.dynamicflags = dynamicflags;
// update in DB
SQLTransaction trans = WorldDatabase.BeginTransaction();
WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE);
stmt->setUInt32(0, m_spawnId);
trans->Append(stmt);
@ -1635,9 +1635,9 @@ void Creature::DeleteFromDB()
GetMap()->RemoveCreatureRespawnTime(m_spawnId);
sObjectMgr->DeleteCreatureData(m_spawnId);
SQLTransaction trans = WorldDatabase.BeginTransaction();
WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE);
stmt->setUInt32(0, m_spawnId);
trans->Append(stmt);

View file

@ -885,11 +885,11 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask, bool
data.artKit = GetGoArtKit();
// Update in DB
SQLTransaction trans = WorldDatabase.BeginTransaction();
WorldDatabaseTransaction trans = WorldDatabase.BeginTransaction();
uint8 index = 0;
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
stmt->setUInt32(0, m_spawnId);
trans->Append(stmt);
@ -993,7 +993,7 @@ void GameObject::DeleteFromDB()
GetMap()->RemoveGORespawnTime(m_spawnId);
sObjectMgr->DeleteGOData(m_spawnId);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAMEOBJECT);
stmt->setUInt32(0, m_spawnId);
WorldDatabase.Execute(stmt);

View file

@ -87,7 +87,7 @@ bool Bag::Create(ObjectGuid::LowType guidlow, uint32 itemid, Player const* owner
return true;
}
void Bag::SaveToDB(SQLTransaction& trans)
void Bag::SaveToDB(CharacterDatabaseTransaction trans)
{
Item::SaveToDB(trans);
}
@ -110,7 +110,7 @@ bool Bag::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fie
return true;
}
void Bag::DeleteFromDB(SQLTransaction& trans)
void Bag::DeleteFromDB(CharacterDatabaseTransaction trans)
{
for (uint8 i = 0; i < MAX_BAG_SIZE; ++i)
if (m_bagslot[i])

View file

@ -39,11 +39,11 @@ public:
// DB operations
// overwrite virtual Item::SaveToDB
void SaveToDB(SQLTransaction& trans) override;
void SaveToDB(CharacterDatabaseTransaction trans) override;
// overwrite virtual Item::LoadFromDB
bool LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fields, uint32 entry) override;
// overwrite virtual Item::DeleteFromDB
void DeleteFromDB(SQLTransaction& trans) override;
void DeleteFromDB(CharacterDatabaseTransaction trans) override;
void BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const override;

View file

@ -300,7 +300,7 @@ void Item::UpdateDuration(Player* owner, uint32 diff)
SetState(ITEM_CHANGED, owner); // save new time in database
}
void Item::SaveToDB(SQLTransaction& trans)
void Item::SaveToDB(CharacterDatabaseTransaction trans)
{
bool isInTransaction = static_cast<bool>(trans);
if (!isInTransaction)
@ -313,7 +313,7 @@ void Item::SaveToDB(SQLTransaction& trans)
case ITEM_CHANGED:
{
uint8 index = 0;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(uState == ITEM_NEW ? CHAR_REP_ITEM_INSTANCE : CHAR_UPD_ITEM_INSTANCE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(uState == ITEM_NEW ? CHAR_REP_ITEM_INSTANCE : CHAR_UPD_ITEM_INSTANCE);
stmt->setUInt32( index, GetEntry());
stmt->setUInt32(++index, GetOwnerGUID().GetCounter());
stmt->setUInt32(++index, GetGuidValue(ITEM_FIELD_CREATOR).GetCounter());
@ -356,7 +356,7 @@ void Item::SaveToDB(SQLTransaction& trans)
}
case ITEM_REMOVED:
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
stmt->setUInt32(0, guid);
trans->Append(stmt);
@ -455,7 +455,7 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi
if (need_save) // normal item changed state set not work at loading
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD);
stmt->setUInt32(0, GetUInt32Value(ITEM_FIELD_DURATION));
stmt->setUInt32(1, GetUInt32Value(ITEM_FIELD_FLAGS));
stmt->setUInt32(2, GetUInt32Value(ITEM_FIELD_DURABILITY));
@ -467,28 +467,28 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi
}
/*static*/
void Item::DeleteFromDB(SQLTransaction& trans, ObjectGuid::LowType itemGuid)
void Item::DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
{
sScriptMgr->OnGlobalItemDelFromDB(trans, itemGuid);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
stmt->setUInt32(0, itemGuid);
trans->Append(stmt);
}
void Item::DeleteFromDB(SQLTransaction& trans)
void Item::DeleteFromDB(CharacterDatabaseTransaction trans)
{
DeleteFromDB(trans, GetGUID().GetCounter());
}
/*static*/
void Item::DeleteFromInventoryDB(SQLTransaction& trans, ObjectGuid::LowType itemGuid)
void Item::DeleteFromInventoryDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM);
stmt->setUInt32(0, itemGuid);
trans->Append(stmt);
}
void Item::DeleteFromInventoryDB(SQLTransaction& trans)
void Item::DeleteFromInventoryDB(CharacterDatabaseTransaction trans)
{
DeleteFromInventoryDB(trans, GetGUID().GetCounter());
}
@ -1127,9 +1127,9 @@ void Item::RemoveFromObjectUpdate()
void Item::SaveRefundDataToDB()
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
stmt->setUInt32(0, GetGUID().GetCounter());
trans->Append(stmt);
@ -1143,17 +1143,17 @@ void Item::SaveRefundDataToDB()
CharacterDatabase.CommitTransaction(trans);
}
void Item::DeleteRefundDataFromDB(SQLTransaction* trans)
void Item::DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans)
{
if (trans)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_REFUND_INSTANCE);
stmt->setUInt32(0, GetGUID().GetCounter());
(*trans)->Append(stmt);
}
}
void Item::SetNotRefundable(Player* owner, bool changestate /*=true*/, SQLTransaction* trans /*=nullptr*/)
void Item::SetNotRefundable(Player* owner, bool changestate /*=true*/, CharacterDatabaseTransaction* trans /*=nullptr*/)
{
if (!HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE))
return;
@ -1225,7 +1225,7 @@ void Item::ClearSoulboundTradeable(Player* currentOwner)
allowedGUIDs.clear();
SetState(ITEM_CHANGED, currentOwner);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_BOP_TRADE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_BOP_TRADE);
stmt->setUInt32(0, GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
}

View file

@ -218,14 +218,14 @@ public:
bool IsBindedNotWith(Player const* player) const;
[[nodiscard]] bool IsBoundByEnchant() const;
[[nodiscard]] bool IsBoundByTempEnchant() const;
virtual void SaveToDB(SQLTransaction& trans);
virtual void SaveToDB(CharacterDatabaseTransaction trans);
virtual bool LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fields, uint32 entry);
static void DeleteFromDB(SQLTransaction& trans, ObjectGuid::LowType itemGuid);
virtual void DeleteFromDB(SQLTransaction& trans);
static void DeleteFromInventoryDB(SQLTransaction& trans, ObjectGuid::LowType itemGuid);
void DeleteFromInventoryDB(SQLTransaction& trans);
static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid);
virtual void DeleteFromDB(CharacterDatabaseTransaction trans);
static void DeleteFromInventoryDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid);
void DeleteFromInventoryDB(CharacterDatabaseTransaction trans);
void SaveRefundDataToDB();
void DeleteRefundDataFromDB(SQLTransaction* trans);
void DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans);
Bag* ToBag() { if (IsBag()) return reinterpret_cast<Bag*>(this); else return nullptr; }
[[nodiscard]] const Bag* ToBag() const { if (IsBag()) return reinterpret_cast<const Bag*>(this); else return nullptr; }
@ -317,7 +317,7 @@ public:
[[nodiscard]] bool IsConjuredConsumable() const { return GetTemplate()->IsConjuredConsumable(); }
// Item Refund system
void SetNotRefundable(Player* owner, bool changestate = true, SQLTransaction* trans = nullptr);
void SetNotRefundable(Player* owner, bool changestate = true, CharacterDatabaseTransaction* trans = nullptr);
void SetRefundRecipient(ObjectGuid::LowType pGuidLow) { m_refundRecipient = pGuidLow; }
void SetPaidMoney(uint32 money) { m_paidMoney = money; }
void SetPaidExtendedCost(uint32 iece) { m_paidExtendedCost = iece; }

View file

@ -96,12 +96,11 @@ void Pet::RemoveFromWorld()
SpellCastResult Pet::TryLoadFromDB(Player* owner, bool current /*= false*/, PetType mandatoryPetType /*= MAX_PET_TYPE*/)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS);
stmt->setUInt32(0, owner->GetGUID().GetCounter());
stmt->setUInt8(1, uint8(current ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
PreparedQueryResult result = CharacterDatabase.AsyncQuery(stmt);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
return SPELL_FAILED_NO_PET;
@ -157,7 +156,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint8 asynchLoadType, uint32 petentry, ui
return false;
ObjectGuid::LowType ownerGuid = owner->GetGUID().GetCounter();
PreparedStatement* stmt;
CharacterDatabasePreparedStatement* stmt;
if (petnumber)
{
@ -191,15 +190,21 @@ bool Pet::LoadPetFromDB(Player* owner, uint8 asynchLoadType, uint32 petentry, ui
stmt->setUInt8(2, uint8(PET_SAVE_LAST_STABLE_SLOT));
}
if (AsynchPetSummon* infoToDelete = owner->GetSession()->_loadPetFromDBFirstCallback.GetSecondParam())
// load them asynchronously
{
delete infoToDelete;
WorldSession* mySess = owner->GetSession();
mySess->GetQueryProcessor().AddCallback(CharacterDatabase.AsyncQuery(stmt)
.WithPreparedCallback([mySess, asynchLoadType, info, owner](PreparedQueryResult result)
{
// process only if player is in world (teleport crashes?)
// otherwise wait with result till he logs in
uint8 loadResult = mySess->HandleLoadPetFromDBFirstCallback(result, asynchLoadType);
if (loadResult != PET_LOAD_OK)
Pet::HandleAsynchLoadFailed(info, owner, asynchLoadType, loadResult);
}));
}
owner->GetSession()->_loadPetFromDBFirstCallback.Reset();
owner->GetSession()->_loadPetFromDBFirstCallback.SetFirstParam(asynchLoadType);
owner->GetSession()->_loadPetFromDBFirstCallback.SetSecondParam(info);
owner->GetSession()->_loadPetFromDBFirstCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
return true;
}
@ -229,7 +234,7 @@ void Pet::SavePetToDB(PetSaveMode mode, bool logout)
uint32 curhealth = GetHealth();
uint32 curmana = GetPower(POWER_MANA);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
// save auras before possibly removing them
_SaveAuras(trans, logout);
@ -248,7 +253,7 @@ void Pet::SavePetToDB(PetSaveMode mode, bool logout)
trans = CharacterDatabase.BeginTransaction();
// remove current data
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_ID);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_ID);
stmt->setUInt32(0, m_charmInfo->GetPetNumber());
trans->Append(stmt);
@ -310,9 +315,9 @@ void Pet::SavePetToDB(PetSaveMode mode, bool logout)
void Pet::DeleteFromDB(ObjectGuid::LowType guidlow)
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_ID);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_BY_ID);
stmt->setUInt32(0, guidlow);
trans->Append(stmt);
@ -1164,9 +1169,9 @@ void Pet::_LoadSpellCooldowns(PreparedQueryResult result)
}
}
void Pet::_SaveSpellCooldowns(SQLTransaction& trans, bool logout)
void Pet::_SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELL_COOLDOWNS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELL_COOLDOWNS);
stmt->setUInt32(0, m_charmInfo->GetPetNumber());
trans->Append(stmt);
@ -1206,7 +1211,7 @@ void Pet::_LoadSpells(PreparedQueryResult result)
}
}
void Pet::_SaveSpells(SQLTransaction& trans)
void Pet::_SaveSpells(CharacterDatabaseTransaction trans)
{
for (PetSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
{
@ -1216,7 +1221,7 @@ void Pet::_SaveSpells(SQLTransaction& trans)
if (itr->second.type == PETSPELL_FAMILY)
continue;
PreparedStatement* stmt;
CharacterDatabasePreparedStatement* stmt;
switch (itr->second.state)
{
@ -1333,9 +1338,9 @@ void Pet::_LoadAuras(PreparedQueryResult result, uint32 timediff)
}
}
void Pet::_SaveAuras(SQLTransaction& trans, bool logout)
void Pet::_SaveAuras(CharacterDatabaseTransaction trans, bool logout)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_AURAS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_AURAS);
stmt->setUInt32(0, m_charmInfo->GetPetNumber());
trans->Append(stmt);
@ -1395,7 +1400,7 @@ void Pet::_SaveAuras(SQLTransaction& trans, bool logout)
uint8 index = 0;
PreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET_AURA);
CharacterDatabasePreparedStatement* stmt2 = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET_AURA);
stmt2->setUInt32(index++, m_charmInfo->GetPetNumber());
stmt2->setUInt64(index++, casterGUID.GetRawValue());
stmt2->setUInt32(index++, itr->second->GetId());
@ -1411,7 +1416,6 @@ void Pet::_SaveAuras(SQLTransaction& trans, bool logout)
stmt2->setInt32(index++, itr->second->GetMaxDuration());
stmt2->setInt32(index++, itr->second->GetDuration());
stmt2->setUInt8(index++, itr->second->GetCharges());
trans->Append(stmt2);
}
}
@ -1426,7 +1430,7 @@ bool Pet::addSpell(uint32 spellId, ActiveStates active /*= ACT_DECIDE*/, PetSpel
{
LOG_ERROR("entities.pet", "Pet::addSpell: Non-existed in SpellStore spell #%u request, deleting for all pets in `pet_spell`.", spellId);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_PET_SPELL);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_PET_SPELL);
stmt->setUInt32(0, spellId);
@ -1795,7 +1799,7 @@ void Pet::resetTalentsForAllPetsOf(Player* owner, Pet* online_pet /*= nullptr*/)
uint32 except_petnumber = online_pet ? online_pet->GetCharmInfo()->GetPetNumber() : 0;
// xinef: zomg! sync query
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET);
stmt->setUInt32(0, owner->GetGUID().GetCounter());
stmt->setUInt32(1, except_petnumber);
PreparedQueryResult resultPets = CharacterDatabase.Query(stmt);

View file

@ -113,9 +113,9 @@ public:
void ClearCastWhenWillAvailable();
void RemoveSpellCooldown(uint32 spell_id, bool update /* = false */);
void _SaveSpellCooldowns(SQLTransaction& trans, bool logout);
void _SaveAuras(SQLTransaction& trans, bool logout);
void _SaveSpells(SQLTransaction& trans);
void _SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout);
void _SaveAuras(CharacterDatabaseTransaction trans, bool logout);
void _SaveSpells(CharacterDatabaseTransaction trans);
void _LoadSpellCooldowns(PreparedQueryResult result);
void _LoadAuras(PreparedQueryResult result, uint32 timediff);

View file

@ -56,6 +56,7 @@
#include "Player.h"
#include "PoolMgr.h"
#include "QuestDef.h"
#include "QueryHolder.h"
#include "ReputationMgr.h"
#include "revision.h"
#include "Realm.h"
@ -1666,7 +1667,7 @@ void Player::Update(uint32 p_time)
if (GetSession()->m_muteTime && GetSession()->m_muteTime < now)
{
GetSession()->m_muteTime = 0;
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME);
stmt->setInt64(0, 0); // Set the mute time to 0
stmt->setString(1, "");
stmt->setString(2, "");
@ -1853,7 +1854,7 @@ void Player::Update(uint32 p_time)
{
if (m_additionalSaveTimer <= p_time)
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
if (m_additionalSaveMask & ADDITIONAL_SAVING_INVENTORY_AND_GOLD)
SaveInventoryAndGoldToDB(trans);
@ -3404,7 +3405,7 @@ void Player::GiveLevel(uint8 level)
if (mailReward && sScriptMgr->CanGiveMailRewardAtGiveLevel(this, level))
{
//- TODO: Poor design of mail system
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
MailDraft(mailReward->mailTemplateId).SendMailTo(trans, this, MailSender(MAIL_CREATURE, mailReward->senderEntry));
CharacterDatabase.CommitTransaction(trans);
}
@ -3733,7 +3734,7 @@ void Player::UpdateNextMailTimeAndUnreads()
//Update the next delivery time and unread mails
time_t cTime = time(nullptr);
//Get the next delivery time
PreparedStatement* stmtNextDeliveryTime = CharacterDatabase.GetPreparedStatement(CHAR_SEL_NEXT_MAIL_DELIVERYTIME);
CharacterDatabasePreparedStatement* stmtNextDeliveryTime = CharacterDatabase.GetPreparedStatement(CHAR_SEL_NEXT_MAIL_DELIVERYTIME);
stmtNextDeliveryTime->setUInt32(0, GetGUID().GetCounter());
stmtNextDeliveryTime->setUInt64(1, cTime);
PreparedQueryResult resultNextDeliveryTime = CharacterDatabase.Query(stmtNextDeliveryTime);
@ -3744,7 +3745,7 @@ void Player::UpdateNextMailTimeAndUnreads()
}
//Get unread mails count
PreparedStatement* stmtUnreadAmount = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD_SYNCH);
CharacterDatabasePreparedStatement* stmtUnreadAmount = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD_SYNCH);
stmtUnreadAmount->setUInt32(0, GetGUID().GetCounter());
stmtUnreadAmount->setUInt64(1, cTime);
PreparedQueryResult resultUnreadAmount = CharacterDatabase.Query(stmtUnreadAmount);
@ -4480,9 +4481,9 @@ void Player::_LoadSpellCooldowns(PreparedQueryResult result)
}
}
void Player::_SaveSpellCooldowns(SQLTransaction& trans, bool logout)
void Player::_SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_COOLDOWN);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_COOLDOWN);
stmt->setUInt32(0, GetGUID().GetCounter());
trans->Append(stmt);
@ -4868,14 +4869,14 @@ void Player::DeleteFromDB(ObjectGuid::LowType lowGuid, uint32 accountId, bool up
// Remove signs from petitions (also remove petitions if owner);
RemovePetitionsAndSigns(playerGuid, 10);
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
switch (charDelete_method)
{
// Completely remove from the database
case CHAR_DELETE_REMOVE:
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL);
stmt->setUInt32(0, lowGuid);
@ -5192,7 +5193,7 @@ void Player::DeleteOldCharacters(uint32 keepDays)
LOG_INFO("server.loading", "Player::DeleteOldChars: Deleting all characters which have been deleted %u days before...", keepDays);
LOG_INFO("server.loading", " ");
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_OLD_CHARS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_OLD_CHARS);
stmt->setUInt32(0, uint32(time(nullptr) - time_t(keepDays * DAY)));
PreparedQueryResult result = CharacterDatabase.Query(stmt);
@ -5394,10 +5395,10 @@ void Player::KillPlayer()
//UpdateObjectVisibility(); // pussywizard: not needed
}
void Player::OfflineResurrect(ObjectGuid const guid, SQLTransaction& trans)
void Player::OfflineResurrect(ObjectGuid const guid, CharacterDatabaseTransaction trans)
{
Corpse::DeleteFromDB(guid, trans);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(AT_LOGIN_RESURRECT));
stmt->setUInt64(1, guid.GetCounter());
CharacterDatabase.ExecuteOrAppend(trans, stmt);
@ -5489,7 +5490,7 @@ void Player::RemoveCorpse()
GetCorpse()->RemoveFromWorld();
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
Corpse::DeleteFromDB(GetGUID(), trans);
CharacterDatabase.CommitTransaction(trans);
}
@ -5501,10 +5502,10 @@ void Player::SpawnCorpseBones(bool triggerSave /*= true*/)
if (triggerSave && !GetSession()->PlayerLogoutWithSave()) // at logout we will already store the player
{
// prevent loading as ghost without corpse
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
// pussywizard: update only ghost flag instead of whole character table entry! data integrity is crucial
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_REMOVE_GHOST);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_REMOVE_GHOST);
stmt->setUInt32(0, GetGUID().GetCounter());
trans->Append(stmt);
@ -7599,7 +7600,7 @@ void Player::SetArenaPoints(uint32 value)
AddKnownCurrency(ITEM_ARENA_POINTS_ID);
}
void Player::ModifyHonorPoints(int32 value, SQLTransaction* trans /*=nullptr*/)
void Player::ModifyHonorPoints(int32 value, CharacterDatabaseTransaction trans)
{
int32 newValue = int32(GetHonorPoints()) + value;
if (newValue < 0)
@ -7608,14 +7609,14 @@ void Player::ModifyHonorPoints(int32 value, SQLTransaction* trans /*=nullptr*/)
if (trans)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_HONOR_POINTS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_HONOR_POINTS);
stmt->setUInt32(0, newValue);
stmt->setUInt32(1, GetGUID().GetCounter());
(*trans)->Append(stmt);
trans->Append(stmt);
}
}
void Player::ModifyArenaPoints(int32 value, SQLTransaction* trans /*=nullptr*/)
void Player::ModifyArenaPoints(int32 value, CharacterDatabaseTransaction trans)
{
int32 newValue = int32(GetArenaPoints()) + value;
if (newValue < 0)
@ -7624,10 +7625,10 @@ void Player::ModifyArenaPoints(int32 value, SQLTransaction* trans /*=nullptr*/)
if (trans)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_ARENA_POINTS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_ARENA_POINTS);
stmt->setUInt32(0, newValue);
stmt->setUInt32(1, GetGUID().GetCounter());
(*trans)->Append(stmt);
trans->Append(stmt);
}
}
@ -7660,7 +7661,7 @@ uint32 Player::GetArenaTeamIdFromStorage(ObjectGuid::LowType guid, uint8 slot)
uint32 Player::GetArenaTeamIdFromDB(ObjectGuid guid, uint8 type)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ARENA_TEAM_ID_BY_PLAYER_GUID);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ARENA_TEAM_ID_BY_PLAYER_GUID);
stmt->setUInt32(0, guid.GetCounter());
stmt->setUInt8(1, type);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
@ -7676,7 +7677,7 @@ uint32 Player::GetZoneIdFromDB(ObjectGuid guid)
{
ObjectGuid::LowType guidLow = guid.GetCounter();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_ZONE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_ZONE);
stmt->setUInt32(0, guidLow);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
@ -12781,7 +12782,7 @@ Item* Player::StoreNewItem(ItemPosCountVec const& dest, uint32 item, bool update
for (++itr; itr != allowedLooters.end(); ++itr)
ss << ' ' << (*itr).GetCounter();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_BOP_TRADE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_BOP_TRADE);
stmt->setUInt32(0, pItem->GetGUID().GetCounter());
stmt->setString(1, ss.str());
CharacterDatabase.Execute(stmt);
@ -13253,7 +13254,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update)
if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED))
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
stmt->setUInt32(0, pItem->GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
}
@ -16044,7 +16045,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
// Xinef: send items that couldn't be added properly by mail
if (!problematicItems.empty())
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
MailSender sender(MAIL_CREATURE, 34337 /* The Postmaster */ );
MailDraft draft("Recovered Item", "We recovered a lost item in the twisting nether and noted that it was yours.$B$BPlease find said object enclosed."); // This is the text used in Cataclysm, it probably wasn't changed.
@ -16123,7 +16124,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
if (uint32 mail_template_id = quest->GetRewMailTemplateId())
{
//- TODO: Poor design of mail system
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
if (quest->GetRewMailSenderEntry() != 0)
MailDraft(mail_template_id).SendMailTo(trans, this, quest->GetRewMailSenderEntry(), MAIL_CHECK_MASK_HAS_BODY, quest->GetRewMailDelaySecs());
else
@ -17839,7 +17840,7 @@ void Player::_LoadEntryPointData(PreparedQueryResult result)
bool Player::LoadPositionFromDB(uint32& mapid, float& x, float& y, float& z, float& o, bool& in_flight, ObjectGuid::LowType guid)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_POSITION);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_POSITION);
stmt->setUInt32(0, guid);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
@ -17865,7 +17866,7 @@ void Player::SetHomebind(WorldLocation const& loc, uint32 areaId)
m_homebindAreaId = areaId;
// update sql homebind
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PLAYER_HOMEBIND);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PLAYER_HOMEBIND);
stmt->setUInt16(0, m_homebindMapId);
stmt->setUInt16(1, m_homebindAreaId);
stmt->setFloat (2, m_homebindX);
@ -17897,7 +17898,7 @@ bool Player::isBeingLoaded() const
return GetSession()->PlayerLoading();
}
bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder const& holder)
{
//// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
//QueryResult* result = CharacterDatabase.PQuery("SELECT guid, account, name, race, class, gender, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, bankSlots, restState, playerFlags, "
@ -17909,7 +17910,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
//"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, "
// 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
//"health, power1, power2, power3, power4, power5, power6, power7, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, knownTitles, actionBars, grantableLevels FROM characters WHERE guid = '%u'", guid);
PreparedQueryResult result = holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM);
PreparedQueryResult result = holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM);
if (!result)
{
@ -17929,7 +17930,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
return false;
}
if (holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BANNED))
if (holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BANNED))
{
LOG_ERROR("entities.player", "Player (%s) is banned, can't load.", playerGuid.ToString().c_str());
return false;
@ -17945,7 +17946,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS ||
(AccountMgr::IsPlayerAccount(GetSession()->GetSecurity()) && sObjectMgr->IsReservedName(m_name)))
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(AT_LOGIN_RENAME));
stmt->setUInt32(1, guid);
CharacterDatabase.Execute(stmt);
@ -17979,7 +17980,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f);
// load achievements before anything else to prevent multiple gains for the same achievement/criteria on every loading (as loading does call UpdateAchievementCriteria)
m_achievementMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS));
m_achievementMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS));
uint32 money = fields[8].GetUInt32();
if (money > MAX_MONEY_AMOUNT)
@ -18028,7 +18029,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
sInstanceSaveMgr->PlayerCreateBoundInstancesMaps(playerGuid);
// load home bind and check in same time class/race pair, it used later for restore broken positions
if (!_LoadHomeBind(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_HOME_BIND)))
if (!_LoadHomeBind(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_HOME_BIND)))
return false;
InitPrimaryProfessions(); // to max set before any spell loaded
@ -18065,8 +18066,8 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
SetUInt16Value(PLAYER_FIELD_KILLS, 0, fields[49].GetUInt16());
SetUInt16Value(PLAYER_FIELD_KILLS, 1, fields[50].GetUInt16());
_LoadInstanceTimeRestrictions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES));
_LoadEntryPointData(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ENTRY_POINT));
_LoadInstanceTimeRestrictions(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES));
_LoadEntryPointData(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ENTRY_POINT));
GetSession()->SetPlayer(this);
MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
@ -18381,7 +18382,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
}
// load skills after InitStatsForLevel because it triggering aura apply also
_LoadSkills(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SKILLS));
_LoadSkills(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SKILLS));
UpdateSkillsForLevel(); //update skills after load, to make sure they are correctly update at player load
// apply original stats mods before spell loading or item equipment that call before equip _RemoveStatsMods()
@ -18395,12 +18396,12 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
LearnDefaultSkills();
LearnCustomSpells();
_LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS));
_LoadTalents(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS));
_LoadSpells(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS));
_LoadTalents(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_TALENTS));
_LoadGlyphs(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GLYPHS));
_LoadGlyphs(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_GLYPHS));
_LoadGlyphAuras();
_LoadAuras(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURAS), time_diff);
_LoadAuras(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_AURAS), time_diff);
// add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura)
if (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
{
@ -18412,32 +18413,32 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP);
// after spell load, learn rewarded spell if need also
_LoadQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS));
_LoadQuestStatusRewarded(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS_REW));
_LoadDailyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DAILY_QUEST_STATUS));
_LoadWeeklyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_WEEKLY_QUEST_STATUS));
_LoadSeasonalQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SEASONAL_QUEST_STATUS));
_LoadMonthlyQuestStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS));
_LoadRandomBGStatus(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_RANDOM_BG));
_LoadQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS));
_LoadQuestStatusRewarded(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS_REW));
_LoadDailyQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DAILY_QUEST_STATUS));
_LoadWeeklyQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_WEEKLY_QUEST_STATUS));
_LoadSeasonalQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SEASONAL_QUEST_STATUS));
_LoadMonthlyQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS));
_LoadRandomBGStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_RANDOM_BG));
// after spell and quest load
InitTalentForLevel();
// must be before inventory (some items required reputation check)
m_reputationMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION));
m_reputationMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION));
// xinef: load mails before inventory, so problematic items can be added to already loaded mails
// unread mails and next delivery time, actual mails not loaded
_LoadMailInit(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_UNREAD_COUNT), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE));
_LoadMailInit(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_COUNT), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_UNREAD_COUNT), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_DATE));
_LoadInventory(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY), time_diff);
_LoadInventory(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY), time_diff);
// update items with duration and realtime
UpdateItemDuration(time_diff, true);
_LoadActions(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACTIONS));
_LoadActions(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACTIONS));
m_social = sSocialMgr->LoadFromDB(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SOCIAL_LIST), GetGUID());
m_social = sSocialMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SOCIAL_LIST), GetGUID());
// check PLAYER_CHOSEN_TITLE compatibility with PLAYER__FIELD_KNOWN_TITLES
// note: PLAYER__FIELD_KNOWN_TITLES updated at quest status loaded
@ -18450,7 +18451,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
// has to be called after last Relocate() in Player::LoadFromDB
SetFallInformation(time(nullptr), GetPositionZ());
_LoadSpellCooldowns(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS));
_LoadSpellCooldowns(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS));
// Spell code allow apply any auras to dead character in load time in aura/spell/item loading
// Do now before stats re-calculation cleanup for ghost state unexpected auras
@ -18543,13 +18544,13 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, SQLQueryHolder* holder)
if (m_grantableLevels > 0)
SetByteValue(PLAYER_FIELD_BYTES, 1, 0x01);
_LoadDeclinedNames(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES));
_LoadDeclinedNames(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES));
//m_achievementMgr->CheckAllAchievementCriteria(); // pussywizard: ZOMG! disabled this bullshit
_LoadEquipmentSets(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS));
_LoadEquipmentSets(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS));
_LoadBrewOfTheMonth(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BREW_OF_THE_MONTH));
_LoadBrewOfTheMonth(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_BREW_OF_THE_MONTH));
// Players are immune to taunt
ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
@ -18810,7 +18811,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
std::map<ObjectGuid::LowType, Bag*> bagMap; // fast guid lookup for bags
std::map<ObjectGuid::LowType, Item*> invalidBagMap; // fast guid lookup for bags
std::list<Item*> problematicItems;
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
// Prevent items from being added to the queue while loading
m_itemUpdateQueueBlocked = true;
@ -18927,7 +18928,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff)
_ApplyAllItemMods();
}
Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, Field* fields)
Item* Player::_LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint32 timeDiff, Field* fields)
{
Item* item = nullptr;
ObjectGuid::LowType itemGuid = fields[13].GetUInt32();
@ -18938,7 +18939,7 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F
item = NewItemOrBag(proto);
if (item->LoadFromDB(itemGuid, GetGUID(), fields, itemEntry))
{
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
// Do not allow to have item limited to another map/zone in alive state
if (IsAlive() && item->IsLimitedToAnotherMapOrZone(GetMapId(), zoneId))
@ -19058,7 +19059,7 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F
void Player::_LoadMailedItems(Mail* mail)
{
// data needs to be at first place for Item::LoadFromDB
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS);
stmt->setUInt32(0, mail->messageID);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
@ -19101,7 +19102,7 @@ void Player::_LoadMailedItems(Mail* mail)
item->FSetState(ITEM_REMOVED);
SQLTransaction temp = SQLTransaction(nullptr);
CharacterDatabaseTransaction temp = CharacterDatabaseTransaction(nullptr);
item->SaveToDB(temp); // it also deletes item object !
continue;
}
@ -19141,7 +19142,7 @@ void Player::_LoadMail()
if (m_mailsUpdated)
{
//Save changed data to the sql before refreshing so we always get up to date data
SQLTransaction saveTransaction = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction saveTransaction = CharacterDatabase.BeginTransaction();
_SaveMail(saveTransaction);
CharacterDatabase.CommitTransaction(saveTransaction);
}
@ -19165,7 +19166,7 @@ void Player::_LoadMail()
//Now load the new ones
m_mailCache.clear();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL);
stmt->setUInt32(0, GetGUID().GetCounter());
stmt->setUInt64(1, time(nullptr));
PreparedQueryResult result = CharacterDatabase.Query(stmt);
@ -19991,7 +19992,7 @@ bool Player::_LoadHomeBind(PreparedQueryResult result)
ok = true;
else
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_HOMEBIND);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_HOMEBIND);
stmt->setUInt32(0, GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
}
@ -20005,7 +20006,7 @@ bool Player::_LoadHomeBind(PreparedQueryResult result)
m_homebindY = info->positionY;
m_homebindZ = info->positionZ;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_HOMEBIND);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PLAYER_HOMEBIND);
stmt->setUInt32(0, GetGUID().GetCounter());
stmt->setUInt16(1, m_homebindMapId);
stmt->setUInt16(2, m_homebindAreaId);
@ -20025,6 +20026,15 @@ bool Player::_LoadHomeBind(PreparedQueryResult result)
/*********************************************************/
void Player::SaveToDB(bool create, bool logout)
{
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
SaveToDB(trans, create, logout);
CharacterDatabase.CommitTransaction(trans);
}
void Player::SaveToDB(CharacterDatabaseTransaction trans, bool create, bool logout)
{
// delay auto save at any saves (manual, in code, or autosave)
m_nextSave = sWorld->getIntConfig(CONFIG_INTERVAL_SAVE);
@ -20049,8 +20059,6 @@ void Player::SaveToDB(bool create, bool logout)
if (!create)
sScriptMgr->OnPlayerSave(this);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
_SaveCharacter(create, trans);
if (m_mailsUpdated) //save mails only when needed
@ -20081,8 +20089,6 @@ void Player::SaveToDB(bool create, bool logout)
if (m_session->isLogingOut() || !sWorld->getBoolConfig(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT))
_SaveStats(trans);
CharacterDatabase.CommitTransaction(trans);
// save pet (hunter pet level and experience and all type pets health/mana).
if (Pet* pet = GetPet())
pet->SavePetToDB(PET_SAVE_AS_CURRENT, logout);
@ -20099,23 +20105,23 @@ void Player::SaveToDB(bool create, bool logout)
}
// fast save function for item/money cheating preventing - save only inventory and money state
void Player::SaveInventoryAndGoldToDB(SQLTransaction& trans)
void Player::SaveInventoryAndGoldToDB(CharacterDatabaseTransaction trans)
{
_SaveInventory(trans);
SaveGoldToDB(trans);
}
void Player::SaveGoldToDB(SQLTransaction& trans)
void Player::SaveGoldToDB(CharacterDatabaseTransaction trans)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_MONEY);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_MONEY);
stmt->setUInt32(0, GetMoney());
stmt->setUInt32(1, GetGUID().GetCounter());
trans->Append(stmt);
}
void Player::_SaveActions(SQLTransaction& trans)
void Player::_SaveActions(CharacterDatabaseTransaction trans)
{
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
for (ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end();)
{
@ -20161,9 +20167,9 @@ void Player::_SaveActions(SQLTransaction& trans)
}
}
void Player::_SaveAuras(SQLTransaction& trans, bool logout)
void Player::_SaveAuras(CharacterDatabaseTransaction trans, bool logout)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_AURA);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_AURA);
stmt->setUInt32(0, GetGUID().GetCounter());
trans->Append(stmt);
@ -20219,9 +20225,9 @@ void Player::_SaveAuras(SQLTransaction& trans, bool logout)
}
}
void Player::_SaveInventory(SQLTransaction& trans)
void Player::_SaveInventory(CharacterDatabaseTransaction trans)
{
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
// force items in buyback slots to new state
// and remove those that aren't already
for (uint8 i = BUYBACK_SLOT_START; i < BUYBACK_SLOT_END; ++i)
@ -20357,14 +20363,14 @@ void Player::_SaveInventory(SQLTransaction& trans)
m_itemUpdateQueue.clear();
}
void Player::_SaveMail(SQLTransaction& trans)
void Player::_SaveMail(CharacterDatabaseTransaction trans)
{
if (!GetMailCacheSize() || !m_mailsUpdated)
{
return;
}
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
for (PlayerMails::iterator itr = m_mailCache.begin(); itr != m_mailCache.end(); ++itr)
{
@ -20434,7 +20440,7 @@ void Player::_SaveMail(SQLTransaction& trans)
m_mailsUpdated = false;
}
void Player::_SaveQuestStatus(SQLTransaction& trans)
void Player::_SaveQuestStatus(CharacterDatabaseTransaction trans)
{
bool isTransaction = static_cast<bool>(trans);
if (!isTransaction)
@ -20442,7 +20448,7 @@ void Player::_SaveQuestStatus(SQLTransaction& trans)
QuestStatusSaveMap::iterator saveItr;
QuestStatusMap::iterator statusItr;
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
bool keepAbandoned = !(sWorld->GetCleaningFlags() & CharacterDatabaseCleaner::CLEANING_FLAG_QUESTSTATUS);
@ -20501,7 +20507,7 @@ void Player::_SaveQuestStatus(SQLTransaction& trans)
CharacterDatabase.CommitTransaction(trans);
}
void Player::_SaveDailyQuestStatus(SQLTransaction& trans)
void Player::_SaveDailyQuestStatus(CharacterDatabaseTransaction trans)
{
if (!m_DailyQuestChanged)
return;
@ -20511,7 +20517,7 @@ void Player::_SaveDailyQuestStatus(SQLTransaction& trans)
// save last daily quest time for all quests: we need only mostly reset time for reset check anyway
// we don't need transactions here.
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_DAILY_CHAR);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_DAILY_CHAR);
stmt->setUInt32(0, GetGUID().GetCounter());
trans->Append(stmt);
for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
@ -20539,13 +20545,13 @@ void Player::_SaveDailyQuestStatus(SQLTransaction& trans)
}
}
void Player::_SaveWeeklyQuestStatus(SQLTransaction& trans)
void Player::_SaveWeeklyQuestStatus(CharacterDatabaseTransaction trans)
{
if (!m_WeeklyQuestChanged || m_weeklyquests.empty())
return;
// we don't need transactions here.
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR);
stmt->setUInt32(0, GetGUID().GetCounter());
trans->Append(stmt);
@ -20562,13 +20568,13 @@ void Player::_SaveWeeklyQuestStatus(SQLTransaction& trans)
m_WeeklyQuestChanged = false;
}
void Player::_SaveSeasonalQuestStatus(SQLTransaction& trans)
void Player::_SaveSeasonalQuestStatus(CharacterDatabaseTransaction trans)
{
if (!m_SeasonalQuestChanged || m_seasonalquests.empty())
return;
// we don't need transactions here.
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR);
stmt->setUInt32(0, GetGUID().GetCounter());
trans->Append(stmt);
@ -20590,13 +20596,13 @@ void Player::_SaveSeasonalQuestStatus(SQLTransaction& trans)
m_SeasonalQuestChanged = false;
}
void Player::_SaveMonthlyQuestStatus(SQLTransaction& trans)
void Player::_SaveMonthlyQuestStatus(CharacterDatabaseTransaction trans)
{
if (!m_MonthlyQuestChanged || m_monthlyquests.empty())
return;
// we don't need transactions here.
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_MONTHLY_CHAR);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_QUEST_STATUS_MONTHLY_CHAR);
stmt->setUInt32(0, GetGUID().GetCounter());
trans->Append(stmt);
@ -20612,9 +20618,9 @@ void Player::_SaveMonthlyQuestStatus(SQLTransaction& trans)
m_MonthlyQuestChanged = false;
}
void Player::_SaveSkills(SQLTransaction& trans)
void Player::_SaveSkills(CharacterDatabaseTransaction trans)
{
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
// we don't need transactions here.
for (SkillStatusMap::iterator itr = mSkillStatus.begin(); itr != mSkillStatus.end();)
{
@ -20668,9 +20674,9 @@ void Player::_SaveSkills(SQLTransaction& trans)
}
}
void Player::_SaveSpells(SQLTransaction& trans)
void Player::_SaveSpells(CharacterDatabaseTransaction trans)
{
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
for (PlayerSpellMap::iterator itr = m_spells.begin(); itr != m_spells.end();)
{
@ -20715,13 +20721,13 @@ void Player::_SaveSpells(SQLTransaction& trans)
// save player stats -- only for external usage
// real stats will be recalculated on player login
void Player::_SaveStats(SQLTransaction& trans)
void Player::_SaveStats(CharacterDatabaseTransaction trans)
{
// check if stat saving is enabled and if char level is high enough
if (!sWorld->getIntConfig(CONFIG_MIN_LEVEL_STAT_SAVE) || getLevel() < sWorld->getIntConfig(CONFIG_MIN_LEVEL_STAT_SAVE))
return;
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_STATS);
stmt->setUInt32(0, GetGUID().GetCounter());
@ -20826,7 +20832,7 @@ void Player::SendAttackSwingNotInRange()
void Player::SavePositionInDB(uint32 mapid, float x, float y, float z, float o, uint32 zone, ObjectGuid guid)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_POSITION);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_POSITION);
stmt->setFloat(0, x);
stmt->setFloat(1, y);
@ -20839,6 +20845,21 @@ void Player::SavePositionInDB(uint32 mapid, float x, float y, float z, float o,
CharacterDatabase.Execute(stmt);
}
void Player::SavePositionInDB(WorldLocation const& loc, uint16 zoneId, ObjectGuid guid, CharacterDatabaseTransaction trans)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_POSITION);
stmt->setFloat(0, loc.GetPositionX());
stmt->setFloat(1, loc.GetPositionY());
stmt->setFloat(2, loc.GetPositionZ());
stmt->setFloat(3, loc.GetOrientation());
stmt->setUInt16(4, uint16(loc.GetMapId()));
stmt->setUInt16(5, zoneId);
stmt->setUInt32(6, guid.GetCounter());
CharacterDatabase.ExecuteOrAppend(trans, stmt);
}
void Player::SetUInt32ValueInArray(Tokenizer& tokens, uint16 index, uint32 value)
{
char buf[11];
@ -20850,20 +20871,18 @@ void Player::SetUInt32ValueInArray(Tokenizer& tokens, uint16 index, uint32 value
tokens[index] = buf;
}
void Player::Customize(ObjectGuid guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair)
void Player::Customize(CharacterCustomizeInfo const* customizeInfo, CharacterDatabaseTransaction trans)
{
// xinef: zomg! sync query
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GENDER_AND_APPEARANCE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GENDER_AND_APPEARANCE);
stmt->setUInt8(0, customizeInfo->Gender);
stmt->setUInt8(1, customizeInfo->Skin);
stmt->setUInt8(2, customizeInfo->Face);
stmt->setUInt8(3, customizeInfo->HairStyle);
stmt->setUInt8(4, customizeInfo->HairColor);
stmt->setUInt8(5, customizeInfo->FacialHair);
stmt->setUInt32(6, customizeInfo->Guid.GetCounter());
stmt->setUInt8(0, gender);
stmt->setUInt8(1, skin);
stmt->setUInt8(2, face);
stmt->setUInt8(3, hairStyle);
stmt->setUInt8(4, hairColor);
stmt->setUInt8(5, facialHair);
stmt->setUInt32(6, guid.GetCounter());
CharacterDatabase.Execute(stmt);
CharacterDatabase.ExecuteOrAppend(trans, stmt);
}
void Player::SendAttackSwingDeadTarget()
@ -21895,22 +21914,22 @@ void Player::RemovePetitionsAndSigns(ObjectGuid guid, uint32 type)
if (type == 10)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_PETITION_SIGNATURES);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_PETITION_SIGNATURES);
stmt->setUInt32(0, guid.GetCounter());
CharacterDatabase.Execute(stmt);
}
else
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE);
stmt->setUInt32(0, guid.GetCounter());
stmt->setUInt8(1, uint8(type));
CharacterDatabase.Execute(stmt);
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
if (type == 10)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_OWNER);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_OWNER);
stmt->setUInt32(0, guid.GetCounter());
trans->Append(stmt);
@ -21923,7 +21942,7 @@ void Player::RemovePetitionsAndSigns(ObjectGuid guid, uint32 type)
}
else
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_OWNER_AND_TYPE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_OWNER_AND_TYPE);
stmt->setUInt32(0, guid.GetCounter());
stmt->setUInt8(1, uint8(type));
trans->Append(stmt);
@ -21942,7 +21961,7 @@ void Player::RemovePetitionsAndSigns(ObjectGuid guid, uint32 type)
void Player::LeaveAllArenaTeams(ObjectGuid guid)
{
// xinef: zomg! sync query
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYER_ARENA_TEAMS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYER_ARENA_TEAMS);
stmt->setUInt32(0, guid.GetCounter());
PreparedQueryResult result = CharacterDatabase.Query(stmt);
@ -23169,7 +23188,7 @@ void Player::LeaveBattleground(Battleground* bg)
{
if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS))
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK);
stmt->setUInt32(0, GetGUID().GetCounter());
stmt->setUInt8(1, BG_DESERTION_TYPE_LEAVE_BG);
CharacterDatabase.Execute(stmt);
@ -24468,7 +24487,7 @@ void Player::AutoUnequipOffhandIfNeed(bool force /*= false*/)
else
{
MoveItemFromInventory(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND, true);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
offItem->DeleteFromInventoryDB(trans); // deletes item from character's inventory
offItem->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
@ -25955,7 +25974,7 @@ void Player::_LoadSkills(PreparedQueryResult result)
{
LOG_ERROR("entities.player", "Character %s has skill %u with value 0. Will be deleted.", GetGUID().ToString().c_str(), skill);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SKILL);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SKILL);
stmt->setUInt32(0, GetGUID().GetCounter());
stmt->setUInt16(1, skill);
@ -26753,13 +26772,13 @@ void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset)
eqslot.state = old_state == EQUIPMENT_SET_NEW ? EQUIPMENT_SET_NEW : EQUIPMENT_SET_CHANGED;
}
void Player::_SaveEquipmentSets(SQLTransaction& trans)
void Player::_SaveEquipmentSets(CharacterDatabaseTransaction trans)
{
for (EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end();)
{
uint32 index = itr->first;
EquipmentSet& eqset = itr->second;
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
uint8 j = 0;
switch (eqset.state)
{
@ -26804,14 +26823,14 @@ void Player::_SaveEquipmentSets(SQLTransaction& trans)
}
}
void Player::_SaveEntryPoint(SQLTransaction& trans)
void Player::_SaveEntryPoint(CharacterDatabaseTransaction trans)
{
// xinef: dont save joinpos with invalid mapid
MapEntry const* mEntry = sMapStore.LookupEntry(m_entryPointData.joinPos.GetMapId());
if (!mEntry)
return;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_ENTRY_POINT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PLAYER_ENTRY_POINT);
stmt->setUInt32(0, GetGUID().GetCounter());
trans->Append(stmt);
@ -26856,7 +26875,7 @@ void Player::RemoveAtLoginFlag(AtLoginFlags flags, bool persist /*= false*/)
if (persist)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_REM_AT_LOGIN_FLAG);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_REM_AT_LOGIN_FLAG);
stmt->setUInt16(0, uint16(flags));
stmt->setUInt32(1, GetGUID().GetCounter());
@ -26894,9 +26913,9 @@ void Player::SetMap(Map* map)
m_mapRef.link(map, this);
}
void Player::_SaveCharacter(bool create, SQLTransaction& trans)
void Player::_SaveCharacter(bool create, CharacterDatabaseTransaction trans)
{
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
uint8 index = 0;
auto finiteAlways = [](float f) { return std::isfinite(f) ? f : 0.0f; };
@ -27189,12 +27208,12 @@ void Player::_LoadGlyphs(PreparedQueryResult result)
} while (result->NextRow());
}
void Player::_SaveGlyphs(SQLTransaction& trans)
void Player::_SaveGlyphs(CharacterDatabaseTransaction trans)
{
if (!NeedToSaveGlyphs())
return;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GLYPHS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_GLYPHS);
stmt->setUInt32(0, GetGUID().GetCounter());
trans->Append(stmt);
@ -27236,9 +27255,9 @@ void Player::_LoadTalents(PreparedQueryResult result)
}
}
void Player::_SaveTalents(SQLTransaction& trans)
void Player::_SaveTalents(CharacterDatabaseTransaction trans)
{
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
for (PlayerTalentMap::iterator itr = m_talents.begin(); itr != m_talents.end();)
{
@ -27290,8 +27309,8 @@ void Player::UpdateSpecCount(uint8 count)
if (m_activeSpec >= count)
ActivateSpec(0);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = nullptr;
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt = nullptr;
// Copy spec data
if (count > curCount)
@ -27342,7 +27361,7 @@ void Player::ActivateSpec(uint8 spec)
InterruptNonMeleeSpells(false);
// xinef: save current actions order
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
_SaveActions(trans);
CharacterDatabase.CommitTransaction(trans);
@ -27429,11 +27448,22 @@ void Player::ActivateSpec(uint8 spec)
m_usedTalentCount = spentTalents;
InitTalentForLevel();
// xinef: optimization, use callback to read the data
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC);
stmt->setUInt32(0, GetGUID().GetCounter());
stmt->setUInt8(1, m_activeSpec);
GetSession()->_loadActionsSwitchSpecCallback = CharacterDatabase.AsyncQuery(stmt); // FutureResult
// load them asynchronously
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC);
stmt->setUInt32(0, GetGUID().GetCounter());
stmt->setUInt8(1, m_activeSpec);
WorldSession* mySess = GetSession();
mySess->GetQueryProcessor().AddCallback(CharacterDatabase.AsyncQuery(stmt)
.WithPreparedCallback([mySess](PreparedQueryResult result)
{
// safe callback, we can't pass this pointer directly
// in case player logs out before db response (player would be deleted in that case)
if (Player* thisPlayer = mySess->GetPlayer())
thisPlayer->LoadActions(result);
}));
}
// xinef: reset power
Powers pw = getPowerType();
@ -27469,6 +27499,14 @@ void Player::ActivateSpec(uint8 spec)
}
}
void Player::LoadActions(PreparedQueryResult result)
{
if (result)
_LoadActions(result);
SendActionButtons(1);
}
void Player::GetTalentTreePoints(uint8 (&specPoints)[3]) const
{
const PlayerTalentMap& talentMap = GetTalentMap();
@ -27833,7 +27871,7 @@ void Player::RefundItem(Item* item)
uint32 moneyRefund = item->GetPaidMoney(); // item-> will be invalidated in DestroyItem
// Save all relevant data to DB to prevent desynchronisation exploits
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
// Delete any references to the refund data
item->SetNotRefundable(this, true, &trans);
@ -27862,11 +27900,11 @@ void Player::RefundItem(Item* item)
// Grant back Honor points
if (uint32 honorRefund = iece->reqhonorpoints)
ModifyHonorPoints(honorRefund, &trans);
ModifyHonorPoints(honorRefund, trans);
// Grant back Arena points
if (uint32 arenaRefund = iece->reqarenapoints)
ModifyArenaPoints(arenaRefund, &trans);
ModifyArenaPoints(arenaRefund, trans);
SaveInventoryAndGoldToDB(trans);
@ -27878,7 +27916,7 @@ void Player::SetRandomWinner(bool isWinner)
m_IsBGRandomWinner = isWinner;
if (m_IsBGRandomWinner)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_BATTLEGROUND_RANDOM);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_BATTLEGROUND_RANDOM);
stmt->setUInt32(0, GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
}
@ -27975,13 +28013,13 @@ void Player::_LoadBrewOfTheMonth(PreparedQueryResult result)
if (lastEventId != eventId && IsEventActive(eventId) && HasAchieved(2796 /* Brew of the Month*/))
{
// Send Mail
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
MailSender sender(MAIL_CREATURE, 27487 /*NPC_BREW_OF_THE_MONTH_CLUB*/);
MailDraft draft(uint16(212 + month)); // 212 is starting template id
draft.SendMailTo(trans, MailReceiver(this, GetGUID().GetCounter()), sender);
// Update Event Id
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_BREW_OF_THE_MONTH);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_BREW_OF_THE_MONTH);
stmt->setUInt32(0, GetGUID().GetCounter());
stmt->setUInt32(1, uint32(eventId));
trans->Append(stmt);
@ -27990,12 +28028,12 @@ void Player::_LoadBrewOfTheMonth(PreparedQueryResult result)
}
}
void Player::_SaveInstanceTimeRestrictions(SQLTransaction& trans)
void Player::_SaveInstanceTimeRestrictions(CharacterDatabaseTransaction trans)
{
if (_instanceResetTimes.empty())
return;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES);
stmt->setUInt32(0, GetSession()->GetAccountId());
trans->Append(stmt);
@ -28267,14 +28305,14 @@ void Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
bool Player::IsPetDismissed()
{
/*
* Check PET_SAVE_NOT_IN_SLOT means the pet is dismissed. If someone ever
* Changes the slot flag, they will break this validation.
*/
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT);
* Check PET_SAVE_NOT_IN_SLOT means the pet is dismissed. If someone ever
* Changes the slot flag, they will break this validation.
*/
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS);
stmt->setUInt32(0, GetGUID().GetCounter());
stmt->setUInt8(1, uint8(PET_SAVE_NOT_IN_SLOT));
if (PreparedQueryResult result = CharacterDatabase.AsyncQuery(stmt))
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
return true;
return false;

View file

@ -8,6 +8,7 @@
#define _PLAYER_H
#include "Battleground.h"
#include "DatabaseEnvFwd.h"
#include "DBCStores.h"
#include "GroupReference.h"
#include "InstanceSaveMgr.h"
@ -1557,7 +1558,7 @@ public:
/*** LOAD SYSTEM ***/
/*********************************************************/
bool LoadFromDB(ObjectGuid guid, SQLQueryHolder* holder);
bool LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& holder);
[[nodiscard]] bool isBeingLoaded() const override;
void Initialize(ObjectGuid::LowType guid);
@ -1574,13 +1575,15 @@ public:
/*********************************************************/
void SaveToDB(bool create, bool logout);
void SaveInventoryAndGoldToDB(SQLTransaction& trans); // fast save function for item/money cheating preventing
void SaveGoldToDB(SQLTransaction& trans);
void SaveToDB(CharacterDatabaseTransaction trans, bool create, bool logout);
void SaveInventoryAndGoldToDB(CharacterDatabaseTransaction trans); // fast save function for item/money cheating preventing
void SaveGoldToDB(CharacterDatabaseTransaction trans);
static void SetUInt32ValueInArray(Tokenizer& data, uint16 index, uint32 value);
static void SetFloatValueInArray(Tokenizer& data, uint16 index, float value);
static void Customize(ObjectGuid guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair);
static void Customize(CharacterCustomizeInfo const* customizeInfo, CharacterDatabaseTransaction trans);
static void SavePositionInDB(uint32 mapid, float x, float y, float z, float o, uint32 zone, ObjectGuid guid);
static void SavePositionInDB(WorldLocation const& loc, uint16 zoneId, ObjectGuid guid, CharacterDatabaseTransaction trans);
static void DeleteFromDB(ObjectGuid::LowType lowGuid, uint32 accountId, bool updateRealmChars, bool deleteFinally);
static void DeleteOldCharacters();
@ -1739,6 +1742,7 @@ public:
[[nodiscard]] uint8 GetSpecsCount() const { return m_specsCount; }
void SetSpecsCount(uint8 count) { m_specsCount = count; }
void ActivateSpec(uint8 spec);
void LoadActions(PreparedQueryResult result);
void GetTalentTreePoints(uint8 (&specPoints)[3]) const;
[[nodiscard]] uint8 GetMostPointsTalentTree() const;
bool HasTankSpec();
@ -1811,7 +1815,7 @@ public:
void RemoveArenaSpellCooldowns(bool removeActivePetCooldowns = false);
void RemoveAllSpellCooldown();
void _LoadSpellCooldowns(PreparedQueryResult result);
void _SaveSpellCooldowns(SQLTransaction& trans, bool logout);
void _SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout);
uint32 GetLastPotionId() { return m_lastPotionId; }
void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; }
void UpdatePotionCooldown(Spell* spell = nullptr);
@ -2043,7 +2047,7 @@ public:
Corpse* CreateCorpse();
void RemoveCorpse();
void KillPlayer();
static void OfflineResurrect(ObjectGuid const guid, SQLTransaction& trans);
static void OfflineResurrect(ObjectGuid const guid, CharacterDatabaseTransaction trans);
bool HasCorpse() const { return _corpseLocation.GetMapId() != MAPID_INVALID; }
WorldLocation GetCorpseLocation() const { return _corpseLocation; }
uint32 GetResurrectionSpellId();
@ -2158,8 +2162,8 @@ public:
bool RewardHonor(Unit* victim, uint32 groupsize, int32 honor = -1, bool awardXP = true);
[[nodiscard]] uint32 GetHonorPoints() const { return GetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY); }
[[nodiscard]] uint32 GetArenaPoints() const { return GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY); }
void ModifyHonorPoints(int32 value, SQLTransaction* trans = nullptr); //! If trans is specified, honor save query will be added to trans
void ModifyArenaPoints(int32 value, SQLTransaction* trans = nullptr); //! If trans is specified, arena point save query will be added to trans
void ModifyHonorPoints(int32 value, CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr)); //! If trans is specified, honor save query will be added to trans
void ModifyArenaPoints(int32 value, CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr)); //! If trans is specified, arena point save query will be added to trans
[[nodiscard]] uint32 GetMaxPersonalArenaRatingRequirement(uint32 minarenaslot) const;
void SetHonorPoints(uint32 value);
void SetArenaPoints(uint32 value);
@ -2734,24 +2738,24 @@ protected:
/*** SAVE SYSTEM ***/
/*********************************************************/
void _SaveActions(SQLTransaction& trans);
void _SaveAuras(SQLTransaction& trans, bool logout);
void _SaveInventory(SQLTransaction& trans);
void _SaveMail(SQLTransaction& trans);
void _SaveQuestStatus(SQLTransaction& trans);
void _SaveDailyQuestStatus(SQLTransaction& trans);
void _SaveWeeklyQuestStatus(SQLTransaction& trans);
void _SaveMonthlyQuestStatus(SQLTransaction& trans);
void _SaveSeasonalQuestStatus(SQLTransaction& trans);
void _SaveSkills(SQLTransaction& trans);
void _SaveSpells(SQLTransaction& trans);
void _SaveEquipmentSets(SQLTransaction& trans);
void _SaveEntryPoint(SQLTransaction& trans);
void _SaveGlyphs(SQLTransaction& trans);
void _SaveTalents(SQLTransaction& trans);
void _SaveStats(SQLTransaction& trans);
void _SaveCharacter(bool create, SQLTransaction& trans);
void _SaveInstanceTimeRestrictions(SQLTransaction& trans);
void _SaveActions(CharacterDatabaseTransaction trans);
void _SaveAuras(CharacterDatabaseTransaction trans, bool logout);
void _SaveInventory(CharacterDatabaseTransaction trans);
void _SaveMail(CharacterDatabaseTransaction trans);
void _SaveQuestStatus(CharacterDatabaseTransaction trans);
void _SaveDailyQuestStatus(CharacterDatabaseTransaction trans);
void _SaveWeeklyQuestStatus(CharacterDatabaseTransaction trans);
void _SaveMonthlyQuestStatus(CharacterDatabaseTransaction trans);
void _SaveSeasonalQuestStatus(CharacterDatabaseTransaction trans);
void _SaveSkills(CharacterDatabaseTransaction trans);
void _SaveSpells(CharacterDatabaseTransaction trans);
void _SaveEquipmentSets(CharacterDatabaseTransaction trans);
void _SaveEntryPoint(CharacterDatabaseTransaction trans);
void _SaveGlyphs(CharacterDatabaseTransaction trans);
void _SaveTalents(CharacterDatabaseTransaction trans);
void _SaveStats(CharacterDatabaseTransaction trans);
void _SaveCharacter(bool create, CharacterDatabaseTransaction trans);
void _SaveInstanceTimeRestrictions(CharacterDatabaseTransaction trans);
/*********************************************************/
/*** ENVIRONMENTAL SYSTEM ***/
@ -2926,7 +2930,7 @@ private:
InventoryResult CanStoreItem_InBag(uint8 bag, ItemPosCountVec& dest, ItemTemplate const* pProto, uint32& count, bool merge, bool non_specialized, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot) const;
InventoryResult CanStoreItem_InInventorySlots(uint8 slot_begin, uint8 slot_end, ItemPosCountVec& dest, ItemTemplate const* pProto, uint32& count, bool merge, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot) const;
Item* _StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool update);
Item* _LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, Field* fields);
Item* _LoadItem(CharacterDatabaseTransaction trans, uint32 zoneId, uint32 timeDiff, Field* fields);
typedef GuidSet RefundableItemsSet;
RefundableItemsSet m_refundableItems;

View file

@ -37,7 +37,7 @@ bool PlayerSocial::AddToSocialList(ObjectGuid friendGuid, SocialFlag flag)
{
itr->second.Flags |= flag;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_CHARACTER_SOCIAL_FLAGS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_CHARACTER_SOCIAL_FLAGS);
stmt->setUInt8(0, itr->second.Flags);
stmt->setUInt32(1, GetPlayerGUID().GetCounter());
@ -49,7 +49,7 @@ bool PlayerSocial::AddToSocialList(ObjectGuid friendGuid, SocialFlag flag)
{
m_playerSocialMap[friendGuid].Flags |= flag;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_SOCIAL);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHARACTER_SOCIAL);
stmt->setUInt32(0, GetPlayerGUID().GetCounter());
stmt->setUInt32(1, friendGuid.GetCounter());
@ -70,7 +70,7 @@ void PlayerSocial::RemoveFromSocialList(ObjectGuid friendGuid, SocialFlag flag)
if (itr->second.Flags == 0)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SOCIAL);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SOCIAL);
stmt->setUInt32(0, GetPlayerGUID().GetCounter());
stmt->setUInt32(1, friendGuid.GetCounter());
@ -81,7 +81,7 @@ void PlayerSocial::RemoveFromSocialList(ObjectGuid friendGuid, SocialFlag flag)
}
else
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_REM_CHARACTER_SOCIAL_FLAGS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_REM_CHARACTER_SOCIAL_FLAGS);
stmt->setUInt8(0, flag);
stmt->setUInt32(1, GetPlayerGUID().GetCounter());
@ -99,7 +99,7 @@ void PlayerSocial::SetFriendNote(ObjectGuid friendGuid, std::string note)
utf8truncate(note, 48); // DB and client size limitation
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_SOCIAL_NOTE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHARACTER_SOCIAL_NOTE);
stmt->setString(0, note);
stmt->setUInt32(1, GetPlayerGUID().GetCounter());

View file

@ -196,8 +196,8 @@ void GameEventMgr::StopEvent(uint16 event_id, bool overwrite)
for (itr = data.conditions.begin(); itr != data.conditions.end(); ++itr)
itr->second.done = 0;
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GAME_EVENT_CONDITION_SAVE);
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_GAME_EVENT_CONDITION_SAVE);
stmt->setUInt8(0, uint8(event_id));
trans->Append(stmt);
@ -1635,9 +1635,9 @@ void GameEventMgr::HandleQuestComplete(uint32 quest_id)
if (citr->second.done > citr->second.reqNum)
citr->second.done = citr->second.reqNum;
// save the change to db
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GAME_EVENT_CONDITION_SAVE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GAME_EVENT_CONDITION_SAVE);
stmt->setUInt8(0, uint8(event_id));
stmt->setUInt32(1, condition);
trans->Append(stmt);
@ -1680,9 +1680,9 @@ bool GameEventMgr::CheckOneGameEventConditions(uint16 event_id)
void GameEventMgr::SaveWorldEventStateToDB(uint16 event_id)
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GAME_EVENT_SAVE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GAME_EVENT_SAVE);
stmt->setUInt8(0, uint8(event_id));
trans->Append(stmt);

View file

@ -1687,7 +1687,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid
if (!linkedGuidLow) // we're removing the linking
{
_linkedRespawnStore.erase(guid);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CRELINKED_RESPAWN);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CRELINKED_RESPAWN);
stmt->setUInt32(0, guidLow);
WorldDatabase.Execute(stmt);
return true;
@ -1716,7 +1716,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(ObjectGuid::LowType guidLow, ObjectGuid
ObjectGuid linkedGuid = ObjectGuid::Create<HighGuid::Unit>(slave->id, linkedGuidLow);
_linkedRespawnStore[guid] = linkedGuid;
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_REP_CREATURE_LINKED_RESPAWN);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_REP_CREATURE_LINKED_RESPAWN);
stmt->setUInt32(0, guidLow);
stmt->setUInt32(1, linkedGuidLow);
WorldDatabase.Execute(stmt);
@ -1956,7 +1956,7 @@ void ObjectMgr::LoadCreatures()
uint32 zoneId = sMapMgr->GetZoneId(data.mapid, data.posX, data.posY, data.posZ);
uint32 areaId = sMapMgr->GetAreaId(data.mapid, data.posX, data.posY, data.posZ);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA);
stmt->setUInt32(0, zoneId);
stmt->setUInt32(1, areaId);
@ -2257,7 +2257,7 @@ void ObjectMgr::LoadGameobjects()
uint32 zoneId = sMapMgr->GetZoneId(data.mapid, data.posX, data.posY, data.posZ);
uint32 areaId = sMapMgr->GetAreaId(data.mapid, data.posX, data.posY, data.posZ);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA);
stmt->setUInt32(0, zoneId);
stmt->setUInt32(1, areaId);
@ -5192,7 +5192,7 @@ void ObjectMgr::LoadWaypointScripts()
for (ScriptMapMap::const_iterator itr = sWaypointScripts.begin(); itr != sWaypointScripts.end(); ++itr)
actionSet.insert(itr->first);
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_ACTION);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_ACTION);
PreparedQueryResult result = WorldDatabase.Query(stmt);
if (result)
@ -5680,7 +5680,7 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
time_t curTime = time(nullptr);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_EXPIRED_MAIL);
stmt->setUInt32(0, curTime);
PreparedQueryResult result = CharacterDatabase.Query(stmt);
if (!result)
@ -7628,7 +7628,7 @@ void ObjectMgr::AddReservedPlayerName(std::string const& name)
_reservedNamesStore.insert(wstr);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_RESERVED_PLAYER_NAME);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_RESERVED_PLAYER_NAME);
stmt->setString(0, name);
CharacterDatabase.Execute(stmt);
}
@ -8142,7 +8142,7 @@ bool ObjectMgr::AddGameTele(GameTele& tele)
_gameTeleStore[new_id] = tele;
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GAME_TELE);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_GAME_TELE);
stmt->setUInt32(0, new_id);
stmt->setFloat(1, tele.position_x);
@ -8171,7 +8171,7 @@ bool ObjectMgr::DeleteGameTele(const std::string& name)
{
if (itr->second.wnameLow == wname)
{
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_TELE);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_GAME_TELE);
stmt->setString(0, itr->second.name);
@ -8366,7 +8366,7 @@ void ObjectMgr::LoadTrainerSpell()
int ObjectMgr::LoadReferenceVendor(int32 vendor, int32 item, std::set<uint32>* skip_vendors)
{
// find all items from the reference vendor
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_NPC_VENDOR_REF);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_NPC_VENDOR_REF);
stmt->setUInt32(0, uint32(item));
PreparedQueryResult result = WorldDatabase.Query(stmt);
@ -8569,7 +8569,7 @@ void ObjectMgr::AddVendorItem(uint32 entry, uint32 item, int32 maxcount, uint32
if (persist)
{
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_NPC_VENDOR);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_INS_NPC_VENDOR);
stmt->setUInt32(0, entry);
stmt->setUInt32(1, item);
@ -8592,7 +8592,7 @@ bool ObjectMgr::RemoveVendorItem(uint32 entry, uint32 item, bool persist /*= tru
if (persist)
{
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_NPC_VENDOR);
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_NPC_VENDOR);
stmt->setUInt32(0, entry);
stmt->setUInt32(1, item);

View file

@ -124,7 +124,7 @@ bool Group::Create(Player* leader)
m_raidDifficulty = leader->GetRaidDifficulty();
// Store group in database
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GROUP);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GROUP);
uint8 index = 0;
@ -168,8 +168,8 @@ bool Group::LoadGroupFromDB(Field* fields)
// group leader not exist
if (!sObjectMgr->GetPlayerNameByGUID(fields[0].GetUInt32(), m_leaderName))
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP);
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP);
stmt->setUInt32(0, groupLowGuid);
trans->Append(stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_MEMBER_ALL);
@ -221,7 +221,7 @@ void Group::LoadMemberFromDB(ObjectGuid::LowType guidLow, uint8 memberFlags, uin
// skip non-existed member
if (!sObjectMgr->GetPlayerNameByGUID(member.guid.GetCounter(), member.name))
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_MEMBER);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_MEMBER);
stmt->setUInt32(0, guidLow);
stmt->setUInt32(1, GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
@ -247,7 +247,7 @@ void Group::ConvertToLFG()
m_lootMethod = NEED_BEFORE_GREED;
if (!isBGGroup() && !isBFGroup())
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_TYPE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_TYPE);
stmt->setUInt8(0, uint8(m_groupType));
stmt->setUInt32(1, GetGUID().GetCounter());
@ -266,7 +266,7 @@ void Group::ConvertToRaid()
if (!isBGGroup() && !isBFGroup())
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_TYPE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_TYPE);
stmt->setUInt8(0, uint8(m_groupType));
stmt->setUInt32(1, GetGUID().GetCounter());
@ -413,7 +413,7 @@ bool Group::AddMember(Player* player)
if (!isBGGroup() && !isBFGroup())
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GROUP_MEMBER);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GROUP_MEMBER);
stmt->setUInt32(0, GetGUID().GetCounter());
stmt->setUInt32(1, member.guid.GetCounter());
stmt->setUInt8(2, member.flags);
@ -559,7 +559,7 @@ bool Group::RemoveMember(ObjectGuid guid, const RemoveMethod& method /*= GROUP_R
// Remove player from group in DB
if (!isBGGroup() && !isBFGroup())
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_MEMBER);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP_MEMBER);
stmt->setUInt32(0, guid.GetCounter());
stmt->setUInt32(1, GetGUID().GetCounter());
CharacterDatabase.Execute(stmt);
@ -687,9 +687,9 @@ void Group::ChangeLeader(ObjectGuid newLeaderGuid)
if (!isBGGroup() && !isBFGroup())
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
// Update the group leader
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_LEADER);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_LEADER);
stmt->setUInt32(0, newLeader->GetGUID().GetCounter());
stmt->setUInt32(1, GetGUID().GetCounter());
trans->Append(stmt);
@ -776,9 +776,9 @@ void Group::Disband(bool hideDestroy /* = false */)
if (!isBGGroup() && !isBFGroup())
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GROUP);
stmt->setUInt32(0, GetGUID().GetCounter());
trans->Append(stmt);
@ -1728,7 +1728,7 @@ void Group::ChangeMembersGroup(ObjectGuid guid, uint8 group)
// Preserve new sub group in database for non-raid groups
if (!isBGGroup() && !isBFGroup())
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_MEMBER_SUBGROUP);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_MEMBER_SUBGROUP);
stmt->setUInt8(0, group);
stmt->setUInt32(1, guid.GetCounter());
@ -1927,7 +1927,7 @@ void Group::SetDungeonDifficulty(Difficulty difficulty)
m_dungeonDifficulty = difficulty;
if (!isBGGroup() && !isBFGroup())
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_DIFFICULTY);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_DIFFICULTY);
stmt->setUInt8(0, uint8(m_dungeonDifficulty));
stmt->setUInt32(1, GetGUID().GetCounter());
@ -1948,7 +1948,7 @@ void Group::SetRaidDifficulty(Difficulty difficulty)
m_raidDifficulty = difficulty;
if (!isBGGroup() && !isBFGroup())
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_RAID_DIFFICULTY);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_RAID_DIFFICULTY);
stmt->setUInt8(0, uint8(m_raidDifficulty));
stmt->setUInt32(1, GetGUID().GetCounter());
@ -2262,7 +2262,7 @@ void Group::SetGroupMemberFlag(ObjectGuid guid, bool apply, GroupMemberFlags fla
ToggleGroupMemberFlag(slot, flag, apply);
// Preserve the new setting in the db
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_MEMBER_FLAG);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GROUP_MEMBER_FLAG);
stmt->setUInt8(0, slot->flags);
stmt->setUInt32(1, guid.GetCounter());

View file

@ -7,6 +7,7 @@
#include "Common.h"
#include "DBCStores.h"
#include "GroupMgr.h"
#include "Log.h"
#include "InstanceSaveMgr.h"
#include "World.h"

View file

@ -128,7 +128,7 @@ inline void Guild::LogHolder::LoadEvent(LogEntry* entry)
// Adds new event happened in game.
// If maximum number of events is reached, oldest event is removed from collection.
inline void Guild::LogHolder::AddEvent(SQLTransaction& trans, LogEntry* entry)
inline void Guild::LogHolder::AddEvent(CharacterDatabaseTransaction trans, LogEntry* entry)
{
// Check max records limit
if (m_log.size() >= m_maxRecords)
@ -164,9 +164,9 @@ inline uint32 Guild::LogHolder::GetNextGUID()
}
// EventLogEntry
void Guild::EventLogEntry::SaveToDB(SQLTransaction& trans) const
void Guild::EventLogEntry::SaveToDB(CharacterDatabaseTransaction trans) const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_EVENTLOG);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_EVENTLOG);
stmt->setUInt32(0, m_guildId);
stmt->setUInt32(1, m_guid);
CharacterDatabase.ExecuteOrAppend(trans, stmt);
@ -200,11 +200,11 @@ void Guild::EventLogEntry::WritePacket(WorldPacket& data) const
}
// BankEventLogEntry
void Guild::BankEventLogEntry::SaveToDB(SQLTransaction& trans) const
void Guild::BankEventLogEntry::SaveToDB(CharacterDatabaseTransaction trans) const
{
uint8 index = 0;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_EVENTLOG);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_EVENTLOG);
stmt->setUInt32( index, m_guildId);
stmt->setUInt32(++index, m_guid);
stmt->setUInt8 (++index, m_bankTabId);
@ -260,9 +260,9 @@ void Guild::RankInfo::LoadFromDB(Field* fields)
m_rights |= GR_RIGHT_ALL;
}
void Guild::RankInfo::SaveToDB(SQLTransaction& trans) const
void Guild::RankInfo::SaveToDB(CharacterDatabaseTransaction trans) const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_RANK);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_RANK);
stmt->setUInt32(0, m_guildId);
stmt->setUInt8 (1, m_rankId);
stmt->setString(2, m_name);
@ -271,7 +271,7 @@ void Guild::RankInfo::SaveToDB(SQLTransaction& trans) const
CharacterDatabase.ExecuteOrAppend(trans, stmt);
}
void Guild::RankInfo::CreateMissingTabsIfNeeded(uint8 tabs, SQLTransaction& trans, bool logOnCreate /* = false */)
void Guild::RankInfo::CreateMissingTabsIfNeeded(uint8 tabs, CharacterDatabaseTransaction trans, bool logOnCreate /* = false */)
{
for (uint8 i = 0; i < tabs; ++i)
{
@ -286,7 +286,7 @@ void Guild::RankInfo::CreateMissingTabsIfNeeded(uint8 tabs, SQLTransaction& tran
if (logOnCreate)
LOG_ERROR("guild", "Guild %u has broken Tab %u for rank %u. Created default tab.", m_guildId, i, m_rankId);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT);
stmt->setUInt32(0, m_guildId);
stmt->setUInt8(1, i);
stmt->setUInt8(2, m_rankId);
@ -317,7 +317,7 @@ void Guild::RankInfo::SetName(std::string const& name)
m_name = name;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_NAME);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_NAME);
stmt->setString(0, m_name);
stmt->setUInt8 (1, m_rankId);
stmt->setUInt32(2, m_guildId);
@ -334,7 +334,7 @@ void Guild::RankInfo::SetRights(uint32 rights)
m_rights = rights;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_RIGHTS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_RIGHTS);
stmt->setUInt32(0, m_rights);
stmt->setUInt8 (1, m_rankId);
stmt->setUInt32(2, m_guildId);
@ -351,7 +351,7 @@ void Guild::RankInfo::SetBankMoneyPerDay(uint32 money)
m_bankMoneyPerDay = money;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_BANK_MONEY);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_RANK_BANK_MONEY);
stmt->setUInt32(0, money);
stmt->setUInt8 (1, m_rankId);
stmt->setUInt32(2, m_guildId);
@ -368,7 +368,7 @@ void Guild::RankInfo::SetBankTabSlotsAndRights(GuildBankRightsAndSlots rightsAnd
if (saveToDB)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_BANK_RIGHT);
stmt->setUInt32(0, m_guildId);
stmt->setUInt8 (1, guildBR.GetTabId());
stmt->setUInt8 (2, m_rankId);
@ -409,7 +409,7 @@ bool Guild::BankTab::LoadItemFromDB(Field* fields)
{
LOG_ERROR("guild", "Item (GUID %u, id: %u) not found in item_instance, deleting from guild bank!", itemGuid, itemEntry);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM);
stmt->setUInt32(0, m_guildId);
stmt->setUInt8 (1, m_tabId);
stmt->setUInt8 (2, slotId);
@ -425,7 +425,7 @@ bool Guild::BankTab::LoadItemFromDB(Field* fields)
}
// Deletes contents of the tab from the world (and from DB if necessary)
void Guild::BankTab::Delete(SQLTransaction& trans, bool removeItemsFromDB)
void Guild::BankTab::Delete(CharacterDatabaseTransaction trans, bool removeItemsFromDB)
{
for (uint8 slotId = 0; slotId < GUILD_BANK_MAX_SLOTS; ++slotId)
if (Item* pItem = m_items[slotId])
@ -503,7 +503,7 @@ void Guild::BankTab::SetInfo(std::string const& name, std::string const& icon)
m_name = name;
m_icon = icon;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_INFO);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_INFO);
stmt->setString(0, m_name);
stmt->setString(1, m_icon);
stmt->setUInt32(2, m_guildId);
@ -519,7 +519,7 @@ void Guild::BankTab::SetText(std::string const& text)
m_text = text;
utf8truncate(m_text, MAX_GUILD_BANK_TAB_TEXT_LEN); // DB and client size limitation
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_TEXT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_TAB_TEXT);
stmt->setString(0, m_text);
stmt->setUInt32(1, m_guildId);
stmt->setUInt8 (2, m_tabId);
@ -528,14 +528,14 @@ void Guild::BankTab::SetText(std::string const& text)
// Sets/removes contents of specified slot.
// If pItem == nullptr contents are removed.
bool Guild::BankTab::SetItem(SQLTransaction& trans, uint8 slotId, Item* item)
bool Guild::BankTab::SetItem(CharacterDatabaseTransaction trans, uint8 slotId, Item* item)
{
if (slotId >= GUILD_BANK_MAX_SLOTS)
return false;
m_items[slotId] = item;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_ITEM);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_ITEM);
stmt->setUInt32(0, m_guildId);
stmt->setUInt8 (1, m_tabId);
stmt->setUInt8 (2, slotId);
@ -605,7 +605,7 @@ void Guild::Member::SetPublicNote(std::string const& publicNote)
m_publicNote = publicNote;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_PNOTE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_PNOTE);
stmt->setString(0, publicNote);
stmt->setUInt32(1, m_guid.GetCounter());
CharacterDatabase.Execute(stmt);
@ -618,7 +618,7 @@ void Guild::Member::SetOfficerNote(std::string const& officerNote)
m_officerNote = officerNote;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_OFFNOTE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_OFFNOTE);
stmt->setString(0, officerNote);
stmt->setUInt32(1, m_guid.GetCounter());
CharacterDatabase.Execute(stmt);
@ -632,15 +632,15 @@ void Guild::Member::ChangeRank(uint8 newRank)
if (Player* player = FindPlayer())
player->SetRank(newRank);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_RANK);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MEMBER_RANK);
stmt->setUInt8 (0, newRank);
stmt->setUInt32(1, m_guid.GetCounter());
CharacterDatabase.Execute(stmt);
}
void Guild::Member::SaveToDB(SQLTransaction& trans) const
void Guild::Member::SaveToDB(CharacterDatabaseTransaction trans) const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER);
stmt->setUInt32(0, m_guildId);
stmt->setUInt32(1, m_guid.GetCounter());
stmt->setUInt8 (2, m_rankId);
@ -722,11 +722,11 @@ void Guild::Member::WritePacket(WorldPacket& data, bool sendOfficerNote) const
// Decreases amount of money/slots left for today.
// If (tabId == GUILD_BANK_MAX_TABS) decrease money amount.
// Otherwise decrease remaining items amount for specified tab.
void Guild::Member::UpdateBankWithdrawValue(SQLTransaction& trans, uint8 tabId, uint32 amount)
void Guild::Member::UpdateBankWithdrawValue(CharacterDatabaseTransaction trans, uint8 tabId, uint32 amount)
{
m_bankWithdraw[tabId] += amount;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER_WITHDRAW);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GUILD_MEMBER_WITHDRAW);
stmt->setUInt32(0, m_guid.GetCounter());
for (uint8 i = 0; i <= GUILD_BANK_MAX_TABS;)
{
@ -781,7 +781,7 @@ void EmblemInfo::WritePacket(WorldPacket& data) const
void EmblemInfo::SaveToDB(uint32 guildId) const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_EMBLEM_INFO);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_EMBLEM_INFO);
stmt->setUInt32(0, m_style);
stmt->setUInt32(1, m_color);
stmt->setUInt32(2, m_borderStyle);
@ -860,7 +860,7 @@ bool Guild::PlayerMoveItemData::InitItem()
return (m_pItem != nullptr);
}
void Guild::PlayerMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* /*pOther*/, uint32 splitedAmount)
void Guild::PlayerMoveItemData::RemoveItem(CharacterDatabaseTransaction trans, MoveItemData* /*pOther*/, uint32 splitedAmount)
{
if (splitedAmount)
{
@ -876,7 +876,7 @@ void Guild::PlayerMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData*
}
}
Item* Guild::PlayerMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem)
Item* Guild::PlayerMoveItemData::StoreItem(CharacterDatabaseTransaction trans, Item* pItem)
{
ASSERT(pItem);
m_pPlayer->MoveItemToInventory(m_vec, pItem, true);
@ -884,7 +884,7 @@ Item* Guild::PlayerMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem)
return pItem;
}
void Guild::PlayerMoveItemData::LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const
void Guild::PlayerMoveItemData::LogBankEvent(CharacterDatabaseTransaction trans, MoveItemData* pFrom, uint32 count) const
{
ASSERT(pFrom);
// Bank -> Char
@ -927,7 +927,7 @@ bool Guild::BankMoveItemData::HasWithdrawRights(MoveItemData* pOther) const
return slots != 0;
}
void Guild::BankMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount)
void Guild::BankMoveItemData::RemoveItem(CharacterDatabaseTransaction trans, MoveItemData* pOther, uint32 splitedAmount)
{
ASSERT(m_pItem);
if (splitedAmount)
@ -946,7 +946,7 @@ void Guild::BankMoveItemData::RemoveItem(SQLTransaction& trans, MoveItemData* pO
m_pGuild->_UpdateMemberWithdrawSlots(trans, m_pPlayer->GetGUID(), m_container);
}
Item* Guild::BankMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem)
Item* Guild::BankMoveItemData::StoreItem(CharacterDatabaseTransaction trans, Item* pItem)
{
if (!pItem)
return nullptr;
@ -968,7 +968,7 @@ Item* Guild::BankMoveItemData::StoreItem(SQLTransaction& trans, Item* pItem)
return pLastItem;
}
void Guild::BankMoveItemData::LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const
void Guild::BankMoveItemData::LogBankEvent(CharacterDatabaseTransaction trans, MoveItemData* pFrom, uint32 count) const
{
ASSERT(pFrom->GetItem());
if (pFrom->IsBank())
@ -986,7 +986,7 @@ void Guild::BankMoveItemData::LogAction(MoveItemData* pFrom) const
MoveItemData::LogAction(pFrom);
}
Item* Guild::BankMoveItemData::_StoreItem(SQLTransaction& trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const
Item* Guild::BankMoveItemData::_StoreItem(CharacterDatabaseTransaction trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const
{
uint8 slotId = uint8(pos.pos);
uint32 count = pos.count;
@ -1120,7 +1120,7 @@ Guild::Guild():
Guild::~Guild()
{
SQLTransaction temp(nullptr);
CharacterDatabaseTransaction temp(nullptr);
_DeleteBankItems(temp);
// Cleanup
@ -1161,10 +1161,11 @@ bool Guild::Create(Player* pLeader, std::string const& name)
_CreateLogHolders();
LOG_DEBUG("guild", "GUILD: creating guild [%s] for leader %s (%s)",
name.c_str(), pLeader->GetName().c_str(), m_leaderGuid.ToString().c_str());
SQLTransaction trans = CharacterDatabase.BeginTransaction();
name.c_str(), pLeader->GetName().c_str(), m_leaderGuid.ToString().c_str());
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBERS);
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBERS);
stmt->setUInt32(0, m_id);
trans->Append(stmt);
@ -1213,9 +1214,9 @@ void Guild::Disband()
DeleteMember(itr->second->GetGUID(), true);
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD);
stmt->setUInt32(0, m_id);
trans->Append(stmt);
@ -1335,7 +1336,7 @@ void Guild::HandleSetMOTD(WorldSession* session, std::string const& motd)
sScriptMgr->OnGuildMOTDChanged(this, motd);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MOTD);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_MOTD);
stmt->setString(0, motd);
stmt->setUInt32(1, m_id);
CharacterDatabase.Execute(stmt);
@ -1356,7 +1357,7 @@ void Guild::HandleSetInfo(WorldSession* session, std::string const& info)
sScriptMgr->OnGuildInfoChanged(this, info);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_INFO);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_INFO);
stmt->setString(0, info);
stmt->setUInt32(1, m_id);
CharacterDatabase.Execute(stmt);
@ -1684,7 +1685,7 @@ void Guild::HandleRemoveRank(WorldSession* session, uint8 rankId)
return;
// Delete bank rights for rank
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_RIGHTS_FOR_RANK);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_RIGHTS_FOR_RANK);
stmt->setUInt32(0, m_id);
stmt->setUInt8(1, rankId);
CharacterDatabase.Execute(stmt);
@ -1706,7 +1707,7 @@ void Guild::HandleMemberDepositMoney(WorldSession* session, uint32 amount)
// Call script after validation and before money transfer.
sScriptMgr->OnGuildMemberDepositMoney(this, player, amount);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
_ModifyBankMoney(trans, amount, true);
player->ModifyMoney(-int32(amount));
@ -1743,7 +1744,7 @@ bool Guild::HandleMemberWithdrawMoney(WorldSession* session, uint32 amount, bool
// Call script after validation and before money transfer.
sScriptMgr->OnGuildMemberWitdrawMoney(this, player, amount, repair);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
// Add money to player (if required)
if (!repair)
{
@ -2068,7 +2069,7 @@ bool Guild::Validate()
}
else
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
rankInfo->CreateMissingTabsIfNeeded(_GetPurchasedTabsSize(), trans, true);
CharacterDatabase.CommitTransaction(trans);
}
@ -2215,7 +2216,7 @@ bool Guild::AddMember(ObjectGuid guid, uint8 rankId)
bool ok = false;
// xinef: zomg! sync query
// Player must exist
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_DATA_FOR_GUILD);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_DATA_FOR_GUILD);
stmt->setUInt32(0, guid.GetCounter());
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
{
@ -2240,7 +2241,7 @@ bool Guild::AddMember(ObjectGuid guid, uint8 rankId)
sWorld->UpdateGlobalPlayerGuild(guid.GetCounter(), m_id);
}
SQLTransaction trans(nullptr);
CharacterDatabaseTransaction trans(nullptr);
member->SaveToDB(trans);
_UpdateAccountsNumber();
@ -2378,9 +2379,9 @@ void Guild::_CreateNewBankTab()
uint8 tabId = _GetPurchasedTabsSize(); // Next free id
m_bankTabs.push_back(new BankTab(m_id, tabId));
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_TAB);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_BANK_TAB);
stmt->setUInt32(0, m_id);
stmt->setUInt8 (1, tabId);
trans->Append(stmt);
@ -2399,7 +2400,7 @@ void Guild::_CreateNewBankTab()
void Guild::_CreateDefaultGuildRanks(LocaleConstant loc)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_RANKS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_RANKS);
stmt->setUInt32(0, m_id);
CharacterDatabase.Execute(stmt);
@ -2424,7 +2425,7 @@ bool Guild::_CreateRank(std::string const& name, uint32 rights)
RankInfo info(m_id, newRankId, name, rights, 0);
m_ranks.push_back(info);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
info.CreateMissingTabsIfNeeded(_GetPurchasedTabsSize(), trans);
info.SaveToDB(trans);
CharacterDatabase.CommitTransaction(trans);
@ -2456,7 +2457,7 @@ bool Guild::_IsLeader(Player* player) const
return false;
}
void Guild::_DeleteBankItems(SQLTransaction& trans, bool removeItemsFromDB)
void Guild::_DeleteBankItems(CharacterDatabaseTransaction trans, bool removeItemsFromDB)
{
for (uint8 tabId = 0; tabId < _GetPurchasedTabsSize(); ++tabId)
{
@ -2467,7 +2468,7 @@ void Guild::_DeleteBankItems(SQLTransaction& trans, bool removeItemsFromDB)
m_bankTabs.clear();
}
bool Guild::_ModifyBankMoney(SQLTransaction& trans, uint64 amount, bool add)
bool Guild::_ModifyBankMoney(CharacterDatabaseTransaction trans, uint64 amount, bool add)
{
if (add)
m_bankMoney += amount;
@ -2479,7 +2480,7 @@ bool Guild::_ModifyBankMoney(SQLTransaction& trans, uint64 amount, bool add)
m_bankMoney -= amount;
}
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_MONEY);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_BANK_MONEY);
stmt->setUInt64(0, m_bankMoney);
stmt->setUInt32(1, m_id);
trans->Append(stmt);
@ -2494,7 +2495,7 @@ void Guild::_SetLeaderGUID(Member* pLeader)
m_leaderGuid = pLeader->GetGUID();
pLeader->ChangeRank(GR_GUILDMASTER);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_LEADER);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_GUILD_LEADER);
stmt->setUInt32(0, m_leaderGuid.GetCounter());
stmt->setUInt32(1, m_id);
CharacterDatabase.Execute(stmt);
@ -2586,7 +2587,7 @@ inline int32 Guild::_GetMemberRemainingMoney(Member const* member) const
return 0;
}
inline void Guild::_UpdateMemberWithdrawSlots(SQLTransaction& trans, ObjectGuid guid, uint8 tabId)
inline void Guild::_UpdateMemberWithdrawSlots(CharacterDatabaseTransaction trans, ObjectGuid guid, uint8 tabId)
{
if (Member* member = GetMember(guid))
{
@ -2612,7 +2613,7 @@ inline bool Guild::_MemberHasTabRights(ObjectGuid guid, uint8 tabId, uint32 righ
// Add new event log record
inline void Guild::_LogEvent(GuildEventLogTypes eventType, ObjectGuid playerGuid1, ObjectGuid playerGuid2, uint8 newRank)
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
m_eventLog->AddEvent(trans, new EventLogEntry(m_id, m_eventLog->GetNextGUID(), eventType, playerGuid1, playerGuid2, newRank));
CharacterDatabase.CommitTransaction(trans);
@ -2620,7 +2621,7 @@ inline void Guild::_LogEvent(GuildEventLogTypes eventType, ObjectGuid playerGuid
}
// Add new bank event log record
void Guild::_LogBankEvent(SQLTransaction& trans, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid guid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId)
void Guild::_LogBankEvent(CharacterDatabaseTransaction trans, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid guid, uint32 itemOrMoney, uint16 itemStackCount, uint8 destTabId)
{
if (tabId > GUILD_BANK_MAX_TABS)
return;
@ -2648,7 +2649,7 @@ inline Item* Guild::_GetItem(uint8 tabId, uint8 slotId) const
return nullptr;
}
inline void Guild::_RemoveItem(SQLTransaction& trans, uint8 tabId, uint8 slotId)
inline void Guild::_RemoveItem(CharacterDatabaseTransaction trans, uint8 tabId, uint8 slotId)
{
if (BankTab* pTab = GetBankTab(tabId))
pTab->SetItem(trans, slotId, nullptr);
@ -2727,7 +2728,7 @@ bool Guild::_DoItemsMove(MoveItemData* pSrc, MoveItemData* pDest, bool sendError
if (swap)
pSrc->LogAction(pDest);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
// 3. Log bank events
pDest->LogBankEvent(trans, pSrc, pSrcItem->GetCount());
if (swap)

View file

@ -302,7 +302,7 @@ public: // pussywizard: public class Member
void ResetFlags() { m_flags = GUILDMEMBER_STATUS_NONE; }
bool LoadFromDB(Field* fields);
void SaveToDB(SQLTransaction& trans) const;
void SaveToDB(CharacterDatabaseTransaction trans) const;
void WritePacket(WorldPacket& data, bool sendOfficerNote) const;
ObjectGuid GetGUID() const { return m_guid; }
@ -326,7 +326,7 @@ public: // pussywizard: public class Member
inline bool IsRankNotLower(uint8 rankId) const { return m_rankId <= rankId; }
inline bool IsSamePlayer(ObjectGuid guid) const { return m_guid == guid; }
void UpdateBankWithdrawValue(SQLTransaction& trans, uint8 tabId, uint32 amount);
void UpdateBankWithdrawValue(CharacterDatabaseTransaction trans, uint8 tabId, uint32 amount);
int32 GetBankWithdrawValue(uint8 tabId) const;
void ResetValues();
@ -384,7 +384,7 @@ private:
ObjectGuid::LowType GetGUID() const { return m_guid; }
uint64 GetTimestamp() const { return m_timestamp; }
virtual void SaveToDB(SQLTransaction& trans) const = 0;
virtual void SaveToDB(CharacterDatabaseTransaction trans) const = 0;
virtual void WritePacket(WorldPacket& data) const = 0;
protected:
@ -405,7 +405,7 @@ private:
~EventLogEntry() override { }
void SaveToDB(SQLTransaction& trans) const override;
void SaveToDB(CharacterDatabaseTransaction trans) const override;
void WritePacket(WorldPacket& data) const override;
private:
@ -437,7 +437,7 @@ private:
~BankEventLogEntry() override { }
void SaveToDB(SQLTransaction& trans) const override;
void SaveToDB(CharacterDatabaseTransaction trans) const override;
void WritePacket(WorldPacket& data) const override;
private:
@ -465,7 +465,7 @@ private:
// Adds event from DB to collection
void LoadEvent(LogEntry* entry);
// Adds new event to collection and saves it to DB
void AddEvent(SQLTransaction& trans, LogEntry* entry);
void AddEvent(CharacterDatabaseTransaction trans, LogEntry* entry);
// Writes information about all events to packet
void WritePacket(WorldPacket& data) const;
uint32 GetNextGUID();
@ -488,7 +488,7 @@ private:
m_bankMoneyPerDay(rankId != GR_GUILDMASTER ? money : GUILD_WITHDRAW_MONEY_UNLIMITED) { }
void LoadFromDB(Field* fields);
void SaveToDB(SQLTransaction& trans) const;
void SaveToDB(CharacterDatabaseTransaction trans) const;
void WritePacket(WorldPacket& data) const;
uint8 GetId() const { return m_rankId; }
@ -514,7 +514,7 @@ private:
}
void SetBankTabSlotsAndRights(GuildBankRightsAndSlots rightsAndSlots, bool saveToDB);
void CreateMissingTabsIfNeeded(uint8 ranks, SQLTransaction& trans, bool logOnCreate = false);
void CreateMissingTabsIfNeeded(uint8 ranks, CharacterDatabaseTransaction trans, bool logOnCreate = false);
private:
uint32 m_guildId;
@ -536,7 +536,7 @@ private:
void LoadFromDB(Field* fields);
bool LoadItemFromDB(Field* fields);
void Delete(SQLTransaction& trans, bool removeItemsFromDB = false);
void Delete(CharacterDatabaseTransaction trans, bool removeItemsFromDB = false);
void WritePacket(WorldPacket& data) const;
bool WriteSlotPacket(WorldPacket& data, uint8 slotId, bool ignoreEmpty = true) const;
@ -551,7 +551,7 @@ private:
void SendText(const Guild* guild, WorldSession* session) const;
inline Item* GetItem(uint8 slotId) const { return slotId < GUILD_BANK_MAX_SLOTS ? m_items[slotId] : nullptr; }
bool SetItem(SQLTransaction& trans, uint8 slotId, Item* pItem);
bool SetItem(CharacterDatabaseTransaction trans, uint8 slotId, Item* pItem);
private:
uint32 m_guildId;
@ -585,11 +585,11 @@ private:
// Clones stored item
bool CloneItem(uint32 count);
// Remove item from container (if splited update items fields)
virtual void RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount = 0) = 0;
virtual void RemoveItem(CharacterDatabaseTransaction trans, MoveItemData* pOther, uint32 splitedAmount = 0) = 0;
// Saves item to container
virtual Item* StoreItem(SQLTransaction& trans, Item* pItem) = 0;
virtual Item* StoreItem(CharacterDatabaseTransaction trans, Item* pItem) = 0;
// Log bank event
virtual void LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const = 0;
virtual void LogBankEvent(CharacterDatabaseTransaction trans, MoveItemData* pFrom, uint32 count) const = 0;
// Log GM action
virtual void LogAction(MoveItemData* pFrom) const;
// Copy slots id from position vector
@ -619,9 +619,9 @@ private:
bool IsBank() const override { return false; }
bool InitItem() override;
void RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount = 0) override;
Item* StoreItem(SQLTransaction& trans, Item* pItem) override;
void LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const override;
void RemoveItem(CharacterDatabaseTransaction trans, MoveItemData* pOther, uint32 splitedAmount = 0) override;
Item* StoreItem(CharacterDatabaseTransaction trans, Item* pItem) override;
void LogBankEvent(CharacterDatabaseTransaction trans, MoveItemData* pFrom, uint32 count) const override;
protected:
InventoryResult CanStore(Item* pItem, bool swap) override;
};
@ -636,16 +636,16 @@ private:
bool InitItem() override;
bool HasStoreRights(MoveItemData* pOther) const override;
bool HasWithdrawRights(MoveItemData* pOther) const override;
void RemoveItem(SQLTransaction& trans, MoveItemData* pOther, uint32 splitedAmount) override;
Item* StoreItem(SQLTransaction& trans, Item* pItem) override;
void LogBankEvent(SQLTransaction& trans, MoveItemData* pFrom, uint32 count) const override;
void RemoveItem(CharacterDatabaseTransaction trans, MoveItemData* pOther, uint32 splitedAmount) override;
Item* StoreItem(CharacterDatabaseTransaction trans, Item* pItem) override;
void LogBankEvent(CharacterDatabaseTransaction trans, MoveItemData* pFrom, uint32 count) const override;
void LogAction(MoveItemData* pFrom) const override;
protected:
InventoryResult CanStore(Item* pItem, bool swap) override;
private:
Item* _StoreItem(SQLTransaction& trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const;
Item* _StoreItem(CharacterDatabaseTransaction trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const;
bool _ReserveSpace(uint8 slotId, Item* pItem, Item* pItemDest, uint32& count);
void CanStoreItemInTab(Item* pItem, uint8 skipSlotId, bool merge, uint32& count);
};
@ -756,7 +756,7 @@ public:
void ResetTimes();
[[nodiscard]] bool ModifyBankMoney(SQLTransaction& trans, const uint64& amount, bool add) { return _ModifyBankMoney(trans, amount, add); }
[[nodiscard]] bool ModifyBankMoney(CharacterDatabaseTransaction trans, const uint64& amount, bool add) { return _ModifyBankMoney(trans, amount, add); }
[[nodiscard]] uint32 GetMemberSize() const { return m_members.size(); }
protected:
@ -799,7 +799,7 @@ private:
inline void _DeleteMemberFromDB(ObjectGuid::LowType lowguid) const
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBER);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GUILD_MEMBER);
stmt->setUInt32(0, lowguid);
CharacterDatabase.Execute(stmt);
}
@ -815,8 +815,8 @@ private:
// Update account number when member added/removed from guild
void _UpdateAccountsNumber();
bool _IsLeader(Player* player) const;
void _DeleteBankItems(SQLTransaction& trans, bool removeItemsFromDB = false);
bool _ModifyBankMoney(SQLTransaction& trans, uint64 amount, bool add);
void _DeleteBankItems(CharacterDatabaseTransaction trans, bool removeItemsFromDB = false);
bool _ModifyBankMoney(CharacterDatabaseTransaction trans, uint64 amount, bool add);
void _SetLeaderGUID(Member* pLeader);
void _SetRankBankMoneyPerDay(uint8 rankId, uint32 moneyPerDay);
@ -829,14 +829,14 @@ private:
int32 _GetMemberRemainingSlots(Member const* member, uint8 tabId) const;
int32 _GetMemberRemainingMoney(Member const* member) const;
void _UpdateMemberWithdrawSlots(SQLTransaction& trans, ObjectGuid guid, uint8 tabId);
void _UpdateMemberWithdrawSlots(CharacterDatabaseTransaction trans, ObjectGuid guid, uint8 tabId);
bool _MemberHasTabRights(ObjectGuid guid, uint8 tabId, uint32 rights) const;
void _LogEvent(GuildEventLogTypes eventType, ObjectGuid playerGuid1, ObjectGuid playerGuid2 = ObjectGuid::Empty, uint8 newRank = 0);
void _LogBankEvent(SQLTransaction& trans, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid playerGuid, uint32 itemOrMoney, uint16 itemStackCount = 0, uint8 destTabId = 0);
void _LogBankEvent(CharacterDatabaseTransaction trans, GuildBankEventLogTypes eventType, uint8 tabId, ObjectGuid playerGuid, uint32 itemOrMoney, uint16 itemStackCount = 0, uint8 destTabId = 0);
Item* _GetItem(uint8 tabId, uint8 slotId) const;
void _RemoveItem(SQLTransaction& trans, uint8 tabId, uint8 slotId);
void _RemoveItem(CharacterDatabaseTransaction trans, uint8 tabId, uint8 slotId);
void _MoveItems(MoveItemData* pSrc, MoveItemData* pDest, uint32 splitedAmount);
bool _DoItemsMove(MoveItemData* pSrc, MoveItemData* pDest, bool sendError, uint32 splitedAmount = 0);

View file

@ -300,7 +300,7 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData)
_player->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
item->DeleteFromInventoryDB(trans);
item->SaveToDB(trans);
AH->SaveToDB(trans);
@ -348,7 +348,7 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData)
{
_player->MoveItemFromInventory(item2->GetBagSlot(), item2->GetSlot(), true);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
item2->DeleteFromInventoryDB(trans);
item2->DeleteFromDB(trans);
CharacterDatabase.CommitTransaction(trans);
@ -361,13 +361,13 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData)
_player->ItemRemovedQuestCheck(item2->GetEntry(), count[j]);
item2->SendUpdateToPlayer(_player);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
item2->SaveToDB(trans);
CharacterDatabase.CommitTransaction(trans);
}
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
newItem->SaveToDB(trans);
AH->SaveToDB(trans);
_player->SaveInventoryAndGoldToDB(trans);
@ -446,7 +446,7 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData)
return;
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
if (price < auction->buyout || auction->buyout == 0)
{
@ -468,7 +468,7 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData)
auction->bid = price;
GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_AUCTION_BID);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_AUCTION_BID);
stmt->setUInt32(0, auction->bidder.GetCounter());
stmt->setUInt32(1, auction->bid);
stmt->setUInt32(2, auction->Id);
@ -533,7 +533,7 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket& recvData)
AuctionEntry* auction = auctionHouse->GetAuction(auctionId);
Player* player = GetPlayer();
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
if (auction && auction->owner == player->GetGUID())
{
Item* pItem = sAuctionMgr->GetAItem(auction->item_guid);

View file

@ -464,7 +464,7 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket& recvData)
{
if (sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS))
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt8(1, BG_DESERTION_TYPE_LEAVE_QUEUE);
CharacterDatabase.Execute(stmt);

View file

@ -329,7 +329,7 @@ void WorldSession::HandleCalendarAddEvent(WorldPacket& recvData)
throw;
}
SQLTransaction trans;
CharacterDatabaseTransaction trans;
if (inviteCount > 1)
trans = CharacterDatabase.BeginTransaction();
@ -489,7 +489,7 @@ void WorldSession::HandleCalendarCopyEvent(WorldPacket& recvData)
sCalendarMgr->AddEvent(newEvent, CALENDAR_SENDTYPE_COPY);
CalendarInviteStore invites = sCalendarMgr->GetEventInvites(eventId);
SQLTransaction trans;
CharacterDatabaseTransaction trans;
if (invites.size() > 1)
trans = CharacterDatabase.BeginTransaction();
@ -794,7 +794,7 @@ void WorldSession::HandleSetSavedInstanceExtend(WorldPacket& recvData)
instanceBind->extended = (bool)toggleExtendOn;
// update in db
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE_EXTENDED);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE_EXTENDED);
stmt->setUInt8(0, toggleExtendOn ? 1 : 0);
stmt->setUInt32(1, GetPlayer()->GetGUID().GetCounter());
stmt->setUInt32(2, instanceBind->save->GetInstanceId());

File diff suppressed because it is too large Load diff

View file

@ -775,7 +775,7 @@ void WorldSession::HandleBuybackItem(WorldPacket& recvData)
{
if (sWorld->getBoolConfig(CONFIG_ITEMDELETE_VENDOR))
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RECOVERY_ITEM);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_RECOVERY_ITEM);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt32(1, pItem->GetEntry());
stmt->setUInt32(2, pItem->GetCount());
@ -1308,9 +1308,9 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData)
return;
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_GIFT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_GIFT);
stmt->setUInt32(0, item->GetOwnerGUID().GetCounter());
stmt->setUInt32(1, item->GetGUID().GetCounter());
stmt->setUInt32(2, item->GetEntry());
@ -1664,7 +1664,7 @@ bool WorldSession::recoveryItem(Item* pItem)
&& pItem->GetTemplate()->Quality >= sWorld->getIntConfig(CONFIG_ITEMDELETE_QUALITY)
&& pItem->GetTemplate()->ItemLevel >= sWorld->getIntConfig(CONFIG_ITEMDELETE_ITEM_LEVEL))
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_RECOVERY_ITEM);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_RECOVERY_ITEM);
stmt->setUInt32(0, pItem->GetOwnerGUID().GetCounter());
stmt->setUInt32(1, pItem->GetTemplate()->ItemId);

View file

@ -268,7 +268,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData)
MailDraft draft(subject, body);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
if (items_count > 0 || money > 0)
{
@ -393,9 +393,9 @@ void WorldSession::HandleMailReturnToSender(WorldPacket& recvData)
}
//we can return mail now
//so firstly delete the old one
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_BY_ID);
stmt->setUInt32(0, mailId);
trans->Append(stmt);
@ -484,7 +484,7 @@ void WorldSession::HandleMailTakeItem(WorldPacket& recvData)
uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, it, false);
if (msg == EQUIP_ERR_OK)
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
m->RemoveItem(itemLowGuid);
m->removedItems.push_back(itemLowGuid);
@ -570,7 +570,7 @@ void WorldSession::HandleMailTakeMoney(WorldPacket& recvData)
player->SendMailResult(mailId, MAIL_MONEY_TAKEN, MAIL_OK);
// save money and mail to prevent cheating
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
player->SaveGoldToDB(trans);
player->_SaveMail(trans);
CharacterDatabase.CommitTransaction(trans);

View file

@ -615,7 +615,7 @@ void WorldSession::HandleBugOpcode(WorldPacket& recv_data)
LOG_DEBUG("network", "%s", type.c_str());
LOG_DEBUG("network", "%s", content.c_str());
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_BUG_REPORT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_BUG_REPORT);
stmt->setString(0, type);
stmt->setString(1, content);
@ -1138,7 +1138,7 @@ void WorldSession::HandleWhoisOpcode(WorldPacket& recv_data)
uint32 accid = player->GetSession()->GetAccountId();
PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_WHOIS);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_WHOIS);
stmt->setUInt32(0, accid);

View file

@ -500,17 +500,16 @@ void WorldSession::HandleListStabledPetsOpcode(WorldPacket& recvData)
void WorldSession::SendStablePet(ObjectGuid guid)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS_DETAIL);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS_DETAIL);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
_sendStabledPetCallback.SetParam(guid);
_sendStabledPetCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
_queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::SendStablePetCallback, this, guid, std::placeholders::_1)));
}
void WorldSession::SendStablePetCallback(PreparedQueryResult result, ObjectGuid guid)
void WorldSession::SendStablePetCallback(ObjectGuid guid, PreparedQueryResult result)
{
if (!GetPlayer())
return;
@ -518,16 +517,12 @@ void WorldSession::SendStablePetCallback(PreparedQueryResult result, ObjectGuid
LOG_DEBUG("network", "WORLD: Recv MSG_LIST_STABLED_PETS Send.");
WorldPacket data(MSG_LIST_STABLED_PETS, 200); // guess size
data << guid;
Pet* pet = _player->GetPet();
size_t wpos = data.wpos();
data << uint8(0); // place holder for slot show number
data << uint8(GetPlayer()->m_stableSlots);
Pet* pet = _player->GetPet();
uint8 num = 0; // counter for place holder
// not let move dead pet in slot
@ -542,11 +537,11 @@ void WorldSession::SendStablePetCallback(PreparedQueryResult result, ObjectGuid
}
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_SYNS);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt8(1, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
if (PreparedQueryResult _result = CharacterDatabase.AsyncQuery(stmt))
if (PreparedQueryResult _result = CharacterDatabase.Query(stmt))
{
Field* fields = _result->Fetch();
@ -630,13 +625,13 @@ void WorldSession::HandleStablePet(WorldPacket& recvData)
}
}
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOTS);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt8(1, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(2, PET_SAVE_LAST_STABLE_SLOT);
_stablePetCallback = CharacterDatabase.AsyncQuery(stmt);
_queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStablePetCallback, this, std::placeholders::_1)));
}
void WorldSession::HandleStablePetCallback(PreparedQueryResult result)
@ -672,16 +667,11 @@ void WorldSession::HandleStablePetCallback(PreparedQueryResult result)
return;
}
// change pet slot directly in database
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
stmt->setUInt8(0, freeSlot);
stmt->setUInt32(1, _player->GetGUID().GetCounter());
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
trans->Append(stmt);
CharacterDatabase.CommitTransaction(trans);
CharacterDatabase.Execute(stmt);
_player->SetTemporaryUnsummonedPetNumber(0);
SendStableResult(STABLE_SUCCESS_STABLE);
@ -709,18 +699,17 @@ void WorldSession::HandleUnstablePet(WorldPacket& recvData)
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_ENTRY);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_ENTRY);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt32(1, petnumber);
stmt->setUInt8(2, PET_SAVE_FIRST_STABLE_SLOT);
stmt->setUInt8(3, PET_SAVE_LAST_STABLE_SLOT);
_unstablePetCallback.SetParam(petnumber);
_unstablePetCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
_queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleUnstablePetCallback, this, petnumber, std::placeholders::_1)));
}
void WorldSession::HandleUnstablePetCallback(PreparedQueryResult result, uint32 petId)
void WorldSession::HandleUnstablePetCallback(uint32 petId, PreparedQueryResult result)
{
if (!GetPlayer())
return;
@ -772,16 +761,13 @@ void WorldSession::HandleUnstablePetCallback(PreparedQueryResult result, uint32
SendStableResult(STABLE_ERR_STABLE);
return;
}
// change pet slot directly in database
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
stmt->setUInt8(0, slot);
stmt->setUInt32(1, _player->GetGUID().GetCounter());
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
trans->Append(stmt);
CharacterDatabase.Execute(stmt);
CharacterDatabase.CommitTransaction(trans);
_player->SetTemporaryUnsummonedPetNumber(0);
}
@ -870,16 +856,15 @@ void WorldSession::HandleStableSwapPet(WorldPacket& recvData)
}
// Find swapped pet slot in stable
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SLOT_BY_ID);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt32(1, petId);
_stableSwapCallback.SetParam(petId);
_stableSwapCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
_queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt).WithPreparedCallback(std::bind(&WorldSession::HandleStableSwapPetCallback, this, petId, std::placeholders::_1)));
}
void WorldSession::HandleStableSwapPetCallback(PreparedQueryResult result, uint32 petId)
void WorldSession::HandleStableSwapPetCallback(uint32 petId, PreparedQueryResult result)
{
if (!GetPlayer())
return;
@ -891,7 +876,6 @@ void WorldSession::HandleStableSwapPetCallback(PreparedQueryResult result, uint3
}
Field* fields = result->Fetch();
uint32 slot = fields[0].GetUInt8();
uint32 petEntry = fields[1].GetUInt32();
@ -919,16 +903,12 @@ void WorldSession::HandleStableSwapPetCallback(PreparedQueryResult result, uint3
_player->RemovePet(pet, pet->IsAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
else if (_player->IsPetDismissed() || _player->GetTemporaryUnsummonedPetNumber())
{
// change pet slot directly in database
SQLTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT);
stmt->setUInt8(0, slot);
stmt->setUInt32(1, _player->GetGUID().GetCounter());
stmt->setUInt8(2, uint8(_player->GetTemporaryUnsummonedPetNumber() ? PET_SAVE_AS_CURRENT : PET_SAVE_NOT_IN_SLOT));
trans->Append(stmt);
CharacterDatabase.Execute(stmt);
CharacterDatabase.CommitTransaction(trans);
_player->SetTemporaryUnsummonedPetNumber(0);
}

View file

@ -22,61 +22,75 @@
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "QueryHolder.h"
class LoadPetFromDBQueryHolder : public SQLQueryHolder
class LoadPetFromDBQueryHolder : public CharacterDatabaseQueryHolder
{
private:
const uint32 m_petNumber;
const bool m_current;
const uint32 m_diffTime;
const std::string m_actionBar;
const uint32 m_savedHealth;
const uint32 m_savedMana;
public:
LoadPetFromDBQueryHolder(uint32 petNumber, bool current, uint32 diffTime, std::string&& actionBar, uint32 health, uint32 mana)
: m_petNumber(petNumber), m_current(current), m_diffTime(diffTime), m_actionBar(std::move(actionBar)),
m_savedHealth(health), m_savedMana(mana) { }
: _petNumber(petNumber),
_current(current),
_diffTime(diffTime),
_actionBar(std::move(actionBar)),
_savedHealth(health),
_savedMana(mana) { }
uint32 GetPetNumber() const { return _petNumber; }
uint32 GetDiffTime() const { return _diffTime; }
bool GetCurrent() const { return _current; }
uint32 GetSavedHealth() const { return _savedHealth; }
uint32 GetSavedMana() const { return _savedMana; }
std::string GetActionBar() const { return _actionBar; }
uint32 GetPetNumber() const { return m_petNumber; }
uint32 GetDiffTime() const { return m_diffTime; }
bool GetCurrent() const { return m_current; }
uint32 GetSavedHealth() const { return m_savedHealth; }
uint32 GetSavedMana() const { return m_savedMana; }
std::string GetActionBar() const { return m_actionBar; }
bool Initialize();
private:
enum
{
AURAS,
SPELLS,
COOLDOWNS,
MAX
};
const uint32 _petNumber;
const bool _current;
const uint32 _diffTime;
const std::string _actionBar;
const uint32 _savedHealth;
const uint32 _savedMana;
};
bool LoadPetFromDBQueryHolder::Initialize()
{
SetSize(MAX_PET_LOAD_QUERY);
SetSize(MAX);
bool res = true;
PreparedStatement* stmt = nullptr;
CharacterDatabasePreparedStatement* stmt = nullptr;
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_AURA);
stmt->setUInt32(0, m_petNumber);
res &= SetPreparedQuery(PET_LOAD_QUERY_LOADAURAS, stmt);
stmt->setUInt32(0, _petNumber);
res &= SetPreparedQuery(AURAS, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL);
stmt->setUInt32(0, m_petNumber);
res &= SetPreparedQuery(PET_LOAD_QUERY_LOADSPELLS, stmt);
stmt->setUInt32(0, _petNumber);
res &= SetPreparedQuery(SPELLS, stmt);
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL_COOLDOWN);
stmt->setUInt32(0, m_petNumber);
res &= SetPreparedQuery(PET_LOAD_QUERY_LOADSPELLCOOLDOWN, stmt);
stmt->setUInt32(0, _petNumber);
res &= SetPreparedQuery(COOLDOWNS, stmt);
return res;
}
uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result, uint8 asynchLoadType)
{
if (!GetPlayer() || GetPlayer()->GetPet() || GetPlayer()->GetVehicle() || GetPlayer()->IsSpectator())
return PET_LOAD_ERROR;
if (!result)
return PET_LOAD_NO_RESULT;
if (!GetPlayer() || GetPlayer()->GetPet() || GetPlayer()->GetVehicle() || GetPlayer()->IsSpectator())
return PET_LOAD_ERROR;
Field* fields = result->Fetch();
// Xinef: this can happen if fetch is called twice, impossibru.
@ -135,11 +149,10 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result,
return PET_LOAD_ERROR;
}
LoadPetFromDBQueryHolder* holder = new LoadPetFromDBQueryHolder(pet_number, current, uint32(time(nullptr) - fields[14].GetUInt32()), fields[13].GetString(), savedhealth, savedmana);
std::shared_ptr<LoadPetFromDBQueryHolder> holder = std::make_shared<LoadPetFromDBQueryHolder>(pet_number, current, uint32(time(nullptr) - fields[14].GetUInt32()), fields[13].GetString(), savedhealth, savedmana);
if (!holder->Initialize())
{
delete pet;
delete holder;
return PET_LOAD_ERROR;
}
@ -150,7 +163,6 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result,
LOG_ERROR("network.opcode", "Pet (%s, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",
pet->GetGUID().ToString().c_str(), pet->GetEntry(), pet->GetPositionX(), pet->GetPositionY());
delete pet;
delete holder;
return PET_LOAD_ERROR;
}
@ -164,7 +176,6 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result,
{
map->AddToMap(pet->ToCreature(), true);
pet->SetLoading(false); // xinef, mine
delete holder;
return PET_LOAD_OK;
}
@ -172,11 +183,12 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result,
pet->GetCharmInfo()->SetPetNumber(pet_number, pet->IsPermanentPetFor(owner)); // Show pet details tab (Shift+P) only for hunter pets, demons or undead
else
pet->GetCharmInfo()->SetPetNumber(pet_number, false);
pet->SetDisplayId(fields[3].GetUInt32());
pet->SetNativeDisplayId(fields[3].GetUInt32());
uint32 petlevel = fields[4].GetUInt16();
pet->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
pet->SetName(fields[8].GetString());
uint32 petlevel = fields[4].GetUInt16();
switch (pet->getPetType())
{
@ -195,7 +207,6 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result,
pet->SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); // class = warrior, gender = none, power = focus
pet->SetSheath(SHEATH_STATE_MELEE);
pet->SetByteFlag(UNIT_FIELD_BYTES_2, 2, fields[9].GetBool() ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED);
pet->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
// this enables popup window (pet abandon, cancel)
pet->SetMaxPower(POWER_HAPPINESS, pet->GetCreatePowers(POWER_HAPPINESS));
@ -214,9 +225,7 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result,
pet->InitStatsForLevel(petlevel);
pet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32());
pet->SynchronizeLevelWithOwner();
pet->SetReactState(ReactStates(fields[6].GetUInt8()));
pet->SetCanModifyStats(true);
@ -226,9 +235,9 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result,
// PET_SAVE_NOT_IN_SLOT(100) = not stable slot (summoning))
if (petSlot)
{
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UDP_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID);
stmt->setUInt8(0, uint8(PET_SAVE_NOT_IN_SLOT));
stmt->setUInt32(1, owner->GetGUID().GetCounter());
stmt->setUInt8(2, uint8(PET_SAVE_AS_CURRENT));
@ -261,10 +270,12 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result,
// do it as early as possible!
pet->InitTalentForLevel(); // set original talents points before spell loading
if (!is_temporary_summoned)
pet->GetCharmInfo()->InitPetActionBar();
map->AddToMap(pet->ToCreature(), true);
if (pet->getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current
pet->SetPower(POWER_MANA, pet->GetMaxPower(POWER_MANA));
else
@ -275,20 +286,15 @@ uint8 WorldSession::HandleLoadPetFromDBFirstCallback(PreparedQueryResult result,
pet->SetAsynchLoadType(asynchLoadType);
// xinef: clear any old result
if (_loadPetFromDBSecondCallback.ready())
AddQueryHolderCallback(CharacterDatabase.DelayQueryHolder(holder)).AfterComplete([this](SQLQueryHolderBase const& holder)
{
SQLQueryHolder* param;
_loadPetFromDBSecondCallback.get(param);
delete param;
}
_loadPetFromDBSecondCallback.cancel();
HandleLoadPetFromDBSecondCallback(static_cast<LoadPetFromDBQueryHolder const&>(holder));
});
_loadPetFromDBSecondCallback = CharacterDatabase.DelayQueryHolder((SQLQueryHolder*)holder);
return PET_LOAD_OK;
}
void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder* holder)
void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder const& holder)
{
if (!GetPlayer())
return;
@ -298,8 +304,8 @@ void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder* h
if (!pet)
return;
pet->_LoadAuras(holder->GetPreparedResult(PET_LOAD_QUERY_LOADAURAS), holder->GetDiffTime());
bool current = holder->GetCurrent();
pet->_LoadAuras(holder.GetPreparedResult(PET_LOAD_QUERY_LOADAURAS), holder.GetDiffTime());
bool current = holder.GetCurrent();
uint32 summon_spell_id = pet->GetUInt32Value(UNIT_CREATED_BY_SPELL);
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(summon_spell_id); // CANT BE nullptr
bool is_temporary_summoned = spellInfo && spellInfo->GetDuration() > 0;
@ -307,26 +313,26 @@ void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder* h
// load action bar, if data broken will fill later by default spells.
if (!is_temporary_summoned)
{
pet->_LoadSpells(holder->GetPreparedResult(PET_LOAD_QUERY_LOADSPELLS));
pet->_LoadSpells(holder.GetPreparedResult(PET_LOAD_QUERY_LOADSPELLS));
pet->InitTalentForLevel(); // re-init to check talent count
pet->_LoadSpellCooldowns(holder->GetPreparedResult(PET_LOAD_QUERY_LOADSPELLCOOLDOWN));
pet->_LoadSpellCooldowns(holder.GetPreparedResult(PET_LOAD_QUERY_LOADSPELLCOOLDOWN));
pet->LearnPetPassives();
pet->InitLevelupSpellsForLevel();
pet->CastPetAuras(current);
pet->GetCharmInfo()->LoadPetActionBar(holder->GetActionBar()); // action bar stored in already read string
pet->GetCharmInfo()->LoadPetActionBar(holder.GetActionBar()); // action bar stored in already read string
}
pet->CleanupActionBar(); // remove unknown spells from action bar after load
owner->PetSpellInitialize();
owner->SendTalentsInfoData(true);
if (owner->GetGroup())
owner->SetGroupUpdateFlag(GROUP_UPDATE_PET);
//set last used pet number (for use in BG's)
if (owner->GetTypeId() == TYPEID_PLAYER && pet->isControlled() && !pet->isTemporarySummoned() && (pet->getPetType() == SUMMON_PET || pet->getPetType() == HUNTER_PET))
{
owner->ToPlayer()->SetLastPetNumber(holder->GetPetNumber());
owner->ToPlayer()->SetLastPetNumber(holder.GetPetNumber());
owner->SetLastPetSpell(pet->GetUInt32Value(UNIT_CREATED_BY_SPELL));
}
@ -337,12 +343,12 @@ void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder* h
}
else
{
if (!holder->GetSavedHealth() && pet->getPetType() == HUNTER_PET && pet->GetAsynchLoadType() != PET_LOAD_SUMMON_DEAD_PET)
if (!holder.GetSavedHealth() && pet->getPetType() == HUNTER_PET && pet->GetAsynchLoadType() != PET_LOAD_SUMMON_DEAD_PET)
pet->setDeathState(JUST_DIED);
else
{
pet->SetHealth(holder->GetSavedHealth() > pet->GetMaxHealth() ? pet->GetMaxHealth() : holder->GetSavedHealth());
pet->SetPower(POWER_MANA, holder->GetSavedMana() > pet->GetMaxPower(POWER_MANA) ? pet->GetMaxPower(POWER_MANA) : holder->GetSavedMana());
pet->SetHealth(holder.GetSavedHealth() > pet->GetMaxHealth() ? pet->GetMaxHealth() : holder.GetSavedHealth());
pet->SetPower(POWER_MANA, holder.GetSavedMana() > pet->GetMaxPower(POWER_MANA) ? pet->GetMaxPower(POWER_MANA) : holder.GetSavedMana());
}
}
@ -357,7 +363,6 @@ void WorldSession::HandleLoadPetFromDBSecondCallback(LoadPetFromDBQueryHolder* h
}
pet->HandleAsynchLoadSucceed();
return;
}
void WorldSession::HandleDismissCritter(WorldPacket& recvData)
@ -1185,12 +1190,12 @@ void WorldSession::HandlePetRename(WorldPacket& recvData)
}
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
if (isdeclined)
{
if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED))
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME);
stmt->setUInt32(0, pet->GetCharmInfo()->GetPetNumber());
trans->Append(stmt);
@ -1204,7 +1209,7 @@ void WorldSession::HandlePetRename(WorldPacket& recvData)
}
}
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_NAME);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_PET_NAME);
stmt->setString(0, name);
stmt->setUInt32(1, _player->GetGUID().GetCounter());
stmt->setUInt32(2, pet->GetCharmInfo()->GetPetNumber());

View file

@ -185,21 +185,22 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket& recvData)
Petition const* petition = sPetitionMgr->GetPetitionByOwnerWithType(_player->GetGUID(), type);
CharacterDatabase.EscapeString(name);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
if (petition)
{
LOG_DEBUG("network", "Invalid petition: %s", petition->petitionGuid.ToString().c_str());
trans->PAppend("DELETE FROM petition WHERE petitionguid = %u", petition->petitionGuid);
trans->PAppend("DELETE FROM petition_sign WHERE petitionguid = %u", petition->petitionGuid);
trans->PAppend("DELETE FROM petition WHERE petitionguid = %u", petition->petitionGuid.GetCounter());
trans->PAppend("DELETE FROM petition_sign WHERE petitionguid = %u", petition->petitionGuid.GetCounter());
// xinef: clear petition store
sPetitionMgr->RemovePetition(petition->petitionGuid);
}
// xinef: petition pointer is invalid from now on
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION);
stmt->setUInt32(0, _player->GetGUID().GetCounter());
stmt->setUInt32(1, charter->GetGUID().GetCounter());
stmt->setString(2, name);
@ -359,7 +360,7 @@ void WorldSession::HandlePetitionRenameOpcode(WorldPacket& recvData)
}
}
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PETITION_NAME);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PETITION_NAME);
stmt->setString(0, newName);
stmt->setUInt32(1, petitionGuid.GetCounter());
@ -481,7 +482,7 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData)
return;
}
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION_SIGNATURE);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION_SIGNATURE);
stmt->setUInt32(0, petition->ownerGuid.GetCounter());
stmt->setUInt32(1, petitionGuid.GetCounter());
@ -772,9 +773,9 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket& recvData)
}
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_GUID);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_GUID);
stmt->setUInt32(0, petitionGuid.GetCounter());
trans->Append(stmt);

View file

@ -227,20 +227,18 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_WRAPPED))// wrapped?
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM);
stmt->setUInt32(0, item->GetGUID().GetCounter());
_openWrappedItemCallback.SetFirstParam(bagIndex);
_openWrappedItemCallback.SetSecondParam(slot);
_openWrappedItemCallback.SetThirdParam(item->GetGUID().GetCounter());
_openWrappedItemCallback.SetFutureResult(CharacterDatabase.AsyncQuery(stmt));
_queryProcessor.AddCallback(CharacterDatabase.AsyncQuery(stmt)
.WithPreparedCallback(std::bind(&WorldSession::HandleOpenWrappedItemCallback, this, bagIndex, slot, item->GetGUID().GetCounter(), std::placeholders::_1)));
}
else
{
pUser->SendLoot(item->GetGUID(), LOOT_CORPSE);
}
}
void WorldSession::HandleOpenWrappedItemCallback(PreparedQueryResult result, uint8 bagIndex, uint8 slot, ObjectGuid::LowType itemLowGUID)
void WorldSession::HandleOpenWrappedItemCallback(uint8 bagIndex, uint8 slot, ObjectGuid::LowType itemLowGUID, PreparedQueryResult result)
{
if (!GetPlayer())
return;
@ -254,12 +252,12 @@ void WorldSession::HandleOpenWrappedItemCallback(PreparedQueryResult result, uin
if (!result)
{
LOG_ERROR("network.opcode", "Wrapped item %s don't have record in character_gifts table and will deleted", item->GetGUID().ToString().c_str());
LOG_ERROR("network", "Wrapped item %s don't have record in character_gifts table and will deleted", item->GetGUID().ToString().c_str());
GetPlayer()->DestroyItem(item->GetBagSlot(), item->GetSlot(), true);
return;
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
Field* fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
@ -273,7 +271,7 @@ void WorldSession::HandleOpenWrappedItemCallback(PreparedQueryResult result, uin
item->SetState(ITEM_CHANGED, GetPlayer());
GetPlayer()->SaveInventoryAndGoldToDB(trans);
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_GIFT);
stmt->setUInt32(0, item->GetGUID().GetCounter());
trans->Append(stmt);

View file

@ -114,7 +114,7 @@ void WorldSession::HandleGMTicketUpdateOpcode(WorldPacket& recv_data)
GMTicketResponse response = GMTICKET_RESPONSE_UPDATE_ERROR;
if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID()))
{
SQLTransaction trans = SQLTransaction(nullptr);
CharacterDatabaseTransaction trans = CharacterDatabaseTransaction(nullptr);
ticket->SetMessage(message);
ticket->SaveToDB(trans);
@ -175,7 +175,7 @@ void WorldSession::HandleGMSurveySubmit(WorldPacket& recv_data)
recv_data >> mainSurvey;
std::unordered_set<uint32> surveyIds;
SQLTransaction trans = CharacterDatabase.BeginTransaction();
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
// sub_survey1, r1, comment1, sub_survey2, r2, comment2, sub_survey3, r3, comment3, sub_survey4, r4, comment4, sub_survey5, r5, comment5, sub_survey6, r6, comment6, sub_survey7, r7, comment7, sub_survey8, r8, comment8, sub_survey9, r9, comment9, sub_survey10, r10, comment10,
for (uint8 i = 0; i < 10; i++)
{
@ -193,7 +193,7 @@ void WorldSession::HandleGMSurveySubmit(WorldPacket& recv_data)
if (!surveyIds.insert(subSurveyId).second)
continue;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SUBSURVEY);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SUBSURVEY);
stmt->setUInt32(0, nextSurveyID);
stmt->setUInt32(1, subSurveyId);
stmt->setUInt32(2, rank);
@ -204,7 +204,7 @@ void WorldSession::HandleGMSurveySubmit(WorldPacket& recv_data)
std::string comment; // just a guess
recv_data >> comment;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SURVEY);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_GM_SURVEY);
stmt->setUInt32(0, GetPlayer()->GetGUID().GetCounter());
stmt->setUInt32(1, nextSurveyID);
stmt->setUInt32(2, mainSurvey);
@ -227,7 +227,7 @@ void WorldSession::HandleReportLag(WorldPacket& recv_data)
recv_data >> y;
recv_data >> z;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_LAG_REPORT);
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_LAG_REPORT);
stmt->setUInt32(0, GetPlayer()->GetGUID().GetCounter());
stmt->setUInt8 (1, lagType);
stmt->setUInt16(2, mapId);

Some files were not shown because too many files have changed in this diff Show more