feat(Core/Database): port TrinityCore database API (#5611)
This commit is contained in:
parent
2a2e54d8c5
commit
9ac6fddcae
155 changed files with 5818 additions and 4321 deletions
1
conf/dist/config.cmake
vendored
1
conf/dist/config.cmake
vendored
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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!")
|
||||
|
|
|
|||
100
src/common/Threading/ProducerConsumerQueue.h
Normal file
100
src/common/Threading/ProducerConsumerQueue.h
Normal 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
|
||||
51
src/common/Utilities/AsyncCallbackProcessor.h
Normal file
51
src/common/Utilities/AsyncCallbackProcessor.h
Normal 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__
|
||||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
82
src/server/database/Database/DatabaseEnvFwd.h
Normal file
82
src/server/database/Database/DatabaseEnvFwd.h
Normal 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__
|
||||
|
|
@ -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&);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
22
src/server/database/Database/MySQLHacks.h
Normal file
22
src/server/database/Database/MySQLHacks.h
Normal 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__
|
||||
188
src/server/database/Database/MySQLPreparedStatement.cpp
Normal file
188
src/server/database/Database/MySQLPreparedStatement.cpp
Normal 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;
|
||||
}
|
||||
60
src/server/database/Database/MySQLPreparedStatement.h
Normal file
60
src/server/database/Database/MySQLPreparedStatement.h
Normal 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__
|
||||
|
|
@ -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()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
9
src/server/database/Database/MySQLWorkaround.h
Normal file
9
src/server/database/Database/MySQLWorkaround.h
Normal 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>
|
||||
|
|
@ -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";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
209
src/server/database/Database/QueryCallback.cpp
Normal file
209
src/server/database/Database/QueryCallback.cpp
Normal 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;
|
||||
}
|
||||
57
src/server/database/Database/QueryCallback.h
Normal file
57
src/server/database/Database/QueryCallback.h
Normal 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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
433
src/server/database/Updater/DBUpdater.cpp
Normal file
433
src/server/database/Updater/DBUpdater.cpp
Normal 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>;
|
||||
79
src/server/database/Updater/DBUpdater.h
Normal file
79
src/server/database/Updater/DBUpdater.h
Normal 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__
|
||||
433
src/server/database/Updater/UpdateFetcher.cpp
Normal file
433
src/server/database/Updater/UpdateFetcher.cpp
Normal 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();
|
||||
}
|
||||
131
src/server/database/Updater/UpdateFetcher.h
Normal file
131
src/server/database/Updater/UpdateFetcher.h
Normal 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__
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "Battlefield.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "Log.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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); }
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "Common.h"
|
||||
#include "DBCStores.h"
|
||||
#include "GroupMgr.h"
|
||||
#include "Log.h"
|
||||
#include "InstanceSaveMgr.h"
|
||||
#include "World.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue