aboutsummaryrefslogtreecommitdiff
path: root/src/server/database
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/database')
-rw-r--r--src/server/database/CMakeLists.txt90
-rw-r--r--src/server/database/Database/AdhocStatement.h2
-rw-r--r--src/server/database/Database/DatabaseEnv.cpp22
-rw-r--r--src/server/database/Database/DatabaseEnv.h10
-rw-r--r--src/server/database/Database/DatabaseLoader.cpp79
-rw-r--r--src/server/database/Database/DatabaseLoader.h17
-rw-r--r--src/server/database/Database/DatabaseWorker.h2
-rw-r--r--src/server/database/Database/DatabaseWorkerPool.cpp322
-rw-r--r--src/server/database/Database/DatabaseWorkerPool.h319
-rw-r--r--src/server/database/Database/Field.h4
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp19
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h7
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.cpp15
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.h9
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.cpp4
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.h2
-rw-r--r--src/server/database/Database/MySQLConnection.cpp87
-rw-r--r--src/server/database/Database/MySQLConnection.h12
-rw-r--r--src/server/database/Database/MySQLThreading.h2
-rw-r--r--src/server/database/Database/PreparedStatement.cpp2
-rw-r--r--src/server/database/Database/PreparedStatement.h6
-rw-r--r--src/server/database/Database/QueryCallback.h209
-rw-r--r--src/server/database/Database/QueryHolder.h4
-rw-r--r--src/server/database/Database/QueryResult.cpp2
-rw-r--r--src/server/database/Database/QueryResult.h4
-rw-r--r--src/server/database/Database/SQLOperation.h2
-rw-r--r--src/server/database/Database/Transaction.h4
-rw-r--r--src/server/database/Logging/AppenderDB.h2
-rw-r--r--src/server/database/Updater/DBUpdater.cpp124
-rw-r--r--src/server/database/Updater/DBUpdater.h10
-rw-r--r--src/server/database/Updater/UpdateFetcher.cpp47
-rw-r--r--src/server/database/Updater/UpdateFetcher.h9
32 files changed, 844 insertions, 605 deletions
diff --git a/src/server/database/CMakeLists.txt b/src/server/database/CMakeLists.txt
index 5a53899f4cb..bd2fa280ad6 100644
--- a/src/server/database/CMakeLists.txt
+++ b/src/server/database/CMakeLists.txt
@@ -8,34 +8,22 @@
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-if (NOT MYSQL_FOUND)
- message(SEND_ERROR "MySQL wasn't found on your system but it's required to build the servers!")
-endif()
-
-if( USE_COREPCH )
- include_directories(${CMAKE_CURRENT_BINARY_DIR})
-endif()
-
-file(GLOB_RECURSE sources_Database Database/*.cpp Database/*.h)
-file(GLOB_RECURSE sources_Logging Logging/*.cpp Logging/*.h)
-file(GLOB_RECURSE sources_Updater Updater/*.cpp Updater/*.h)
-
-file(GLOB sources_localdir *.cpp *.h)
-
-#
-# Build shared sourcelist
-#
+CollectSourceFiles(
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ PRIVATE_SOURCES
+ # Exclude
+ ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders)
if (USE_COREPCH)
- set(database_STAT_PCH_HDR PrecompiledHeaders/databasePCH.h)
- set(database_STAT_PCH_SRC PrecompiledHeaders/databasePCH.cpp)
+ set(PRIVATE_PCH_HEADER PrecompiledHeaders/databasePCH.h)
+ set(PRIVATE_PCH_SOURCE PrecompiledHeaders/databasePCH.cpp)
endif()
-set(database_STAT_SRCS
- ${database_STAT_SRCS}
- ${sources_Database}
- ${sources_Logging}
- ${sources_Updater}
+GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
+
+add_library(database
+ ${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES}
)
# Do NOT add any extra include directory unless it does not create unneeded extra dependencies,
@@ -47,31 +35,43 @@ set(database_STAT_SRCS
# linkage (enums, defines...) it is discouraged to do so unless necessary, as it will pullute
# include_directories leading to further unnoticed dependency aditions
# Linker Depencency requirements: common
-include_directories(
+CollectIncludeDirectories(
${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR}/Database
- ${CMAKE_CURRENT_SOURCE_DIR}/Updater
- ${CMAKE_SOURCE_DIR}/dep/cppformat
- ${CMAKE_SOURCE_DIR}/dep/process
- ${CMAKE_SOURCE_DIR}/src/common/
- ${CMAKE_SOURCE_DIR}/src/common/Configuration
- ${CMAKE_SOURCE_DIR}/src/common/Debugging
- ${CMAKE_SOURCE_DIR}/src/common/Logging
- ${CMAKE_SOURCE_DIR}/src/common/Threading
- ${CMAKE_SOURCE_DIR}/src/common/Utilities
- ${MYSQL_INCLUDE_DIR}
- ${OPENSSL_INCLUDE_DIR}
- ${VALGRIND_INCLUDE_DIR}
-)
+ PUBLIC_INCLUDES
+ # Exclude
+ ${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders)
-GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
+target_include_directories(database
+ PUBLIC
+ ${PUBLIC_INCLUDES}
+ PRIVATE
+ ${CMAKE_CURRENT_BINARY_DIR})
-add_library(database STATIC
- ${database_STAT_SRCS}
- ${database_STAT_PCH_SRC}
-)
+add_definitions(-DTRINITY_API_EXPORT_DATABASE)
+
+target_link_libraries(database
+ PUBLIC
+ common
+ mysql)
+
+set_target_properties(database
+ PROPERTIES
+ FOLDER
+ "server")
+
+if( BUILD_SHARED_LIBS )
+ if( UNIX )
+ install(TARGETS database
+ LIBRARY
+ DESTINATION lib)
+ elseif( WIN32 )
+ install(TARGETS database
+ RUNTIME
+ DESTINATION "${CMAKE_INSTALL_PREFIX}")
+ endif()
+endif()
# Generate precompiled header
if (USE_COREPCH)
- add_cxx_pch(database ${database_STAT_PCH_HDR} ${database_STAT_PCH_SRC})
+ add_cxx_pch(database ${PRIVATE_PCH_HEADER} ${PRIVATE_PCH_SOURCE})
endif ()
diff --git a/src/server/database/Database/AdhocStatement.h b/src/server/database/Database/AdhocStatement.h
index ab85493a14e..9315038bce1 100644
--- a/src/server/database/Database/AdhocStatement.h
+++ b/src/server/database/Database/AdhocStatement.h
@@ -25,7 +25,7 @@ typedef std::future<QueryResult> QueryResultFuture;
typedef std::promise<QueryResult> QueryResultPromise;
/*! Raw, ad-hoc query. */
-class BasicStatementTask : public SQLOperation
+class TC_DATABASE_API BasicStatementTask : public SQLOperation
{
public:
BasicStatementTask(const char* sql, bool async = false);
diff --git a/src/server/database/Database/DatabaseEnv.cpp b/src/server/database/Database/DatabaseEnv.cpp
new file mode 100644
index 00000000000..3b2e632e4fb
--- /dev/null
+++ b/src/server/database/Database/DatabaseEnv.cpp
@@ -0,0 +1,22 @@
+/*
+ * 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 "DatabaseEnv.h"
+
+WorldDatabaseWorkerPool WorldDatabase;
+CharacterDatabaseWorkerPool CharacterDatabase;
+LoginDatabaseWorkerPool LoginDatabase;
diff --git a/src/server/database/Database/DatabaseEnv.h b/src/server/database/Database/DatabaseEnv.h
index cc8355a1302..05a4d5aad75 100644
--- a/src/server/database/Database/DatabaseEnv.h
+++ b/src/server/database/Database/DatabaseEnv.h
@@ -38,9 +38,11 @@
#include "Implementation/CharacterDatabase.h"
#include "Implementation/WorldDatabase.h"
-extern WorldDatabaseWorkerPool WorldDatabase;
-extern CharacterDatabaseWorkerPool CharacterDatabase;
-extern LoginDatabaseWorkerPool LoginDatabase;
+/// Accessor to the world database
+TC_DATABASE_API extern WorldDatabaseWorkerPool WorldDatabase;
+/// Accessor to the character database
+TC_DATABASE_API extern CharacterDatabaseWorkerPool CharacterDatabase;
+/// Accessor to the realm/login database
+TC_DATABASE_API extern LoginDatabaseWorkerPool LoginDatabase;
#endif
-
diff --git a/src/server/database/Database/DatabaseLoader.cpp b/src/server/database/Database/DatabaseLoader.cpp
index 92d8730cd12..f358bdd54b4 100644
--- a/src/server/database/Database/DatabaseLoader.cpp
+++ b/src/server/database/Database/DatabaseLoader.cpp
@@ -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,9 +156,30 @@ bool DatabaseLoader::PrepareStatements()
return Process(_prepare);
}
-template
-DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(DatabaseWorkerPool<LoginDatabaseConnection>& pool, std::string const& name);
-template
-DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(DatabaseWorkerPool<WorldDatabaseConnection>& pool, std::string const& name);
-template
-DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(DatabaseWorkerPool<CharacterDatabaseConnection>& pool, std::string const& name);
+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 TC_DATABASE_API
+DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(LoginDatabaseWorkerPool&, std::string const&);
+template TC_DATABASE_API
+DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(CharacterDatabaseWorkerPool&, std::string const&);
+template TC_DATABASE_API
+DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(WorldDatabaseWorkerPool&, std::string const&);
diff --git a/src/server/database/Database/DatabaseLoader.h b/src/server/database/Database/DatabaseLoader.h
index da92cf85a9f..647c1e113e3 100644
--- a/src/server/database/Database/DatabaseLoader.h
+++ b/src/server/database/Database/DatabaseLoader.h
@@ -19,14 +19,15 @@
#define DatabaseLoader_h__
#include "DatabaseWorkerPool.h"
-#include "DatabaseEnv.h"
+#include "DBUpdater.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.
-class DatabaseLoader
+class TC_DATABASE_API DatabaseLoader
{
public:
DatabaseLoader(std::string const& logger, uint32 const defaultUpdateMask);
@@ -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__
diff --git a/src/server/database/Database/DatabaseWorker.h b/src/server/database/Database/DatabaseWorker.h
index c21a3d2a343..d6b43943f7d 100644
--- a/src/server/database/Database/DatabaseWorker.h
+++ b/src/server/database/Database/DatabaseWorker.h
@@ -24,7 +24,7 @@
class MySQLConnection;
class SQLOperation;
-class DatabaseWorker
+class TC_DATABASE_API DatabaseWorker
{
public:
DatabaseWorker(ProducerConsumerQueue<SQLOperation*>* newQueue, MySQLConnection* connection);
diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp
new file mode 100644
index 00000000000..6fc4dc21a51
--- /dev/null
+++ b/src/server/database/Database/DatabaseWorkerPool.cpp
@@ -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). Search on forum for TCE00011.",
+ 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(), 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 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 TC_DATABASE_API DatabaseWorkerPool<LoginDatabaseConnection>;
+template class TC_DATABASE_API DatabaseWorkerPool<WorldDatabaseConnection>;
+template class TC_DATABASE_API DatabaseWorkerPool<CharacterDatabaseConnection>;
diff --git a/src/server/database/Database/DatabaseWorkerPool.h b/src/server/database/Database/DatabaseWorkerPool.h
index d5a254647eb..ffdde91c0a6 100644
--- a/src/server/database/Database/DatabaseWorkerPool.h
+++ b/src/server/database/Database/DatabaseWorkerPool.h
@@ -19,7 +19,7 @@
#define _DATABASEWORKERPOOL_H
#include "Common.h"
-#include "Callback.h"
+#include "QueryCallback.h"
#include "MySQLConnection.h"
#include "Transaction.h"
#include "DatabaseWorker.h"
@@ -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));
-
- _async_threads = asyncThreads;
- _synch_threads = synchThreads;
- }
+ void SetConnectionInfo(std::string const& infoString, uint8 const asyncThreads, uint8 const synchThreads);
- uint32 Open()
- {
- WPFatal(_connectionInfo.get(), "Connection info was not set!");
+ uint32 Open();
- 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
{
@@ -198,12 +120,12 @@ class DatabaseWorkerPool
//! This method should only be used for queries that are only executed once, e.g during startup.
void DirectExecute(const char* sql)
{
- if (!sql)
+ if (Trinity::IsFormatEmptyOrNull(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.
@@ -267,7 +175,7 @@ class DatabaseWorkerPool
template<typename Format, typename... Args>
QueryResult PQuery(Format&& sql, Args&&... args)
{
- if (!sql)
+ if (Trinity::IsFormatEmptyOrNull(sql))
return QueryResult(nullptr);
return Query(Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
@@ -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;
};
diff --git a/src/server/database/Database/Field.h b/src/server/database/Database/Field.h
index ec9e626ee1b..123e25dbbf3 100644
--- a/src/server/database/Database/Field.h
+++ b/src/server/database/Database/Field.h
@@ -53,7 +53,7 @@
| SUM, AVG | DECIMAL |
| COUNT | BIGINT |
*/
-class Field
+class TC_DATABASE_API Field
{
friend class ResultSet;
friend class PreparedResultSet;
@@ -323,7 +323,7 @@ class Field
data.value = NULL;
}
- static size_t SizeForType(MYSQL_FIELD* field)
+ static uint32 SizeForType(MYSQL_FIELD* field)
{
switch (field->type)
{
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index ab68aca2a8c..bb0e2bb09d4 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -42,11 +42,11 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_MAIL_LIST_INFO, "SELECT id, sender, (SELECT name FROM characters WHERE guid = sender) AS sendername, receiver, (SELECT name FROM characters WHERE guid = receiver) AS receivername, "
"subject, deliver_time, expire_time, money, has_items FROM mail WHERE receiver = ? ", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_MAIL_LIST_ITEMS, "SELECT itemEntry,count FROM item_instance WHERE guid = ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.playerBytes, c.playerBytes2, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, "
+ PrepareStatement(CHAR_SEL_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, "
"gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid "
"FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY c.guid", CONNECTION_ASYNC);
- PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.playerBytes, c.playerBytes2, c.level, c.zone, c.map, "
+ PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, "
"c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, "
"cb.guid, 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 "
@@ -56,7 +56,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHAR_RACE, "SELECT race FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_LEVEL, "SELECT level FROM characters WHERE guid = ?", CONNECTION_SYNCH);
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_CHARACTER_NAME_DATA, "SELECT race, class, gender, level, name 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);
PrepareStatement(CHAR_SEL_CHAR_POSITION, "SELECT position_x, position_y, position_z, orientation, map, taxi_path FROM characters WHERE guid = ?", CONNECTION_SYNCH);
@@ -64,7 +64,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_BATTLEGROUND_RANDOM, "DELETE FROM character_battleground_random WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_BATTLEGROUND_RANDOM, "INSERT INTO character_battleground_random (guid) VALUES (?)", CONNECTION_ASYNC);
- PrepareStatement(CHAR_SEL_CHARACTER, "SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, "
+ PrepareStatement(CHAR_SEL_CHARACTER, "SELECT guid, account, name, race, class, gender, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, bankSlots, restState, playerFlags, "
"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, "
"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, "
"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, "
@@ -157,11 +157,9 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_GIFT, "DELETE FROM character_gifts WHERE item_guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM, "SELECT entry, flags FROM character_gifts WHERE item_guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_ACCOUNT_BY_NAME, "SELECT account FROM characters WHERE name = ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_CHARACTER_DATA_BY_GUID, "SELECT account, name, level FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES, "DELETE FROM account_instance_times WHERE accountId = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES, "INSERT INTO account_instance_times (accountId, instanceId, releaseTime) VALUES (?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_NAME_CLASS, "SELECT name, class FROM characters WHERE guid = ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_CHARACTER_NAME, "SELECT name FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_MATCH_MAKER_RATING, "SELECT matchMakerRating FROM character_arena_stats WHERE guid = ? AND slot = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHARACTER_COUNT, "SELECT account, COUNT(guid) FROM characters WHERE account = ? GROUP BY account", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_NAME, "UPDATE characters set name = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC);
@@ -351,7 +349,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_LFG_DATA, "DELETE FROM lfg_data WHERE guid = ?", CONNECTION_ASYNC);
// Player saving
- PrepareStatement(CHAR_INS_CHARACTER, "INSERT INTO characters (guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, "
+ PrepareStatement(CHAR_INS_CHARACTER, "INSERT INTO characters (guid, account, name, race, class, gender, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, bankSlots, restState, playerFlags, "
"map, instance_id, instance_mode_mask, position_x, position_y, position_z, orientation, trans_x, trans_y, trans_z, trans_o, transguid, "
"taximask, cinematic, "
"totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, "
@@ -359,8 +357,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"death_expire_time, taxi_path, arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, "
"todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, health, power1, power2, power3, "
"power4, power5, power6, power7, latency, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, knownTitles, actionBars, grantableLevels) VALUES "
- "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC);
- PrepareStatement(CHAR_UPD_CHARACTER, "UPDATE characters SET name=?,race=?,class=?,gender=?,level=?,xp=?,money=?,playerBytes=?,playerBytes2=?,playerFlags=?,"
+ "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHARACTER, "UPDATE characters SET name=?,race=?,class=?,gender=?,level=?,xp=?,money=?,skin=?,face=?,hairStyle=?,hairColor=?,facialStyle=?,bankSlots=?,restState=?,playerFlags=?,"
"map=?,instance_id=?,instance_mode_mask=?,position_x=?,position_y=?,position_z=?,orientation=?,trans_x=?,trans_y=?,trans_z=?,trans_o=?,transguid=?,taximask=?,cinematic=?,totaltime=?,leveltime=?,rest_bonus=?,"
"logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,extra_flags=?,stable_slots=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?,"
"arenaPoints=?,totalHonorPoints=?,todayHonorPoints=?,yesterdayHonorPoints=?,totalKills=?,todayKills=?,yesterdayKills=?,chosenTitle=?,knownCurrencies=?,"
@@ -407,7 +405,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID, "DELETE FROM character_instance WHERE guid = ? AND instance = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_INSTANCE, "UPDATE character_instance SET instance = ?, permanent = ?, extendState = ? WHERE guid = ? AND instance = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHAR_INSTANCE, "INSERT INTO character_instance (guid, instance, permanent, extendState) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
- PrepareStatement(CHAR_UPD_GENDER_PLAYERBYTES, "UPDATE characters SET gender = ?, playerBytes = ?, playerBytes2 = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_GENDER_AND_APPEARANCE, "UPDATE characters SET gender = ?, skin = ?, face = ?, hairStyle = ?, hairColor = ?, facialStyle = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHARACTER_SKILL, "DELETE FROM character_skills WHERE guid = ? AND skill = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_ADD_CHARACTER_SOCIAL_FLAGS, "UPDATE character_social SET flags = flags | ? WHERE guid = ? AND friend = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_REM_CHARACTER_SOCIAL_FLAGS, "UPDATE character_social SET flags = flags & ~ ? WHERE guid = ? AND friend = ?", CONNECTION_ASYNC);
@@ -440,7 +438,6 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHAR_OLD_CHARS, "SELECT guid, deleteInfos_Account FROM characters WHERE deleteDate IS NOT NULL AND deleteDate < ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_ARENA_TEAM_ID_BY_PLAYER_GUID, "SELECT arena_team_member.arenateamid FROM arena_team_member JOIN arena_team ON arena_team_member.arenateamid = arena_team.arenateamid WHERE guid = ? AND type = ? LIMIT 1", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_MAIL, "SELECT id, messageType, sender, receiver, subject, body, has_items, expire_time, deliver_time, money, cod, checked, stationery, mailTemplateId FROM mail WHERE receiver = ? ORDER BY id DESC", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_CHAR_PLAYERBYTES2, "SELECT playerBytes2 FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHAR_GUID_BY_NAME, "SELECT guid FROM characters WHERE name = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_CHAR_AURA_FROZEN, "DELETE FROM character_aura WHERE spell = 9454 AND guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_INVENTORY_COUNT_ITEM, "SELECT COUNT(itemEntry) FROM character_inventory ci INNER JOIN item_instance ii ON ii.guid = ci.item WHERE itemEntry = ?", CONNECTION_SYNCH);
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index 19d4a609e77..0cac6d35b55 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.h
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -146,13 +146,11 @@ enum CharacterDatabaseStatements
CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES,
CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES,
CHAR_SEL_CHARACTER_NAME_CLASS,
- CHAR_SEL_CHARACTER_NAME,
CHAR_SEL_MATCH_MAKER_RATING,
CHAR_SEL_CHARACTER_COUNT,
CHAR_UPD_NAME,
CHAR_UPD_NAME_BY_GUID,
CHAR_DEL_DECLINED_NAME,
- CHAR_SEL_CHARACTER_DATA_BY_GUID,
CHAR_INS_GUILD,
CHAR_DEL_GUILD,
@@ -334,7 +332,7 @@ enum CharacterDatabaseStatements
CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID,
CHAR_UPD_CHAR_INSTANCE,
CHAR_INS_CHAR_INSTANCE,
- CHAR_UPD_GENDER_PLAYERBYTES,
+ CHAR_UPD_GENDER_AND_APPEARANCE,
CHAR_DEL_CHARACTER_SKILL,
CHAR_UPD_ADD_CHARACTER_SOCIAL_FLAGS,
CHAR_UPD_REM_CHARACTER_SOCIAL_FLAGS,
@@ -371,7 +369,6 @@ enum CharacterDatabaseStatements
CHAR_SEL_CHAR_OLD_CHARS,
CHAR_SEL_ARENA_TEAM_ID_BY_PLAYER_GUID,
CHAR_SEL_MAIL,
- CHAR_SEL_CHAR_PLAYERBYTES2,
CHAR_SEL_CHAR_GUID_BY_NAME,
CHAR_DEL_CHAR_AURA_FROZEN,
CHAR_SEL_CHAR_INVENTORY_COUNT_ITEM,
@@ -538,7 +535,7 @@ enum CharacterDatabaseStatements
MAX_CHARACTERDATABASE_STATEMENTS
};
-class CharacterDatabaseConnection : public MySQLConnection
+class TC_DATABASE_API CharacterDatabaseConnection : public MySQLConnection
{
public:
typedef CharacterDatabaseStatements Statements;
diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp
index 66847f0a6a0..4f056e2686d 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.cpp
+++ b/src/server/database/Database/Implementation/LoginDatabase.cpp
@@ -24,33 +24,34 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_REALMLIST, "SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild FROM realmlist WHERE flag <> 3 ORDER BY name", CONNECTION_SYNCH);
PrepareStatement(LOGIN_DEL_EXPIRED_IP_BANS, "DELETE FROM ip_banned WHERE unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_IP_INFO, "(SELECT unbandate > UNIX_TIMESTAMP() OR unbandate = bandate AS banned, NULL as country FROM ip_banned WHERE ip = ?) "
"UNION "
"(SELECT NULL AS banned, country FROM ip2nation WHERE INET_NTOA(ip) = ?)", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_SEL_IP_BANNED, "SELECT * FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_INS_IP_AUTO_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity Auth', 'Failed login autoban')", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_IP_BANNED_ALL, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) ORDER BY unbandate", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_IP_BANNED_BY_IP, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) AND ip LIKE CONCAT('%%', ?, '%%') ORDER BY unbandate", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED, "SELECT bandate, unbandate FROM account_banned WHERE id = ? AND active = 1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED_ALL, "SELECT account.id, username FROM account, account_banned WHERE account.id = account_banned.id AND active = 1 GROUP BY account.id", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME, "SELECT account.id, username FROM account, account_banned WHERE account.id = account_banned.id AND active = 1 AND username LIKE CONCAT('%%', ?, '%%') GROUP BY account.id", CONNECTION_SYNCH);
PrepareStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED, "INSERT INTO account_banned VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity Auth', 'Failed login autoban', 1)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_DEL_ACCOUNT_BANNED, "DELETE FROM account_banned WHERE id = ?", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_SEL_SESSIONKEY, "SELECT a.sessionkey, a.id, aa.gmlevel FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_UPD_VS, "UPDATE account SET v = ?, s = ? WHERE username = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_LOGONPROOF, "UPDATE account SET sessionkey = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.sha_pass_hash, a.id, a.locked, a.lock_country, a.last_ip, aa.gmlevel, a.v, a.s, a.token_key FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, "
+ "ab.unbandate = ab.bandate, aa.gmlevel, a.token_key, a.sha_pass_hash, a.v, a.s "
+ "FROM account a LEFT JOIN account_access aa ON a.id = aa.id LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_RECONNECTCHALLENGE, "SELECT a.id, UPPER(a.username), a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, "
+ "ab.unbandate = ab.bandate, aa.gmlevel, a.sessionKey "
+ "FROM account a LEFT JOIN account_access aa ON a.id = aa.id LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_LOGON_COUNTRY, "SELECT country FROM ip2nation WHERE ip < ? ORDER BY ip DESC LIMIT 0,1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_UPD_FAILEDLOGINS, "UPDATE account SET failed_logins = failed_logins + 1 WHERE username = ?", CONNECTION_ASYNC);
- PrepareStatement(LOGIN_SEL_FAILEDLOGINS, "SELECT id, failed_logins FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.sessionkey, a.last_ip, a.locked, a.lock_country, a.expansion, a.mutetime, a.locale, a.recruiter, a.os, aa.gmLevel, "
"ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id FROM account a LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?) "
"LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account r ON a.id = r.recruiter WHERE a.username = ? ORDER BY aa.RealmID DESC LIMIT 1", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL, "SELECT id, username FROM account WHERE email = ?", CONNECTION_SYNCH);
- PrepareStatement(LOGIN_SEL_NUM_CHARS_ON_REALM, "SELECT numchars FROM realmcharacters WHERE realmid = ? AND acctid= ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_REALM_CHARACTER_COUNTS, "SELECT realmid, numchars FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_IP, "SELECT id, username FROM account WHERE last_ip = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_ID, "SELECT 1 FROM account WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_INS_IP_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?)", CONNECTION_ASYNC);
diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h
index 69c2e758551..e206be16d73 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.h
+++ b/src/server/database/Database/Implementation/LoginDatabase.h
@@ -33,25 +33,22 @@ enum LoginDatabaseStatements
LOGIN_DEL_EXPIRED_IP_BANS,
LOGIN_UPD_EXPIRED_ACCOUNT_BANS,
LOGIN_SEL_IP_INFO,
- LOGIN_SEL_IP_BANNED,
LOGIN_INS_IP_AUTO_BANNED,
- LOGIN_SEL_ACCOUNT_BANNED,
LOGIN_SEL_ACCOUNT_BANNED_ALL,
LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME,
LOGIN_INS_ACCOUNT_AUTO_BANNED,
LOGIN_DEL_ACCOUNT_BANNED,
- LOGIN_SEL_SESSIONKEY,
LOGIN_UPD_VS,
LOGIN_UPD_LOGONPROOF,
LOGIN_SEL_LOGONCHALLENGE,
+ LOGIN_SEL_RECONNECTCHALLENGE,
LOGIN_SEL_LOGON_COUNTRY,
LOGIN_UPD_FAILEDLOGINS,
- LOGIN_SEL_FAILEDLOGINS,
LOGIN_SEL_ACCOUNT_ID_BY_NAME,
LOGIN_SEL_ACCOUNT_LIST_BY_NAME,
LOGIN_SEL_ACCOUNT_INFO_BY_NAME,
LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL,
- LOGIN_SEL_NUM_CHARS_ON_REALM,
+ LOGIN_SEL_REALM_CHARACTER_COUNTS,
LOGIN_SEL_ACCOUNT_BY_IP,
LOGIN_INS_IP_BANNED,
LOGIN_DEL_IP_NOT_BANNED,
@@ -119,7 +116,7 @@ enum LoginDatabaseStatements
MAX_LOGINDATABASE_STATEMENTS
};
-class LoginDatabaseConnection : public MySQLConnection
+class TC_DATABASE_API LoginDatabaseConnection : public MySQLConnection
{
public:
typedef LoginDatabaseStatements Statements;
diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp
index 7a183d5bf78..83720c1a996 100644
--- a/src/server/database/Database/Implementation/WorldDatabase.cpp
+++ b/src/server/database/Database/Implementation/WorldDatabase.cpp
@@ -30,8 +30,8 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_SEL_SMARTAI_WP, "SELECT entry, pointid, position_x, position_y, position_z FROM waypoints ORDER BY entry, pointid", CONNECTION_SYNCH);
PrepareStatement(WORLD_DEL_GAMEOBJECT, "DELETE FROM gameobject WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_DEL_EVENT_GAMEOBJECT, "DELETE FROM game_event_gameobject WHERE guid = ?", CONNECTION_ASYNC);
- PrepareStatement(WORLD_INS_GRAVEYARD_ZONE, "INSERT INTO game_graveyard_zone (id, ghost_zone, faction) VALUES (?, ?, ?)", CONNECTION_ASYNC);
- PrepareStatement(WORLD_DEL_GRAVEYARD_ZONE, "DELETE FROM game_graveyard_zone WHERE id = ? AND ghost_zone = ? AND faction = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_INS_GRAVEYARD_ZONE, "INSERT INTO graveyard_zone (ID, GhostZone, Faction) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_DEL_GRAVEYARD_ZONE, "DELETE FROM graveyard_zone WHERE ID = ? AND GhostZone = ? AND Faction = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_INS_GAME_TELE, "INSERT INTO game_tele (id, position_x, position_y, position_z, orientation, map, name) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(WORLD_DEL_GAME_TELE, "DELETE FROM game_tele WHERE name = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_INS_NPC_VENDOR, "INSERT INTO npc_vendor (entry, item, maxcount, incrtime, extendedcost) VALUES(?, ?, ?, ?, ?)", CONNECTION_ASYNC);
diff --git a/src/server/database/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h
index 6ac4ce589e3..e0a02423446 100644
--- a/src/server/database/Database/Implementation/WorldDatabase.h
+++ b/src/server/database/Database/Implementation/WorldDatabase.h
@@ -103,7 +103,7 @@ enum WorldDatabaseStatements
MAX_WORLDDATABASE_STATEMENTS
};
-class WorldDatabaseConnection : public MySQLConnection
+class TC_DATABASE_API WorldDatabaseConnection : public MySQLConnection
{
public:
typedef WorldDatabaseStatements Statements;
diff --git a/src/server/database/Database/MySQLConnection.cpp b/src/server/database/Database/MySQLConnection.cpp
index 41dd61d3c3a..93d2a35f310 100644
--- a/src/server/database/Database/MySQLConnection.cpp
+++ b/src/server/database/Database/MySQLConnection.cpp
@@ -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:
diff --git a/src/server/database/Database/MySQLConnection.h b/src/server/database/Database/MySQLConnection.h
index a981caa607e..566995988c0 100644
--- a/src/server/database/Database/MySQLConnection.h
+++ b/src/server/database/Database/MySQLConnection.h
@@ -35,7 +35,7 @@ enum ConnectionFlags
CONNECTION_BOTH = CONNECTION_ASYNC | CONNECTION_SYNCH
};
-struct MySQLConnectionInfo
+struct TC_DATABASE_API MySQLConnectionInfo
{
explicit MySQLConnectionInfo(std::string const& infoString)
{
@@ -62,7 +62,7 @@ struct MySQLConnectionInfo
typedef std::map<uint32 /*index*/, std::pair<std::string /*query*/, ConnectionFlags /*sync/async*/> > PreparedStatementMap;
-class MySQLConnection
+class TC_DATABASE_API MySQLConnection
{
template <class T> friend class DatabaseWorkerPool;
friend class PingOperation;
@@ -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;
diff --git a/src/server/database/Database/MySQLThreading.h b/src/server/database/Database/MySQLThreading.h
index 1cfa11d7e5b..b6083500989 100644
--- a/src/server/database/Database/MySQLThreading.h
+++ b/src/server/database/Database/MySQLThreading.h
@@ -20,7 +20,7 @@
#include "Log.h"
-class MySQL
+class TC_DATABASE_API MySQL
{
public:
static void Library_Init()
diff --git a/src/server/database/Database/PreparedStatement.cpp b/src/server/database/Database/PreparedStatement.cpp
index 848a923c75d..119f1d4c93b 100644
--- a/src/server/database/Database/PreparedStatement.cpp
+++ b/src/server/database/Database/PreparedStatement.cpp
@@ -344,7 +344,7 @@ void MySQLPreparedStatement::setString(const uint8 index, const char* value)
CheckValidIndex(index);
m_paramsSet[index] = true;
MYSQL_BIND* param = &m_bind[index];
- size_t len = strlen(value) + 1;
+ uint32 len = uint32(strlen(value) + 1);
param->buffer_type = MYSQL_TYPE_VAR_STRING;
delete [] static_cast<char *>(param->buffer);
param->buffer = new char[len];
diff --git a/src/server/database/Database/PreparedStatement.h b/src/server/database/Database/PreparedStatement.h
index 7d6c98463d0..faaec27014f 100644
--- a/src/server/database/Database/PreparedStatement.h
+++ b/src/server/database/Database/PreparedStatement.h
@@ -70,7 +70,7 @@ struct PreparedStatementData
class MySQLPreparedStatement;
//- Upper-level class that is used in code
-class PreparedStatement
+class TC_DATABASE_API PreparedStatement
{
friend class PreparedStatementTask;
friend class MySQLPreparedStatement;
@@ -109,7 +109,7 @@ class PreparedStatement
//- 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
+class TC_DATABASE_API MySQLPreparedStatement
{
friend class MySQLConnection;
friend class PreparedStatement;
@@ -157,7 +157,7 @@ typedef std::future<PreparedQueryResult> PreparedQueryResultFuture;
typedef std::promise<PreparedQueryResult> PreparedQueryResultPromise;
//- Lower-level class, enqueuable operation
-class PreparedStatementTask : public SQLOperation
+class TC_DATABASE_API PreparedStatementTask : public SQLOperation
{
public:
PreparedStatementTask(PreparedStatement* stmt, bool async = false);
diff --git a/src/server/database/Database/QueryCallback.h b/src/server/database/Database/QueryCallback.h
new file mode 100644
index 00000000000..5f6ae74da4f
--- /dev/null
+++ b/src/server/database/Database/QueryCallback.h
@@ -0,0 +1,209 @@
+/*
+ * 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/>.
+ */
+
+#ifndef _QUERY_CALLBACK_H
+#define _QUERY_CALLBACK_H
+
+#include <future>
+#include "QueryResult.h"
+
+typedef std::future<QueryResult> QueryResultFuture;
+typedef std::promise<QueryResult> QueryResultPromise;
+typedef std::future<PreparedQueryResult> PreparedQueryResultFuture;
+typedef std::promise<PreparedQueryResult> PreparedQueryResultPromise;
+
+#define CALLBACK_STAGE_INVALID uint8(-1)
+
+template <typename Result, typename ParamType, bool chain = false>
+class QueryCallback
+{
+ public:
+ QueryCallback() : _param(), _stage(chain ? 0 : CALLBACK_STAGE_INVALID) { }
+
+ //! The parameter of this function should be a resultset returned from either .AsyncQuery or .AsyncPQuery
+ void SetFutureResult(std::future<Result> value)
+ {
+ _result = std::move(value);
+ }
+
+ std::future<Result>& GetFutureResult()
+ {
+ return _result;
+ }
+
+ int IsReady()
+ {
+ return _result.valid() && _result.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
+ }
+
+ void GetResult(Result& res)
+ {
+ res = _result.get();
+ }
+
+ void FreeResult()
+ {
+ // Nothing to do here, the constructor of std::future will take care of the cleanup
+ }
+
+ void SetParam(ParamType value)
+ {
+ _param = value;
+ }
+
+ ParamType GetParam()
+ {
+ return _param;
+ }
+
+ //! Resets the stage of the callback chain
+ void ResetStage()
+ {
+ if (!chain)
+ return;
+
+ _stage = 0;
+ }
+
+ //! Advances the callback chain to the next stage, so upper level code can act on its results accordingly
+ void NextStage()
+ {
+ if (!chain)
+ return;
+
+ ++_stage;
+ }
+
+ //! Returns the callback stage (or CALLBACK_STAGE_INVALID if invalid)
+ uint8 GetStage()
+ {
+ return _stage;
+ }
+
+ //! Resets all underlying variables (param, result and stage)
+ void Reset()
+ {
+ SetParam(ParamType());
+ FreeResult();
+ ResetStage();
+ }
+
+ private:
+ std::future<Result> _result;
+ ParamType _param;
+ uint8 _stage;
+
+ QueryCallback(QueryCallback const& right) = delete;
+ QueryCallback& operator=(QueryCallback const& right) = delete;
+};
+
+template <typename Result, typename ParamType1, typename ParamType2, bool chain = false>
+class QueryCallback_2
+{
+ public:
+ QueryCallback_2() : _stage(chain ? 0 : CALLBACK_STAGE_INVALID) { }
+
+ //! The parameter of this function should be a resultset returned from either .AsyncQuery or .AsyncPQuery
+ void SetFutureResult(std::future<Result> value)
+ {
+ _result = std::move(value);
+ }
+
+ std::future<Result>& GetFutureResult()
+ {
+ return _result;
+ }
+
+ int IsReady()
+ {
+ return _result.valid() && _result.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
+ }
+
+ void GetResult(Result& res)
+ {
+ res = _result.get();
+ }
+
+ void FreeResult()
+ {
+ // Nothing to do here, the constructor of std::future will take care of the cleanup
+ }
+
+ void SetFirstParam(ParamType1 value)
+ {
+ _param_1 = value;
+ }
+
+ void SetSecondParam(ParamType2 value)
+ {
+ _param_2 = value;
+ }
+
+ ParamType1 GetFirstParam()
+ {
+ return _param_1;
+ }
+
+ ParamType2 GetSecondParam()
+ {
+ return _param_2;
+ }
+
+ //! Resets the stage of the callback chain
+ void ResetStage()
+ {
+ if (!chain)
+ return;
+
+ _stage = 0;
+ }
+
+ //! Advances the callback chain to the next stage, so upper level code can act on its results accordingly
+ void NextStage()
+ {
+ if (!chain)
+ return;
+
+ ++_stage;
+ }
+
+ //! Returns the callback stage (or CALLBACK_STAGE_INVALID if invalid)
+ uint8 GetStage()
+ {
+ return _stage;
+ }
+
+ //! Resets all underlying variables (param, result and stage)
+ void Reset()
+ {
+ SetFirstParam(NULL);
+ SetSecondParam(NULL);
+ FreeResult();
+ ResetStage();
+ }
+
+ private:
+ std::future<Result> _result;
+ ParamType1 _param_1;
+ ParamType2 _param_2;
+ uint8 _stage;
+
+ QueryCallback_2(QueryCallback_2 const& right) = delete;
+ QueryCallback_2& operator=(QueryCallback_2 const& right) = delete;
+};
+
+#endif // _QUERY_CALLBACK_H
diff --git a/src/server/database/Database/QueryHolder.h b/src/server/database/Database/QueryHolder.h
index 9a5a03fda42..2446a4db2bd 100644
--- a/src/server/database/Database/QueryHolder.h
+++ b/src/server/database/Database/QueryHolder.h
@@ -20,7 +20,7 @@
#include <future>
-class SQLQueryHolder
+class TC_DATABASE_API SQLQueryHolder
{
friend class SQLQueryHolderTask;
private:
@@ -46,7 +46,7 @@ class SQLQueryHolder
typedef std::future<SQLQueryHolder*> QueryResultHolderFuture;
typedef std::promise<SQLQueryHolder*> QueryResultHolderPromise;
-class SQLQueryHolderTask : public SQLOperation
+class TC_DATABASE_API SQLQueryHolderTask : public SQLOperation
{
private:
SQLQueryHolder* m_holder;
diff --git a/src/server/database/Database/QueryResult.cpp b/src/server/database/Database/QueryResult.cpp
index f02457f67ca..db9e737830c 100644
--- a/src/server/database/Database/QueryResult.cpp
+++ b/src/server/database/Database/QueryResult.cpp
@@ -76,7 +76,7 @@ m_length(NULL)
std::size_t rowSize = 0;
for (uint32 i = 0; i < m_fieldCount; ++i)
{
- size_t size = Field::SizeForType(&field[i]);
+ uint32 size = Field::SizeForType(&field[i]);
rowSize += size;
m_rBind[i].buffer_type = field[i].type;
diff --git a/src/server/database/Database/QueryResult.h b/src/server/database/Database/QueryResult.h
index d4d63b5ec85..3b1691db1a6 100644
--- a/src/server/database/Database/QueryResult.h
+++ b/src/server/database/Database/QueryResult.h
@@ -27,7 +27,7 @@
#endif
#include <mysql.h>
-class ResultSet
+class TC_DATABASE_API ResultSet
{
public:
ResultSet(MYSQL_RES* result, MYSQL_FIELD* fields, uint64 rowCount, uint32 fieldCount);
@@ -60,7 +60,7 @@ class ResultSet
typedef std::shared_ptr<ResultSet> QueryResult;
-class PreparedResultSet
+class TC_DATABASE_API PreparedResultSet
{
public:
PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64 rowCount, uint32 fieldCount);
diff --git a/src/server/database/Database/SQLOperation.h b/src/server/database/Database/SQLOperation.h
index f0500d1f232..5b3032eab87 100644
--- a/src/server/database/Database/SQLOperation.h
+++ b/src/server/database/Database/SQLOperation.h
@@ -53,7 +53,7 @@ union SQLResultSetUnion
class MySQLConnection;
-class SQLOperation
+class TC_DATABASE_API SQLOperation
{
public:
SQLOperation(): m_conn(NULL) { }
diff --git a/src/server/database/Database/Transaction.h b/src/server/database/Database/Transaction.h
index 5780c0363d9..7b5b6addfe4 100644
--- a/src/server/database/Database/Transaction.h
+++ b/src/server/database/Database/Transaction.h
@@ -25,7 +25,7 @@
class PreparedStatement;
/*! Transactions, high level class. */
-class Transaction
+class TC_DATABASE_API Transaction
{
friend class TransactionTask;
friend class MySQLConnection;
@@ -58,7 +58,7 @@ class Transaction
typedef std::shared_ptr<Transaction> SQLTransaction;
/*! Low level class*/
-class TransactionTask : public SQLOperation
+class TC_DATABASE_API TransactionTask : public SQLOperation
{
template <class T> friend class DatabaseWorkerPool;
friend class DatabaseWorker;
diff --git a/src/server/database/Logging/AppenderDB.h b/src/server/database/Logging/AppenderDB.h
index a6acc66b48c..225ae969802 100644
--- a/src/server/database/Logging/AppenderDB.h
+++ b/src/server/database/Logging/AppenderDB.h
@@ -20,7 +20,7 @@
#include "Appender.h"
-class AppenderDB: public Appender
+class TC_DATABASE_API AppenderDB: public Appender
{
public:
typedef std::integral_constant<AppenderType, APPENDER_DB>::type TypeIndex;
diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp
index 170954a86f4..8515da9f6f8 100644
--- a/src/server/database/Updater/DBUpdater.cpp
+++ b/src/server/database/Updater/DBUpdater.cpp
@@ -21,57 +21,41 @@
#include "UpdateFetcher.h"
#include "DatabaseLoader.h"
#include "Config.h"
+#include "BuiltInConfig.h"
+#include "StartProcess.h"
#include <fstream>
#include <iostream>
#include <unordered_map>
-#include <boost/process.hpp>
-#include <boost/iostreams/stream.hpp>
-#include <boost/iostreams/copy.hpp>
-#include <boost/iostreams/device/file_descriptor.hpp>
-#include <boost/system/system_error.hpp>
-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();
- try
- {
- exe = search_path("mysql");
- }
- catch (std::runtime_error&)
+ if (auto path = Trinity::SearchExecutableInPath("mysql"))
{
- }
+ exe = std::move(*path);
- if (!exe.empty() && exists(exe))
- {
- // Correct the path to the cli
- corrected_path() = absolute(exe).generic_string();
- return true;
+ if (!exe.empty() && exists(exe))
+ {
+ // Correct the path to the cli
+ corrected_path() = absolute(exe).generic_string();
+ return true;
+ }
}
- TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\' or in path, correct the path in the *.conf (\"Updates.MySqlCLIPath\").",
+ TC_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;
@@ -85,16 +69,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 +85,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 +144,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<>
@@ -202,7 +178,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
// Path of temp file
static Path const temp("create_table.sql");
- // Create temporary query to use external mysql cli
+ // Create temporary query to use external MySQL CLi
std::ofstream file(temp.generic_string());
if (!file.is_open())
{
@@ -221,7 +197,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
}
catch (UpdateException&)
{
- TC_LOG_FATAL("sql.updates", "Failed to create database %s! Has the user `CREATE` priviliges?", pool.GetConnectionInfo()->database.c_str());
+ TC_LOG_FATAL("sql.updates", "Failed to create database %s! Does the user (named in *.conf) have `CREATE` privileges on the MySQL server?", pool.GetConnectionInfo()->database.c_str());
boost::filesystem::remove(temp);
return false;
}
@@ -239,7 +215,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))
{
@@ -304,7 +280,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
{
case LOCATION_REPOSITORY:
{
- TC_LOG_ERROR("sql.updates", ">> Base file \"%s\" is missing, try to clone the source again.",
+ TC_LOG_ERROR("sql.updates", ">> Base file \"%s\" is missing. Try fixing it by cloning the source again.",
base.generic_string().c_str());
break;
@@ -312,7 +288,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
case LOCATION_DOWNLOAD:
{
TC_LOG_ERROR("sql.updates", ">> File \"%s\" is missing, download it from \"https://github.com/TrinityCore/TrinityCore/releases\"" \
- " and place it in your server directory.", base.filename().generic_string().c_str());
+ " uncompress it and place the file TDB_full_world_(a_variable_name).sql in your worldserver directory.", base.filename().generic_string().c_str());
break;
}
}
@@ -379,7 +355,7 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
if (!std::isdigit(port_or_socket[0]))
{
- // We can't check here if host == "." because is named localhost if socket option is enabled
+ // We can't check if host == "." here, because it is named localhost if socket option is enabled
args.push_back("-P0");
args.push_back("--protocol=SOCKET");
args.push_back("-S" + port_or_socket);
@@ -400,55 +376,23 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
if (!database.empty())
args.push_back(database);
- // ToDo: use the existing query in memory as virtual file if possible
- file_descriptor_source source(path);
-
- uint32 ret;
- try
- {
- boost::process::pipe outPipe = create_pipe();
- boost::process::pipe errPipe = create_pipe();
-
- child c = execute(run_exe(
- boost::filesystem::absolute(DBUpdaterUtil::GetMySqlCli()).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)));
-
- file_descriptor_source mysqlOutfd(outPipe.source, close_handle);
- file_descriptor_source mysqlErrfd(errPipe.source, close_handle);
-
- stream<file_descriptor_source> mysqlOutStream(mysqlOutfd);
- stream<file_descriptor_source> mysqlErrStream(mysqlErrfd);
-
- std::stringstream out;
- std::stringstream err;
-
- copy(mysqlOutStream, out);
- copy(mysqlErrStream, err);
-
- TC_LOG_INFO("sql.updates", "%s", out.str().c_str());
- TC_LOG_ERROR("sql.updates", "%s", err.str().c_str());
-
- ret = wait_for_exit(c);
- }
- catch (boost::system::system_error&)
- {
- ret = EXIT_FAILURE;
- }
-
- source.close();
+ // Invokes a mysql process which doesn't leak credentials to logs
+ int const ret = Trinity::StartProcess(DBUpdaterUtil::GetCorrectedMySQLExecutable(), args,
+ "sql.updates", path.generic_string(), true);
if (ret != EXIT_SUCCESS)
{
TC_LOG_FATAL("sql.updates", "Applying of file \'%s\' to database \'%s\' failed!" \
- " If you are an user pull the latest revision from the repository. If you are a developer fix your sql query.",
+ " 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 TrinityCore 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 DBUpdater<LoginDatabaseConnection>;
-template class DBUpdater<WorldDatabaseConnection>;
-template class DBUpdater<CharacterDatabaseConnection>;
+template class TC_DATABASE_API DBUpdater<LoginDatabaseConnection>;
+template class TC_DATABASE_API DBUpdater<WorldDatabaseConnection>;
+template class TC_DATABASE_API DBUpdater<CharacterDatabaseConnection>;
diff --git a/src/server/database/Updater/DBUpdater.h b/src/server/database/Updater/DBUpdater.h
index c9792ffe060..cc5d3aad68b 100644
--- a/src/server/database/Updater/DBUpdater.h
+++ b/src/server/database/Updater/DBUpdater.h
@@ -23,7 +23,7 @@
#include <string>
#include <boost/filesystem.hpp>
-class UpdateException : public std::exception
+class TC_DATABASE_API UpdateException : public std::exception
{
public:
UpdateException(std::string const& msg) : _msg(msg) { }
@@ -41,7 +41,7 @@ enum BaseLocation
LOCATION_DOWNLOAD
};
-struct UpdateResult
+struct TC_DATABASE_API UpdateResult
{
UpdateResult()
: updated(0), recent(0), archived(0) { }
@@ -57,7 +57,7 @@ struct UpdateResult
class DBUpdaterUtil
{
public:
- static std::string GetMySqlCli();
+ static std::string GetCorrectedMySQLExecutable();
static bool CheckExecutable();
@@ -66,13 +66,11 @@ private:
};
template <class T>
-class DBUpdater
+class TC_DATABASE_API DBUpdater
{
public:
using Path = boost::filesystem::path;
- static std::string GetSourceDirectory();
-
static inline std::string GetConfigEntry();
static inline std::string GetTableName();
diff --git a/src/server/database/Updater/UpdateFetcher.cpp b/src/server/database/Updater/UpdateFetcher.cpp
index fd0dbdd4b5a..7dc0a307ca2 100644
--- a/src/server/database/Updater/UpdateFetcher.cpp
+++ b/src/server/database/Updater/UpdateFetcher.cpp
@@ -18,6 +18,7 @@
#include "UpdateFetcher.h"
#include "Log.h"
#include "Util.h"
+#include "SHA1.h"
#include <fstream>
#include <chrono>
@@ -25,7 +26,6 @@
#include <sstream>
#include <exception>
#include <unordered_map>
-#include <openssl/sha.h>
using namespace boost::filesystem;
@@ -137,24 +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());
- auto const start_pos = in.tellg();
- in.ignore(std::numeric_limits<std::streamsize>::max());
- auto const char_count = in.gcount();
- in.seekg(start_pos);
+ throw UpdateException("Opening the sql update failed!");
+ }
- SQLUpdate const update(new std::string(char_count, char{}));
+ auto update = [&in] {
+ std::ostringstream ss;
+ ss << in.rdbuf();
+ return ss.str();
+ }();
- in.read(&(*update)[0], update->size());
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();
@@ -200,11 +209,8 @@ 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);
+ // Calculate a Sha1 hash based on query content.
+ std::string const hash = CalculateSHA1Hash(ReadSQLUpdate(availableQuery.first));
UpdateMode mode = MODE_APPLY;
@@ -327,15 +333,6 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, bool const allow
return UpdateResult(importedUpdates, countRecentUpdates, countArchivedUpdates);
}
-std::string UpdateFetcher::CalculateHash(SQLUpdate 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);
-
- return ByteArrayToHexStr(digest, SHA_DIGEST_LENGTH);
-}
-
uint32 UpdateFetcher::Apply(Path const& path) const
{
using Time = std::chrono::high_resolution_clock;
@@ -347,7 +344,7 @@ uint32 UpdateFetcher::Apply(Path const& path) const
_applyFile(path);
// Return time the query took to apply
- return std::chrono::duration_cast<std::chrono::milliseconds>(Time::now() - begin).count();
+ return uint32(std::chrono::duration_cast<std::chrono::milliseconds>(Time::now() - begin).count());
}
void UpdateFetcher::UpdateEntry(AppliedFileEntry const& entry, uint32 const speed) const
diff --git a/src/server/database/Updater/UpdateFetcher.h b/src/server/database/Updater/UpdateFetcher.h
index 22a0d08c7f8..cabc3c2fce3 100644
--- a/src/server/database/Updater/UpdateFetcher.h
+++ b/src/server/database/Updater/UpdateFetcher.h
@@ -25,7 +25,7 @@
#include <memory>
#include <vector>
-class UpdateFetcher
+class TC_DATABASE_API UpdateFetcher
{
typedef boost::filesystem::path Path;
@@ -103,16 +103,15 @@ 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::shared_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;
uint32 Apply(Path const& path) const;