mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-22 18:15:31 +01:00
Merge remote-tracking branch 'tc/3.3.5' into 4.3.4
Note: additional hand-picked ports from 6.x to fix build Conflicts: sql/updates/world/2016_02_22_00_world.sql sql/updates/world/2016_02_22_01_world.sql sql/updates/world/2016_02_22_02_world.sql sql/updates/world/2016_03_07_00_world.sql src/server/authserver/Realms/RealmList.cpp src/server/authserver/Realms/RealmList.h src/server/authserver/Server/AuthSession.cpp src/server/game/Accounts/AccountMgr.cpp src/server/game/AuctionHouse/AuctionHouseMgr.cpp src/server/game/Chat/Chat.cpp src/server/game/Conditions/ConditionMgr.cpp src/server/game/Conditions/ConditionMgr.h src/server/game/Entities/Player/Player.cpp src/server/game/Handlers/CharacterHandler.cpp src/server/game/Handlers/MiscHandler.cpp src/server/game/Scripting/ScriptLoader.cpp src/server/game/Scripting/ScriptLoader.h src/server/game/Server/WorldSession.cpp src/server/game/Server/WorldSocket.cpp src/server/game/World/World.cpp src/server/game/World/World.h src/server/scripts/CMakeLists.txt src/server/scripts/Commands/cs_gm.cpp src/server/scripts/Commands/cs_misc.cpp src/server/scripts/Commands/cs_rbac.cpp src/server/scripts/Commands/cs_ticket.cpp src/server/scripts/Commands/cs_wp.cpp src/server/scripts/EasternKingdoms/CMakeLists.txt src/server/scripts/EasternKingdoms/zone_burning_steppes.cpp src/server/scripts/EasternKingdoms/zone_stormwind_city.cpp src/server/scripts/Kalimdor/CMakeLists.txt src/server/scripts/Kalimdor/zone_dustwallow_marsh.cpp src/server/scripts/Kalimdor/zone_orgrimmar.cpp src/server/scripts/OutdoorPvP/CMakeLists.txt src/server/scripts/Spells/spell_dk.cpp src/server/scripts/Spells/spell_hunter.cpp src/server/shared/CMakeLists.txt src/server/worldserver/CMakeLists.txt src/server/worldserver/Main.cpp src/tools/mmaps_generator/CMakeLists.txt
This commit is contained in:
@@ -32,7 +32,7 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
|
||||
{
|
||||
bool const updatesEnabledForThis = DBUpdater<T>::IsEnabled(_updateFlags);
|
||||
|
||||
_open.push(std::make_pair([this, name, updatesEnabledForThis, &pool]() -> bool
|
||||
_open.push([this, name, updatesEnabledForThis, &pool]() -> bool
|
||||
{
|
||||
std::string const dbString = sConfigMgr->GetStringDefault(name + "DatabaseInfo", "");
|
||||
if (dbString.empty())
|
||||
@@ -71,12 +71,13 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Add the close operation
|
||||
_close.push([&pool]
|
||||
{
|
||||
pool.Close();
|
||||
});
|
||||
return true;
|
||||
},
|
||||
[&pool]()
|
||||
{
|
||||
pool.Close();
|
||||
}));
|
||||
});
|
||||
|
||||
// Populate and update only if updates are enabled for this pool
|
||||
if (updatesEnabledForThis)
|
||||
@@ -137,38 +138,7 @@ bool DatabaseLoader::Load()
|
||||
|
||||
bool DatabaseLoader::OpenDatabases()
|
||||
{
|
||||
while (!_open.empty())
|
||||
{
|
||||
std::pair<Predicate, std::function<void()>> const load = _open.top();
|
||||
if (load.first())
|
||||
_close.push(load.second);
|
||||
else
|
||||
{
|
||||
// Close all loaded databases
|
||||
while (!_close.empty())
|
||||
{
|
||||
_close.top()();
|
||||
_close.pop();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_open.pop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Processes the elements of the given stack until a predicate returned false.
|
||||
bool DatabaseLoader::Process(std::stack<Predicate>& stack)
|
||||
{
|
||||
while (!stack.empty())
|
||||
{
|
||||
if (!stack.top()())
|
||||
return false;
|
||||
|
||||
stack.pop();
|
||||
}
|
||||
return true;
|
||||
return Process(_open);
|
||||
}
|
||||
|
||||
bool DatabaseLoader::PopulateDatabases()
|
||||
@@ -186,6 +156,27 @@ bool DatabaseLoader::PrepareStatements()
|
||||
return Process(_prepare);
|
||||
}
|
||||
|
||||
bool DatabaseLoader::Process(std::queue<Predicate>& queue)
|
||||
{
|
||||
while (!queue.empty())
|
||||
{
|
||||
if (!queue.front()())
|
||||
{
|
||||
// Close all open databases which have a registered close operation
|
||||
while (!_close.empty())
|
||||
{
|
||||
_close.top()();
|
||||
_close.pop();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
queue.pop();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template
|
||||
DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(DatabaseWorkerPool<LoginDatabaseConnection>& pool, std::string const& name);
|
||||
template
|
||||
|
||||
@@ -21,8 +21,9 @@
|
||||
#include "DatabaseWorkerPool.h"
|
||||
#include "DatabaseEnv.h"
|
||||
|
||||
#include <stack>
|
||||
#include <functional>
|
||||
#include <stack>
|
||||
#include <queue>
|
||||
|
||||
// A helper class to initiate all database worker pools,
|
||||
// handles updating, delays preparing of statements and cleans up on failure.
|
||||
@@ -56,16 +57,18 @@ private:
|
||||
bool PrepareStatements();
|
||||
|
||||
using Predicate = std::function<bool()>;
|
||||
using Closer = std::function<void()>;
|
||||
|
||||
static bool Process(std::stack<Predicate>& stack);
|
||||
// Invokes all functions in the given queue and closes the databases on errors.
|
||||
// Returns false when there was an error.
|
||||
bool Process(std::queue<Predicate>& queue);
|
||||
|
||||
std::string const _logger;
|
||||
bool const _autoSetup;
|
||||
uint32 const _updateFlags;
|
||||
|
||||
std::stack<std::pair<Predicate, std::function<void()>>> _open;
|
||||
std::stack<std::function<void()>> _close;
|
||||
std::stack<Predicate> _populate, _update, _prepare;
|
||||
std::queue<Predicate> _open, _populate, _update, _prepare;
|
||||
std::stack<Closer> _close;
|
||||
};
|
||||
|
||||
#endif // DatabaseLoader_h__
|
||||
|
||||
322
src/server/database/Database/DatabaseWorkerPool.cpp
Normal file
322
src/server/database/Database/DatabaseWorkerPool.cpp
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "DatabaseWorkerPool.h"
|
||||
#include "DatabaseEnv.h"
|
||||
|
||||
#define MIN_MYSQL_SERVER_VERSION 50100u
|
||||
#define MIN_MYSQL_CLIENT_VERSION 50100u
|
||||
|
||||
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, "TrinityCore does not support MySQL versions below 5.1");
|
||||
WPFatal(mysql_get_client_version() == MYSQL_VERSION_ID, "Used MySQL library version (%s) does not match the version used to compile TrinityCore (%s).",
|
||||
mysql_get_client_info(), MYSQL_SERVER_VERSION);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::SetConnectionInfo(std::string const& infoString,
|
||||
uint8 const asyncThreads, uint8 const synchThreads)
|
||||
{
|
||||
_connectionInfo = Trinity::make_unique<MySQLConnectionInfo>(infoString);
|
||||
|
||||
_async_threads = asyncThreads;
|
||||
_synch_threads = synchThreads;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint32 DatabaseWorkerPool<T>::Open()
|
||||
{
|
||||
WPFatal(_connectionInfo.get(), "Connection info was not set!");
|
||||
|
||||
TC_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)
|
||||
{
|
||||
TC_LOG_INFO("sql.driver", "DatabasePool '%s' opened successfully. " SZFMTD
|
||||
" total connections running.", GetDatabaseName(),
|
||||
(_connections[IDX_SYNCH].size() + _connections[IDX_ASYNC].size()));
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::Close()
|
||||
{
|
||||
TC_LOG_INFO("sql.driver", "Closing down DatabasePool '%s'.", GetDatabaseName());
|
||||
|
||||
//! Closes the actualy MySQL connection.
|
||||
_connections[IDX_ASYNC].clear();
|
||||
|
||||
TC_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.
|
||||
_connections[IDX_SYNCH].clear();
|
||||
|
||||
TC_LOG_INFO("sql.driver", "All connections on DatabasePool '%s' closed.", GetDatabaseName());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool DatabaseWorkerPool<T>::PrepareStatements()
|
||||
{
|
||||
for (auto& connections : _connections)
|
||||
for (auto& connection : connections)
|
||||
{
|
||||
connection->LockIfReady();
|
||||
if (!connection->PrepareStatements())
|
||||
{
|
||||
connection->Unlock();
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
connection->Unlock();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
QueryResult DatabaseWorkerPool<T>::Query(const char* sql, T* connection /*= nullptr*/)
|
||||
{
|
||||
if (!connection)
|
||||
connection = GetFreeConnection();
|
||||
|
||||
ResultSet* result = connection->Query(sql);
|
||||
connection->Unlock();
|
||||
if (!result || !result->GetRowCount() || !result->NextRow())
|
||||
{
|
||||
delete result;
|
||||
return QueryResult(NULL);
|
||||
}
|
||||
|
||||
return QueryResult(result);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PreparedQueryResult DatabaseWorkerPool<T>::Query(PreparedStatement* stmt)
|
||||
{
|
||||
auto connection = GetFreeConnection();
|
||||
PreparedResultSet* ret = connection->Query(stmt);
|
||||
connection->Unlock();
|
||||
|
||||
//! Delete proxy-class. Not needed anymore
|
||||
delete stmt;
|
||||
|
||||
if (!ret || !ret->GetRowCount())
|
||||
{
|
||||
delete ret;
|
||||
return PreparedQueryResult(NULL);
|
||||
}
|
||||
|
||||
return PreparedQueryResult(ret);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
QueryResultFuture DatabaseWorkerPool<T>::AsyncQuery(const char* sql)
|
||||
{
|
||||
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 result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PreparedQueryResultFuture DatabaseWorkerPool<T>::AsyncQuery(PreparedStatement* stmt)
|
||||
{
|
||||
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 result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
QueryResultHolderFuture DatabaseWorkerPool<T>::DelayQueryHolder(SQLQueryHolder* holder)
|
||||
{
|
||||
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 result;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::CommitTransaction(SQLTransaction transaction)
|
||||
{
|
||||
#ifdef TRINITY_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:
|
||||
TC_LOG_DEBUG("sql.driver", "Transaction contains 0 queries. Not executing.");
|
||||
return;
|
||||
case 1:
|
||||
TC_LOG_DEBUG("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif // TRINITY_DEBUG
|
||||
|
||||
Enqueue(new TransactionTask(transaction));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::DirectCommitTransaction(SQLTransaction& transaction)
|
||||
{
|
||||
T* connection = GetFreeConnection();
|
||||
int errorCode = connection->ExecuteTransaction(transaction);
|
||||
if (!errorCode)
|
||||
{
|
||||
connection->Unlock(); // OK, operation succesful
|
||||
return;
|
||||
}
|
||||
|
||||
//! Handle MySQL Errno 1213 without extending deadlock to the core itself
|
||||
/// @todo More elegant way
|
||||
if (errorCode == ER_LOCK_DEADLOCK)
|
||||
{
|
||||
uint8 loopBreaker = 5;
|
||||
for (uint8 i = 0; i < loopBreaker; ++i)
|
||||
{
|
||||
if (!connection->ExecuteTransaction(transaction))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//! Clean up now.
|
||||
transaction->Cleanup();
|
||||
|
||||
connection->Unlock();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::EscapeString(std::string& str)
|
||||
{
|
||||
if (str.empty())
|
||||
return;
|
||||
|
||||
char* buf = new char[str.size() * 2 + 1];
|
||||
EscapeString(buf, str.c_str(), 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 Trinity::make_unique<T>(_queue.get(), *_connectionInfo);
|
||||
case IDX_SYNCH:
|
||||
return Trinity::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 (mysql_get_server_version(connection->GetHandle()) < MIN_MYSQL_SERVER_VERSION)
|
||||
{
|
||||
TC_LOG_ERROR("sql.driver", "TrinityCore does not support MySQL versions below 5.1");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_connections[type].push_back(std::move(connection));
|
||||
}
|
||||
}
|
||||
|
||||
// Everything is fine
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T* DatabaseWorkerPool<T>::GetFreeConnection()
|
||||
{
|
||||
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 DatabaseWorkerPool<LoginDatabaseConnection>;
|
||||
template class DatabaseWorkerPool<WorldDatabaseConnection>;
|
||||
template class DatabaseWorkerPool<CharacterDatabaseConnection>;
|
||||
@@ -32,9 +32,7 @@
|
||||
|
||||
#include <mysqld_error.h>
|
||||
#include <memory>
|
||||
|
||||
#define MIN_MYSQL_SERVER_VERSION 50100u
|
||||
#define MIN_MYSQL_CLIENT_VERSION 50100u
|
||||
#include <array>
|
||||
|
||||
class PingOperation : public SQLOperation
|
||||
{
|
||||
@@ -59,97 +57,21 @@ class DatabaseWorkerPool
|
||||
|
||||
public:
|
||||
/* Activity state */
|
||||
DatabaseWorkerPool() : _queue(new ProducerConsumerQueue<SQLOperation*>()),
|
||||
_async_threads(0), _synch_threads(0)
|
||||
{
|
||||
memset(_connectionCount, 0, sizeof(_connectionCount));
|
||||
_connections.resize(IDX_SIZE);
|
||||
|
||||
WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
|
||||
WPFatal(mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION, "TrinityCore does not support MySQL versions below 5.1");
|
||||
WPFatal(mysql_get_client_version() == MYSQL_VERSION_ID, "Used MySQL library version (%s) does not match the version used to compile TrinityCore (%s).",
|
||||
mysql_get_client_info(), MYSQL_SERVER_VERSION);
|
||||
}
|
||||
DatabaseWorkerPool();
|
||||
|
||||
~DatabaseWorkerPool()
|
||||
{
|
||||
_queue->Cancel();
|
||||
}
|
||||
|
||||
void SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads)
|
||||
{
|
||||
_connectionInfo.reset(new MySQLConnectionInfo(infoString));
|
||||
void SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads);
|
||||
|
||||
_async_threads = asyncThreads;
|
||||
_synch_threads = synchThreads;
|
||||
}
|
||||
uint32 Open();
|
||||
|
||||
uint32 Open()
|
||||
{
|
||||
WPFatal(_connectionInfo.get(), "Connection info was not set!");
|
||||
|
||||
TC_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)
|
||||
{
|
||||
TC_LOG_INFO("sql.driver", "DatabasePool '%s' opened successfully. %u total connections running.", GetDatabaseName(),
|
||||
(_connectionCount[IDX_SYNCH] + _connectionCount[IDX_ASYNC]));
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void Close()
|
||||
{
|
||||
TC_LOG_INFO("sql.driver", "Closing down DatabasePool '%s'.", GetDatabaseName());
|
||||
|
||||
for (uint8 i = 0; i < _connectionCount[IDX_ASYNC]; ++i)
|
||||
{
|
||||
T* t = _connections[IDX_ASYNC][i];
|
||||
t->Close(); //! Closes the actualy MySQL connection.
|
||||
}
|
||||
|
||||
TC_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();
|
||||
|
||||
TC_LOG_INFO("sql.driver", "All connections on DatabasePool '%s' closed.", GetDatabaseName());
|
||||
}
|
||||
void Close();
|
||||
|
||||
//! Prepares all prepared statements
|
||||
bool PrepareStatements()
|
||||
{
|
||||
for (uint8 i = 0; i < IDX_SIZE; ++i)
|
||||
for (uint32 c = 0; c < _connectionCount[i]; ++c)
|
||||
{
|
||||
T* t = _connections[i][c];
|
||||
t->LockIfReady();
|
||||
if (!t->PrepareStatements())
|
||||
{
|
||||
t->Unlock();
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
t->Unlock();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
bool PrepareStatements();
|
||||
|
||||
inline MySQLConnectionInfo const* GetConnectionInfo() const
|
||||
{
|
||||
@@ -201,9 +123,9 @@ class DatabaseWorkerPool
|
||||
if (!sql)
|
||||
return;
|
||||
|
||||
T* t = GetFreeConnection();
|
||||
t->Execute(sql);
|
||||
t->Unlock();
|
||||
T* connection = GetFreeConnection();
|
||||
connection->Execute(sql);
|
||||
connection->Unlock();
|
||||
}
|
||||
|
||||
//! Directly executes a one-way SQL operation in string format -with variable args-, that will block the calling thread until finished.
|
||||
@@ -221,9 +143,9 @@ class DatabaseWorkerPool
|
||||
//! Statement must be prepared with the CONNECTION_SYNCH flag.
|
||||
void DirectExecute(PreparedStatement* stmt)
|
||||
{
|
||||
T* t = GetFreeConnection();
|
||||
t->Execute(stmt);
|
||||
t->Unlock();
|
||||
T* connection = GetFreeConnection();
|
||||
connection->Execute(stmt);
|
||||
connection->Unlock();
|
||||
|
||||
//! Delete proxy-class. Not needed anymore
|
||||
delete stmt;
|
||||
@@ -235,21 +157,7 @@ class DatabaseWorkerPool
|
||||
|
||||
//! Directly executes an SQL query in string format that will block the calling thread until finished.
|
||||
//! Returns reference counted auto pointer, no need for manual memory management in upper level code.
|
||||
QueryResult Query(const char* sql, T* conn = nullptr)
|
||||
{
|
||||
if (!conn)
|
||||
conn = GetFreeConnection();
|
||||
|
||||
ResultSet* result = conn->Query(sql);
|
||||
conn->Unlock();
|
||||
if (!result || !result->GetRowCount() || !result->NextRow())
|
||||
{
|
||||
delete result;
|
||||
return QueryResult(NULL);
|
||||
}
|
||||
|
||||
return QueryResult(result);
|
||||
}
|
||||
QueryResult Query(const char* 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.
|
||||
@@ -276,23 +184,7 @@ class DatabaseWorkerPool
|
||||
//! 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)
|
||||
{
|
||||
T* t = GetFreeConnection();
|
||||
PreparedResultSet* ret = t->Query(stmt);
|
||||
t->Unlock();
|
||||
|
||||
//! Delete proxy-class. Not needed anymore
|
||||
delete stmt;
|
||||
|
||||
if (!ret || !ret->GetRowCount())
|
||||
{
|
||||
delete ret;
|
||||
return PreparedQueryResult(NULL);
|
||||
}
|
||||
|
||||
return PreparedQueryResult(ret);
|
||||
}
|
||||
PreparedQueryResult Query(PreparedStatement* stmt);
|
||||
|
||||
/**
|
||||
Asynchronous query (with resultset) methods.
|
||||
@@ -300,14 +192,7 @@ class DatabaseWorkerPool
|
||||
|
||||
//! Enqueues a query in string format that will set the value of the QueryResultFuture return object as soon as the query is executed.
|
||||
//! The return value is then processed in ProcessQueryCallback methods.
|
||||
QueryResultFuture AsyncQuery(const char* sql)
|
||||
{
|
||||
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 result;
|
||||
}
|
||||
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.
|
||||
@@ -320,27 +205,13 @@ class DatabaseWorkerPool
|
||||
//! 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)
|
||||
{
|
||||
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 result;
|
||||
}
|
||||
PreparedQueryResultFuture AsyncQuery(PreparedStatement* 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)
|
||||
{
|
||||
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 result;
|
||||
}
|
||||
QueryResultHolderFuture DelayQueryHolder(SQLQueryHolder* holder);
|
||||
|
||||
/**
|
||||
Transaction context methods.
|
||||
@@ -354,57 +225,11 @@ class DatabaseWorkerPool
|
||||
|
||||
//! 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)
|
||||
{
|
||||
#ifdef TRINITY_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:
|
||||
TC_LOG_DEBUG("sql.driver", "Transaction contains 0 queries. Not executing.");
|
||||
return;
|
||||
case 1:
|
||||
TC_LOG_DEBUG("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif // TRINITY_DEBUG
|
||||
|
||||
Enqueue(new TransactionTask(transaction));
|
||||
}
|
||||
void CommitTransaction(SQLTransaction 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)
|
||||
{
|
||||
T* con = GetFreeConnection();
|
||||
int errorCode = con->ExecuteTransaction(transaction);
|
||||
if (!errorCode)
|
||||
{
|
||||
con->Unlock(); // OK, operation succesful
|
||||
return;
|
||||
}
|
||||
|
||||
//! Handle MySQL Errno 1213 without extending deadlock to the core itself
|
||||
/// @todo More elegant way
|
||||
if (errorCode == ER_LOCK_DEADLOCK)
|
||||
{
|
||||
uint8 loopBreaker = 5;
|
||||
for (uint8 i = 0; i < loopBreaker; ++i)
|
||||
{
|
||||
if (!con->ExecuteTransaction(transaction))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//! Clean up now.
|
||||
transaction->Cleanup();
|
||||
|
||||
con->Unlock();
|
||||
}
|
||||
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.
|
||||
@@ -441,90 +266,21 @@ class DatabaseWorkerPool
|
||||
}
|
||||
|
||||
//! Apply escape string'ing for current collation. (utf8)
|
||||
void EscapeString(std::string& str)
|
||||
{
|
||||
if (str.empty())
|
||||
return;
|
||||
|
||||
char* buf = new char[str.size() * 2 + 1];
|
||||
EscapeString(buf, str.c_str(), str.size());
|
||||
str = buf;
|
||||
delete[] buf;
|
||||
}
|
||||
void EscapeString(std::string& str);
|
||||
|
||||
//! Keeps all our MySQL connections alive, prevent the server from disconnecting us.
|
||||
void 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);
|
||||
}
|
||||
void KeepAlive();
|
||||
|
||||
private:
|
||||
uint32 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.get(), *_connectionInfo);
|
||||
else if (type == IDX_SYNCH)
|
||||
t = new T(*_connectionInfo);
|
||||
else
|
||||
ABORT();
|
||||
|
||||
_connections[type][i] = t;
|
||||
++_connectionCount[type];
|
||||
|
||||
uint32 error = t->Open();
|
||||
|
||||
if (!error)
|
||||
{
|
||||
if (mysql_get_server_version(t->GetHandle()) < MIN_MYSQL_SERVER_VERSION)
|
||||
{
|
||||
TC_LOG_ERROR("sql.driver", "TrinityCore does not support MySQL versions below 5.1");
|
||||
error = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Failed to open a connection or invalid version, abort and cleanup
|
||||
if (error)
|
||||
{
|
||||
while (_connectionCount[type] != 0)
|
||||
{
|
||||
t = _connections[type][i--];
|
||||
delete t;
|
||||
--_connectionCount[type];
|
||||
}
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
// Everything is fine
|
||||
return 0;
|
||||
}
|
||||
uint32 OpenConnections(InternalIndex type, uint8 numConnections);
|
||||
|
||||
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);
|
||||
return mysql_real_escape_string(
|
||||
_connections[IDX_SYNCH].front()->GetHandle(), to, from, length);
|
||||
}
|
||||
|
||||
void Enqueue(SQLOperation* op)
|
||||
@@ -534,22 +290,7 @@ class DatabaseWorkerPool
|
||||
|
||||
//! Gets a free connection in the synchronous connection pool.
|
||||
//! Caller MUST call t->Unlock() after touching the MySQL context to prevent deadlocks.
|
||||
T* GetFreeConnection()
|
||||
{
|
||||
uint8 i = 0;
|
||||
size_t num_cons = _connectionCount[IDX_SYNCH];
|
||||
T* t = NULL;
|
||||
//! 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;
|
||||
}
|
||||
T* GetFreeConnection();
|
||||
|
||||
char const* GetDatabaseName() const
|
||||
{
|
||||
@@ -558,9 +299,7 @@ class DatabaseWorkerPool
|
||||
|
||||
//! Queue shared by async worker threads.
|
||||
std::unique_ptr<ProducerConsumerQueue<SQLOperation*>> _queue;
|
||||
std::vector<std::vector<T*>> _connections;
|
||||
//! Counter of MySQL connections;
|
||||
uint32 _connectionCount[IDX_SIZE];
|
||||
std::array<std::vector<std::unique_ptr<T>>, IDX_SIZE> _connections;
|
||||
std::unique_ptr<MySQLConnectionInfo> _connectionInfo;
|
||||
uint8 _async_threads, _synch_threads;
|
||||
};
|
||||
|
||||
@@ -37,7 +37,6 @@ MySQLConnection::MySQLConnection(MySQLConnectionInfo& connInfo) :
|
||||
m_reconnecting(false),
|
||||
m_prepareError(false),
|
||||
m_queue(NULL),
|
||||
m_worker(NULL),
|
||||
m_Mysql(NULL),
|
||||
m_connectionInfo(connInfo),
|
||||
m_connectionFlags(CONNECTION_SYNCH) { }
|
||||
@@ -50,24 +49,26 @@ m_Mysql(NULL),
|
||||
m_connectionInfo(connInfo),
|
||||
m_connectionFlags(CONNECTION_ASYNC)
|
||||
{
|
||||
m_worker = new DatabaseWorker(m_queue, this);
|
||||
m_worker = Trinity::make_unique<DatabaseWorker>(m_queue, this);
|
||||
}
|
||||
|
||||
MySQLConnection::~MySQLConnection()
|
||||
{
|
||||
delete m_worker;
|
||||
|
||||
for (size_t i = 0; i < m_stmts.size(); ++i)
|
||||
delete m_stmts[i];
|
||||
|
||||
if (m_Mysql)
|
||||
mysql_close(m_Mysql);
|
||||
Close();
|
||||
}
|
||||
|
||||
void MySQLConnection::Close()
|
||||
{
|
||||
/// Only close us if we're not operating
|
||||
delete this;
|
||||
// Stop the worker thread before the statements are cleared
|
||||
m_worker.reset();
|
||||
|
||||
m_stmts.clear();
|
||||
|
||||
if (m_Mysql)
|
||||
{
|
||||
mysql_close(m_Mysql);
|
||||
m_Mysql = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 MySQLConnection::Open()
|
||||
@@ -412,7 +413,7 @@ int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction)
|
||||
MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index)
|
||||
{
|
||||
ASSERT(index < m_stmts.size());
|
||||
MySQLPreparedStatement* ret = m_stmts[index];
|
||||
MySQLPreparedStatement* ret = m_stmts[index].get();
|
||||
if (!ret)
|
||||
TC_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");
|
||||
@@ -424,16 +425,12 @@ void MySQLConnection::PrepareStatement(uint32 index, const char* sql, Connection
|
||||
{
|
||||
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] = NULL;
|
||||
m_stmts[index].reset();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -455,8 +452,7 @@ void MySQLConnection::PrepareStatement(uint32 index, const char* sql, Connection
|
||||
}
|
||||
else
|
||||
{
|
||||
MySQLPreparedStatement* mStmt = new MySQLPreparedStatement(stmt);
|
||||
m_stmts[index] = mStmt;
|
||||
m_stmts[index] = Trinity::make_unique<MySQLPreparedStatement>(stmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -477,7 +473,7 @@ PreparedResultSet* MySQLConnection::Query(PreparedStatement* stmt)
|
||||
return new PreparedResultSet(stmt->m_stmt->GetSTMT(), result, rowCount, fieldCount);
|
||||
}
|
||||
|
||||
bool MySQLConnection::_HandleMySQLErrno(uint32 errNo)
|
||||
bool MySQLConnection::_HandleMySQLErrno(uint32 errNo, uint8 attempts /*= 5*/)
|
||||
{
|
||||
switch (errNo)
|
||||
{
|
||||
@@ -486,9 +482,21 @@ bool MySQLConnection::_HandleMySQLErrno(uint32 errNo)
|
||||
case CR_INVALID_CONN_HANDLE:
|
||||
case CR_SERVER_LOST_EXTENDED:
|
||||
{
|
||||
if (m_Mysql)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Lost the connection to the MySQL server!");
|
||||
|
||||
mysql_close(GetHandle());
|
||||
m_Mysql = nullptr;
|
||||
}
|
||||
|
||||
/*no break*/
|
||||
}
|
||||
case CR_CONN_HOST_ERROR:
|
||||
{
|
||||
TC_LOG_INFO("sql.sql", "Attempting to reconnect to the MySQL server...");
|
||||
|
||||
m_reconnecting = true;
|
||||
uint64 oldThreadId = mysql_thread_id(GetHandle());
|
||||
mysql_close(GetHandle());
|
||||
|
||||
uint32 const lErrno = Open();
|
||||
if (!lErrno)
|
||||
@@ -496,24 +504,37 @@ bool MySQLConnection::_HandleMySQLErrno(uint32 errNo)
|
||||
// Don't remove 'this' pointer unless you want to skip loading all prepared statements...
|
||||
if (!this->PrepareStatements())
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Could not re-prepare statements!");
|
||||
Close();
|
||||
return false;
|
||||
TC_LOG_FATAL("sql.sql", "Could not re-prepare statements!");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
std::abort();
|
||||
}
|
||||
|
||||
TC_LOG_INFO("sql.sql", "Connection to the MySQL server is active.");
|
||||
if (oldThreadId != mysql_thread_id(GetHandle()))
|
||||
TC_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");
|
||||
TC_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;
|
||||
}
|
||||
|
||||
// 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); // Call self (recursive)
|
||||
if ((--attempts) == 0)
|
||||
{
|
||||
// Shut down the server when the mysql server isn't
|
||||
// reachable for some time
|
||||
TC_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:
|
||||
|
||||
@@ -116,18 +116,18 @@ class MySQLConnection
|
||||
virtual void DoPrepareStatements() = 0;
|
||||
|
||||
protected:
|
||||
std::vector<MySQLPreparedStatement*> m_stmts; //! PreparedStatements storage
|
||||
std::vector<std::unique_ptr<MySQLPreparedStatement>> m_stmts; //! PreparedStatements storage
|
||||
PreparedStatementMap m_queries; //! Query 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:
|
||||
ProducerConsumerQueue<SQLOperation*>* m_queue; //! Queue shared with other asynchronous connections.
|
||||
DatabaseWorker* m_worker; //! Core worker task.
|
||||
MYSQL * m_Mysql; //! MySQL Handle.
|
||||
std::unique_ptr<DatabaseWorker> m_worker; //! Core worker task.
|
||||
MYSQL* 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;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "UpdateFetcher.h"
|
||||
#include "DatabaseLoader.h"
|
||||
#include "Config.h"
|
||||
#include "BuiltInConfig.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@@ -35,23 +36,17 @@ using namespace boost::process;
|
||||
using namespace boost::process::initializers;
|
||||
using namespace boost::iostreams;
|
||||
|
||||
std::string DBUpdaterUtil::GetMySqlCli()
|
||||
std::string DBUpdaterUtil::GetCorrectedMySQLExecutable()
|
||||
{
|
||||
if (!corrected_path().empty())
|
||||
return corrected_path();
|
||||
else
|
||||
{
|
||||
std::string const entry = sConfigMgr->GetStringDefault("Updates.MySqlCLIPath", "");
|
||||
if (!entry.empty())
|
||||
return entry;
|
||||
else
|
||||
return GitRevision::GetMySQLExecutable();
|
||||
}
|
||||
return BuiltInConfig::GetMySQLExecutable();
|
||||
}
|
||||
|
||||
bool DBUpdaterUtil::CheckExecutable()
|
||||
{
|
||||
boost::filesystem::path exe(GetMySqlCli());
|
||||
boost::filesystem::path exe(GetCorrectedMySQLExecutable());
|
||||
if (!exists(exe))
|
||||
{
|
||||
exe.clear();
|
||||
@@ -85,16 +80,6 @@ std::string& DBUpdaterUtil::corrected_path()
|
||||
return path;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
std::string DBUpdater<T>::GetSourceDirectory()
|
||||
{
|
||||
std::string const entry = sConfigMgr->GetStringDefault("Updates.SourcePath", "");
|
||||
if (!entry.empty())
|
||||
return entry;
|
||||
else
|
||||
return GitRevision::GetSourceDirectory();
|
||||
}
|
||||
|
||||
// Auth Database
|
||||
template<>
|
||||
std::string DBUpdater<LoginDatabaseConnection>::GetConfigEntry()
|
||||
@@ -111,7 +96,8 @@ std::string DBUpdater<LoginDatabaseConnection>::GetTableName()
|
||||
template<>
|
||||
std::string DBUpdater<LoginDatabaseConnection>::GetBaseFile()
|
||||
{
|
||||
return DBUpdater<LoginDatabaseConnection>::GetSourceDirectory() + "/sql/base/auth_database.sql";
|
||||
return BuiltInConfig::GetSourceDirectory() +
|
||||
"/sql/base/auth_database.sql";
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -169,7 +155,8 @@ std::string DBUpdater<CharacterDatabaseConnection>::GetTableName()
|
||||
template<>
|
||||
std::string DBUpdater<CharacterDatabaseConnection>::GetBaseFile()
|
||||
{
|
||||
return DBUpdater<CharacterDatabaseConnection>::GetSourceDirectory() + "/sql/base/characters_database.sql";
|
||||
return BuiltInConfig::GetSourceDirectory() +
|
||||
"/sql/base/characters_database.sql";
|
||||
}
|
||||
|
||||
template<>
|
||||
@@ -239,7 +226,7 @@ bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool)
|
||||
|
||||
TC_LOG_INFO("sql.updates", "Updating %s database...", DBUpdater<T>::GetTableName().c_str());
|
||||
|
||||
Path const sourceDirectory(GetSourceDirectory());
|
||||
Path const sourceDirectory(BuiltInConfig::GetSourceDirectory());
|
||||
|
||||
if (!is_directory(sourceDirectory))
|
||||
{
|
||||
@@ -410,7 +397,7 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
|
||||
boost::process::pipe errPipe = create_pipe();
|
||||
|
||||
child c = execute(run_exe(
|
||||
boost::filesystem::absolute(DBUpdaterUtil::GetMySqlCli()).generic_string()),
|
||||
boost::filesystem::absolute(DBUpdaterUtil::GetCorrectedMySQLExecutable()).generic_string()),
|
||||
set_args(args), bind_stdin(source), throw_on_error(),
|
||||
bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
|
||||
bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
|
||||
|
||||
@@ -57,7 +57,7 @@ struct UpdateResult
|
||||
class DBUpdaterUtil
|
||||
{
|
||||
public:
|
||||
static std::string GetMySqlCli();
|
||||
static std::string GetCorrectedMySQLExecutable();
|
||||
|
||||
static bool CheckExecutable();
|
||||
|
||||
@@ -71,8 +71,6 @@ class DBUpdater
|
||||
public:
|
||||
using Path = boost::filesystem::path;
|
||||
|
||||
static std::string GetSourceDirectory();
|
||||
|
||||
static inline std::string GetConfigEntry();
|
||||
|
||||
static inline std::string GetTableName();
|
||||
|
||||
@@ -137,22 +137,33 @@ UpdateFetcher::AppliedFileStorage UpdateFetcher::ReceiveAppliedFiles() const
|
||||
return map;
|
||||
}
|
||||
|
||||
UpdateFetcher::SQLUpdate UpdateFetcher::ReadSQLUpdate(boost::filesystem::path const& file) const
|
||||
std::string UpdateFetcher::ReadSQLUpdate(boost::filesystem::path const& file) const
|
||||
{
|
||||
std::ifstream in(file.c_str());
|
||||
WPFatal(in.is_open(), "Could not read an update file.");
|
||||
if (!in.is_open())
|
||||
{
|
||||
TC_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 disabled 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 Trinity::make_unique<std::string>(ss.str());
|
||||
return ss.str();
|
||||
}();
|
||||
|
||||
in.close();
|
||||
return update;
|
||||
}
|
||||
|
||||
UpdateResult UpdateFetcher::Update(bool const redundancyChecks, bool const allowRehash, bool const archivedRedundancy, int32 const cleanDeadReferencesMaxCount) const
|
||||
UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
|
||||
bool const allowRehash,
|
||||
bool const archivedRedundancy,
|
||||
int32 const cleanDeadReferencesMaxCount) const
|
||||
{
|
||||
LocaleFileStorage const available = GetFileList();
|
||||
AppliedFileStorage applied = ReceiveAppliedFiles();
|
||||
@@ -198,11 +209,9 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, bool const allow
|
||||
}
|
||||
}
|
||||
|
||||
// Read update from file
|
||||
SQLUpdate const update = ReadSQLUpdate(availableQuery.first);
|
||||
|
||||
// Calculate hash
|
||||
std::string const hash = CalculateHash(update);
|
||||
std::string const hash =
|
||||
CalculateHash(ReadSQLUpdate(availableQuery.first));
|
||||
|
||||
UpdateMode mode = MODE_APPLY;
|
||||
|
||||
@@ -325,11 +334,11 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, bool const allow
|
||||
return UpdateResult(importedUpdates, countRecentUpdates, countArchivedUpdates);
|
||||
}
|
||||
|
||||
std::string UpdateFetcher::CalculateHash(SQLUpdate const& query) const
|
||||
std::string UpdateFetcher::CalculateHash(std::string const& query) const
|
||||
{
|
||||
// Calculate a Sha1 hash based on query content.
|
||||
unsigned char digest[SHA_DIGEST_LENGTH];
|
||||
SHA1((unsigned char*)query->c_str(), query->length(), (unsigned char*)&digest);
|
||||
SHA1((unsigned char*)query.c_str(), query.length(), (unsigned char*)&digest);
|
||||
|
||||
return ByteArrayToHexStr(digest, SHA_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
@@ -103,16 +103,16 @@ private:
|
||||
typedef std::unordered_map<std::string, std::string> HashToFileNameStorage;
|
||||
typedef std::unordered_map<std::string, AppliedFileEntry> AppliedFileStorage;
|
||||
typedef std::vector<UpdateFetcher::DirectoryEntry> DirectoryStorage;
|
||||
typedef std::unique_ptr<std::string> SQLUpdate;
|
||||
|
||||
LocaleFileStorage GetFileList() const;
|
||||
void FillFileListRecursively(Path const& path, LocaleFileStorage& storage, State const state, uint32 const depth) const;
|
||||
void FillFileListRecursively(Path const& path, LocaleFileStorage& storage,
|
||||
State const state, uint32 const depth) const;
|
||||
|
||||
DirectoryStorage ReceiveIncludedDirectories() const;
|
||||
AppliedFileStorage ReceiveAppliedFiles() const;
|
||||
|
||||
SQLUpdate ReadSQLUpdate(Path const& file) const;
|
||||
std::string CalculateHash(SQLUpdate const& query) const;
|
||||
std::string ReadSQLUpdate(Path const& file) const;
|
||||
std::string CalculateHash(std::string const& query) const;
|
||||
|
||||
uint32 Apply(Path const& path) const;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user