aboutsummaryrefslogtreecommitdiff
path: root/src/server/database
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/database')
-rw-r--r--src/server/database/CMakeLists.txt74
-rw-r--r--src/server/database/Database/AdhocStatement.cpp55
-rw-r--r--src/server/database/Database/AdhocStatement.h43
-rw-r--r--src/server/database/Database/DatabaseEnv.h46
-rw-r--r--src/server/database/Database/DatabaseLoader.cpp194
-rw-r--r--src/server/database/Database/DatabaseLoader.h71
-rw-r--r--src/server/database/Database/DatabaseWorker.cpp59
-rw-r--r--src/server/database/Database/DatabaseWorker.h46
-rw-r--r--src/server/database/Database/DatabaseWorkerPool.h564
-rw-r--r--src/server/database/Database/Field.cpp65
-rw-r--r--src/server/database/Database/Field.h382
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp614
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h550
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.cpp116
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.h136
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.cpp94
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.h119
-rw-r--r--src/server/database/Database/MySQLConnection.cpp542
-rw-r--r--src/server/database/Database/MySQLConnection.h139
-rw-r--r--src/server/database/Database/MySQLThreading.h37
-rw-r--r--src/server/database/Database/PreparedStatement.cpp479
-rw-r--r--src/server/database/Database/PreparedStatement.h174
-rw-r--r--src/server/database/Database/QueryHolder.cpp189
-rw-r--r--src/server/database/Database/QueryHolder.h66
-rw-r--r--src/server/database/Database/QueryResult.cpp224
-rw-r--r--src/server/database/Database/QueryResult.h111
-rw-r--r--src/server/database/Database/SQLOperation.h77
-rw-r--r--src/server/database/Database/Transaction.cpp87
-rw-r--r--src/server/database/Database/Transaction.h77
-rw-r--r--src/server/database/Logging/AppenderDB.cpp45
-rw-r--r--src/server/database/Logging/AppenderDB.h40
-rw-r--r--src/server/database/PrecompiledHeaders/databasePCH.cpp1
-rw-r--r--src/server/database/PrecompiledHeaders/databasePCH.h23
-rw-r--r--src/server/database/Updater/DBUpdater.cpp419
-rw-r--r--src/server/database/Updater/DBUpdater.h92
-rw-r--r--src/server/database/Updater/UpdateFetcher.cpp410
-rw-r--r--src/server/database/Updater/UpdateFetcher.h132
37 files changed, 6592 insertions, 0 deletions
diff --git a/src/server/database/CMakeLists.txt b/src/server/database/CMakeLists.txt
new file mode 100644
index 00000000000..3fa7ed10aaf
--- /dev/null
+++ b/src/server/database/CMakeLists.txt
@@ -0,0 +1,74 @@
+# Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+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
+#
+
+if (USE_COREPCH)
+ set(database_STAT_PCH_HDR PrecompiledHeaders/databasePCH.h)
+ set(database_STAT_PCH_SRC PrecompiledHeaders/databasePCH.cpp)
+endif()
+
+set(database_STAT_SRCS
+ ${database_STAT_SRCS}
+ ${sources_Database}
+ ${sources_Logging}
+ ${sources_Updater}
+)
+
+include_directories(
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/recastnavigation/Detour
+ ${CMAKE_SOURCE_DIR}/dep/SFMT
+ ${CMAKE_SOURCE_DIR}/dep/cppformat
+ ${CMAKE_SOURCE_DIR}/dep/utf8cpp
+ ${CMAKE_SOURCE_DIR}/dep/process
+ ${CMAKE_SOURCE_DIR}/src/server
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/Database
+ ${CMAKE_CURRENT_SOURCE_DIR}/Logging
+ ${CMAKE_CURRENT_SOURCE_DIR}/Updater
+ ${CMAKE_SOURCE_DIR}/src/common/
+ ${CMAKE_SOURCE_DIR}/src/common/Collision
+ ${CMAKE_SOURCE_DIR}/src/common/Collision/Management
+ ${CMAKE_SOURCE_DIR}/src/common/Collision/Models
+ ${CMAKE_SOURCE_DIR}/src/common/Debugging
+ ${CMAKE_SOURCE_DIR}/src/common/Threading
+ ${CMAKE_SOURCE_DIR}/src/common/Utilities
+ ${CMAKE_SOURCE_DIR}/src/common/Configuration
+ ${CMAKE_SOURCE_DIR}/src/common/Logging
+ ${CMAKE_SOURCE_DIR}/src/server/shared
+ ${MYSQL_INCLUDE_DIR}
+ ${OPENSSL_INCLUDE_DIR} # seems needed for Windows build
+ ${VALGRIND_INCLUDE_DIR}
+)
+
+GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
+
+add_library(database STATIC
+ ${database_STAT_SRCS}
+ ${database_STAT_PCH_SRC}
+)
+
+# Generate precompiled header
+if (USE_COREPCH)
+ add_cxx_pch(database ${database_STAT_PCH_HDR} ${database_STAT_PCH_SRC})
+endif ()
diff --git a/src/server/database/Database/AdhocStatement.cpp b/src/server/database/Database/AdhocStatement.cpp
new file mode 100644
index 00000000000..547ff967efe
--- /dev/null
+++ b/src/server/database/Database/AdhocStatement.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008-2015 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 "AdhocStatement.h"
+#include "MySQLConnection.h"
+
+/*! Basic, ad-hoc queries. */
+BasicStatementTask::BasicStatementTask(const char* sql, bool async) :
+m_result(nullptr)
+{
+ m_sql = strdup(sql);
+ m_has_result = async; // If the operation is async, then there's a result
+ if (async)
+ m_result = new QueryResultPromise();
+}
+
+BasicStatementTask::~BasicStatementTask()
+{
+ free((void*)m_sql);
+ if (m_has_result && m_result != nullptr)
+ delete m_result;
+}
+
+bool BasicStatementTask::Execute()
+{
+ if (m_has_result)
+ {
+ ResultSet* result = m_conn->Query(m_sql);
+ if (!result || !result->GetRowCount() || !result->NextRow())
+ {
+ delete result;
+ m_result->set_value(QueryResult(NULL));
+ return false;
+ }
+
+ m_result->set_value(QueryResult(result));
+ return true;
+ }
+
+ return m_conn->Execute(m_sql);
+}
diff --git a/src/server/database/Database/AdhocStatement.h b/src/server/database/Database/AdhocStatement.h
new file mode 100644
index 00000000000..c449e0f6e59
--- /dev/null
+++ b/src/server/database/Database/AdhocStatement.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008-2015 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 _ADHOCSTATEMENT_H
+#define _ADHOCSTATEMENT_H
+
+#include <future>
+#include "SQLOperation.h"
+
+typedef std::future<QueryResult> QueryResultFuture;
+typedef std::promise<QueryResult> QueryResultPromise;
+
+/*! Raw, ad-hoc query. */
+class BasicStatementTask : public SQLOperation
+{
+ public:
+ BasicStatementTask(const char* sql, bool async = false);
+ ~BasicStatementTask();
+
+ bool Execute() override;
+ QueryResultFuture GetFuture() const { return m_result->get_future(); }
+
+ private:
+ const char* m_sql; //- Raw query to be executed
+ bool m_has_result;
+ QueryResultPromise* m_result;
+};
+
+#endif
diff --git a/src/server/database/Database/DatabaseEnv.h b/src/server/database/Database/DatabaseEnv.h
new file mode 100644
index 00000000000..c3e11c5b98e
--- /dev/null
+++ b/src/server/database/Database/DatabaseEnv.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 DATABASEENV_H
+#define DATABASEENV_H
+
+#include "Common.h"
+#include "Errors.h"
+#include "Log.h"
+
+#include "Field.h"
+#include "QueryResult.h"
+
+#include "MySQLThreading.h"
+#include "Transaction.h"
+
+#define _LIKE_ "LIKE"
+#define _TABLE_SIM_ "`"
+#define _CONCAT3_(A, B, C) "CONCAT( " A ", " B ", " C " )"
+#define _OFFSET_ "LIMIT %d, 1"
+
+#include "Implementation/LoginDatabase.h"
+#include "Implementation/CharacterDatabase.h"
+#include "Implementation/WorldDatabase.h"
+
+extern WorldDatabaseWorkerPool WorldDatabase;
+extern CharacterDatabaseWorkerPool CharacterDatabase;
+extern LoginDatabaseWorkerPool LoginDatabase;
+
+#endif
+
diff --git a/src/server/database/Database/DatabaseLoader.cpp b/src/server/database/Database/DatabaseLoader.cpp
new file mode 100644
index 00000000000..a3e2083fff4
--- /dev/null
+++ b/src/server/database/Database/DatabaseLoader.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2008-2015 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 "DatabaseLoader.h"
+#include "DBUpdater.h"
+#include "Config.h"
+
+#include <mysqld_error.h>
+
+DatabaseLoader::DatabaseLoader(std::string const& logger, uint32 const defaultUpdateMask)
+ : _logger(logger), _autoSetup(sConfigMgr->GetBoolDefault("Updates.AutoSetup", true)),
+ _updateFlags(sConfigMgr->GetIntDefault("Updates.EnableDatabases", defaultUpdateMask))
+{
+}
+
+template <class T>
+DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::string const& name)
+{
+ bool const updatesEnabledForThis = DBUpdater<T>::IsEnabled(_updateFlags);
+
+ _open.push(std::make_pair([this, name, updatesEnabledForThis, &pool]() -> bool
+ {
+ std::string const dbString = sConfigMgr->GetStringDefault(name + "DatabaseInfo", "");
+ if (dbString.empty())
+ {
+ TC_LOG_ERROR(_logger.c_str(), "Database %s not specified in configuration file!", name.c_str());
+ return false;
+ }
+
+ uint8 const asyncThreads = uint8(sConfigMgr->GetIntDefault(name + "Database.WorkerThreads", 1));
+ if (asyncThreads < 1 || asyncThreads > 32)
+ {
+ TC_LOG_ERROR(_logger.c_str(), "%s database: invalid number of worker threads specified. "
+ "Please pick a value between 1 and 32.", name.c_str());
+ return false;
+ }
+
+ uint8 const synchThreads = uint8(sConfigMgr->GetIntDefault(name + "Database.SynchThreads", 1));
+
+ pool.SetConnectionInfo(dbString, asyncThreads, synchThreads);
+ if (uint32 error = pool.Open())
+ {
+ // Database does not exist
+ if ((error == ER_BAD_DB_ERROR) && updatesEnabledForThis && _autoSetup)
+ {
+ // Try to create the database and connect again if auto setup is enabled
+ if (DBUpdater<T>::Create(pool) && (!pool.Open()))
+ error = 0;
+ }
+
+ // If the error wasn't handled quit
+ if (error)
+ {
+ TC_LOG_ERROR("sql.driver", "\nDatabasePool %s NOT opened. There were errors opening the MySQL connections. Check your SQLDriverLogFile "
+ "for specific errors. Read wiki at http://collab.kpsn.org/display/tc/TrinityCore+Home", name.c_str());
+
+ return false;
+ }
+ }
+ return true;
+ },
+ [&pool]()
+ {
+ pool.Close();
+ }));
+
+ // Populate and update only if updates are enabled for this pool
+ if (updatesEnabledForThis)
+ {
+ _populate.push([this, name, &pool]() -> bool
+ {
+ if (!DBUpdater<T>::Populate(pool))
+ {
+ TC_LOG_ERROR(_logger.c_str(), "Could not populate the %s database, see log for details.", name.c_str());
+ return false;
+ }
+ return true;
+ });
+
+ _update.push([this, name, &pool]() -> bool
+ {
+ if (!DBUpdater<T>::Update(pool))
+ {
+ TC_LOG_ERROR(_logger.c_str(), "Could not update the %s database, see log for details.", name.c_str());
+ return false;
+ }
+ return true;
+ });
+ }
+
+ _prepare.push([this, name, &pool]() -> bool
+ {
+ if (!pool.PrepareStatements())
+ {
+ TC_LOG_ERROR(_logger.c_str(), "Could not prepare statements of the %s database, see log for details.", name.c_str());
+ return false;
+ }
+ return true;
+ });
+
+ return *this;
+}
+
+bool DatabaseLoader::Load()
+{
+ if (!_updateFlags)
+ TC_LOG_INFO("sql.updates", "Automatic database updates are disabled for all databases!");
+
+ if (!OpenDatabases())
+ return false;
+
+ if (!PopulateDatabases())
+ return false;
+
+ if (!UpdateDatabases())
+ return false;
+
+ if (!PrepareStatements())
+ return false;
+
+ return true;
+}
+
+bool DatabaseLoader::OpenDatabases()
+{
+ 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;
+}
+
+bool DatabaseLoader::PopulateDatabases()
+{
+ return Process(_populate);
+}
+
+bool DatabaseLoader::UpdateDatabases()
+{
+ return Process(_update);
+}
+
+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);
diff --git a/src/server/database/Database/DatabaseLoader.h b/src/server/database/Database/DatabaseLoader.h
new file mode 100644
index 00000000000..d35597ba807
--- /dev/null
+++ b/src/server/database/Database/DatabaseLoader.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2008-2015 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 DatabaseLoader_h__
+#define DatabaseLoader_h__
+
+#include "DatabaseWorkerPool.h"
+#include "DatabaseEnv.h"
+
+#include <stack>
+#include <functional>
+
+// A helper class to initiate all database worker pools,
+// handles updating, delays preparing of statements and cleans up on failure.
+class DatabaseLoader
+{
+public:
+ DatabaseLoader(std::string const& logger, uint32 const defaultUpdateMask);
+
+ // Register a database to the loader (lazy implemented)
+ template <class T>
+ DatabaseLoader& AddDatabase(DatabaseWorkerPool<T>& pool, std::string const& name);
+
+ // Load all databases
+ bool Load();
+
+ enum DatabaseTypeFlags
+ {
+ DATABASE_NONE = 0,
+
+ DATABASE_LOGIN = 1,
+ DATABASE_CHARACTER = 2,
+ DATABASE_WORLD = 4,
+
+ DATABASE_MASK_ALL = DATABASE_LOGIN | DATABASE_CHARACTER | DATABASE_WORLD
+ };
+
+private:
+ bool OpenDatabases();
+ bool PopulateDatabases();
+ bool UpdateDatabases();
+ bool PrepareStatements();
+
+ using Predicate = std::function<bool()>;
+
+ static bool Process(std::stack<Predicate>& stack);
+
+ 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;
+};
+
+#endif // DatabaseLoader_h__
diff --git a/src/server/database/Database/DatabaseWorker.cpp b/src/server/database/Database/DatabaseWorker.cpp
new file mode 100644
index 00000000000..56757ce12a0
--- /dev/null
+++ b/src/server/database/Database/DatabaseWorker.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008-2015 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"
+#include "DatabaseWorker.h"
+#include "SQLOperation.h"
+#include "ProducerConsumerQueue.h"
+
+DatabaseWorker::DatabaseWorker(ProducerConsumerQueue<SQLOperation*>* newQueue, MySQLConnection* connection)
+{
+ _connection = connection;
+ _queue = newQueue;
+ _cancelationToken = false;
+ _workerThread = std::thread(&DatabaseWorker::WorkerThread, this);
+}
+
+DatabaseWorker::~DatabaseWorker()
+{
+ _cancelationToken = true;
+
+ _queue->Cancel();
+
+ _workerThread.join();
+}
+
+void DatabaseWorker::WorkerThread()
+{
+ if (!_queue)
+ return;
+
+ for (;;)
+ {
+ SQLOperation* operation = nullptr;
+
+ _queue->WaitAndPop(operation);
+
+ if (_cancelationToken || !operation)
+ return;
+
+ operation->SetConnection(_connection);
+ operation->call();
+
+ delete operation;
+ }
+}
diff --git a/src/server/database/Database/DatabaseWorker.h b/src/server/database/Database/DatabaseWorker.h
new file mode 100644
index 00000000000..0ca476b1e66
--- /dev/null
+++ b/src/server/database/Database/DatabaseWorker.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008-2015 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 _WORKERTHREAD_H
+#define _WORKERTHREAD_H
+
+#include <thread>
+#include "ProducerConsumerQueue.h"
+
+class MySQLConnection;
+class SQLOperation;
+
+class DatabaseWorker
+{
+ public:
+ DatabaseWorker(ProducerConsumerQueue<SQLOperation*>* newQueue, MySQLConnection* connection);
+ ~DatabaseWorker();
+
+ private:
+ ProducerConsumerQueue<SQLOperation*>* _queue;
+ MySQLConnection* _connection;
+
+ void WorkerThread();
+ std::thread _workerThread;
+
+ std::atomic_bool _cancelationToken;
+
+ DatabaseWorker(DatabaseWorker const& right) = delete;
+ DatabaseWorker& operator=(DatabaseWorker const& right) = delete;
+};
+
+#endif
diff --git a/src/server/database/Database/DatabaseWorkerPool.h b/src/server/database/Database/DatabaseWorkerPool.h
new file mode 100644
index 00000000000..f5002c6943b
--- /dev/null
+++ b/src/server/database/Database/DatabaseWorkerPool.h
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2008-2015 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 _DATABASEWORKERPOOL_H
+#define _DATABASEWORKERPOOL_H
+
+#include "Common.h"
+#include "Callback.h"
+#include "MySQLConnection.h"
+#include "Transaction.h"
+#include "DatabaseWorker.h"
+#include "PreparedStatement.h"
+#include "Log.h"
+#include "QueryResult.h"
+#include "QueryHolder.h"
+#include "AdhocStatement.h"
+#include "StringFormat.h"
+
+#include <mysqld_error.h>
+#include <memory>
+
+#define MIN_MYSQL_SERVER_VERSION 50100u
+#define MIN_MYSQL_CLIENT_VERSION 50100u
+
+class PingOperation : public SQLOperation
+{
+ //! Operation for idle delaythreads
+ bool Execute() override
+ {
+ m_conn->Ping();
+ return true;
+ }
+};
+
+template <class T>
+class DatabaseWorkerPool
+{
+ private:
+ enum InternalIndex
+ {
+ IDX_ASYNC,
+ IDX_SYNCH,
+ IDX_SIZE
+ };
+
+ 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");
+ }
+
+ ~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;
+ }
+
+ uint32 Open()
+ {
+ WPFatal(_connectionInfo.get(), "Connection info was not set!");
+
+ TC_LOG_INFO("sql.driver", "Opening DatabasePool '%s'. Asynchronous connections: %u, synchronous connections: %u.",
+ GetDatabaseName(), _async_threads, _synch_threads);
+
+ uint32 error = OpenConnections(IDX_ASYNC, _async_threads);
+
+ if (error)
+ return error;
+
+ error = OpenConnections(IDX_SYNCH, _synch_threads);
+
+ if (!error)
+ {
+ TC_LOG_INFO("sql.driver", "DatabasePool '%s' opened successfully. %u total connections running.", GetDatabaseName(),
+ (_connectionCount[IDX_SYNCH] + _connectionCount[IDX_ASYNC]));
+ }
+
+ return error;
+ }
+
+ void Close()
+ {
+ TC_LOG_INFO("sql.driver", "Closing down DatabasePool '%s'.", GetDatabaseName());
+
+ for (uint8 i = 0; i < _connectionCount[IDX_ASYNC]; ++i)
+ {
+ T* t = _connections[IDX_ASYNC][i];
+ t->Close(); //! Closes the actualy MySQL connection.
+ }
+
+ TC_LOG_INFO("sql.driver", "Asynchronous connections on DatabasePool '%s' terminated. Proceeding with synchronous connections.",
+ GetDatabaseName());
+
+ //! Shut down the synchronous connections
+ //! There's no need for locking the connection, because DatabaseWorkerPool<>::Close
+ //! should only be called after any other thread tasks in the core have exited,
+ //! meaning there can be no concurrent access at this point.
+ for (uint8 i = 0; i < _connectionCount[IDX_SYNCH]; ++i)
+ _connections[IDX_SYNCH][i]->Close();
+
+ TC_LOG_INFO("sql.driver", "All connections on DatabasePool '%s' closed.", GetDatabaseName());
+ }
+
+ //! 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;
+ }
+
+ inline MySQLConnectionInfo const* GetConnectionInfo() const
+ {
+ return _connectionInfo.get();
+ }
+
+ /**
+ Delayed one-way statement methods.
+ */
+
+ //! Enqueues a one-way SQL operation in string format that will be executed asynchronously.
+ //! This method should only be used for queries that are only executed once, e.g during startup.
+ void Execute(const char* sql)
+ {
+ if (Trinity::IsFormatEmptyOrNull(sql))
+ return;
+
+ BasicStatementTask* task = new BasicStatementTask(sql);
+ Enqueue(task);
+ }
+
+ //! Enqueues a one-way SQL operation in string format -with variable args- that will be executed asynchronously.
+ //! This method should only be used for queries that are only executed once, e.g during startup.
+ template<typename Format, typename... Args>
+ void PExecute(Format&& sql, Args&&... args)
+ {
+ if (Trinity::IsFormatEmptyOrNull(sql))
+ return;
+
+ Execute(Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
+ }
+
+ //! Enqueues a one-way SQL operation in prepared statement format that will be executed asynchronously.
+ //! Statement must be prepared with CONNECTION_ASYNC flag.
+ void Execute(PreparedStatement* stmt)
+ {
+ PreparedStatementTask* task = new PreparedStatementTask(stmt);
+ Enqueue(task);
+ }
+
+ /**
+ Direct synchronous one-way statement methods.
+ */
+
+ //! Directly executes a one-way SQL operation in string format, that will block the calling thread until finished.
+ //! This method should only be used for queries that are only executed once, e.g during startup.
+ void DirectExecute(const char* sql)
+ {
+ if (!sql)
+ return;
+
+ T* t = GetFreeConnection();
+ t->Execute(sql);
+ t->Unlock();
+ }
+
+ //! Directly executes a one-way SQL operation in string format -with variable args-, that will block the calling thread until finished.
+ //! This method should only be used for queries that are only executed once, e.g during startup.
+ template<typename Format, typename... Args>
+ void DirectPExecute(Format&& sql, Args&&... args)
+ {
+ if (Trinity::IsFormatEmptyOrNull(sql))
+ return;
+
+ DirectExecute(Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
+ }
+
+ //! Directly executes a one-way SQL operation in prepared statement format, that will block the calling thread until finished.
+ //! Statement must be prepared with the CONNECTION_SYNCH flag.
+ void DirectExecute(PreparedStatement* stmt)
+ {
+ T* t = GetFreeConnection();
+ t->Execute(stmt);
+ t->Unlock();
+
+ //! Delete proxy-class. Not needed anymore
+ delete stmt;
+ }
+
+ /**
+ Synchronous query (with resultset) methods.
+ */
+
+ //! 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);
+ }
+
+ //! Directly executes an SQL query in string format -with variable args- that will block the calling thread until finished.
+ //! Returns reference counted auto pointer, no need for manual memory management in upper level code.
+ template<typename Format, typename... Args>
+ QueryResult PQuery(Format&& sql, T* conn, Args&&... args)
+ {
+ if (Trinity::IsFormatEmptyOrNull(sql))
+ return QueryResult(nullptr);
+
+ return Query(Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str(), conn);
+ }
+
+ //! Directly executes an SQL query in string format -with variable args- that will block the calling thread until finished.
+ //! Returns reference counted auto pointer, no need for manual memory management in upper level code.
+ template<typename Format, typename... Args>
+ QueryResult PQuery(Format&& sql, Args&&... args)
+ {
+ if (!sql)
+ return QueryResult(nullptr);
+
+ return Query(Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
+ }
+
+ //! 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);
+ }
+
+ /**
+ Asynchronous query (with resultset) methods.
+ */
+
+ //! 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;
+ }
+
+ //! Enqueues a query in string format -with variable args- that will set the value of the QueryResultFuture return object as soon as the query is executed.
+ //! The return value is then processed in ProcessQueryCallback methods.
+ template<typename Format, typename... Args>
+ QueryResultFuture AsyncPQuery(Format&& sql, Args&&... args)
+ {
+ return AsyncQuery(Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
+ }
+
+ //! 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;
+ }
+
+ //! 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;
+ }
+
+ /**
+ Transaction context methods.
+ */
+
+ //! Begins an automanaged transaction pointer that will automatically rollback if not commited. (Autocommit=0)
+ SQLTransaction BeginTransaction()
+ {
+ return SQLTransaction(new Transaction);
+ }
+
+ //! Enqueues a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations
+ //! were appended to the transaction will be respected during execution.
+ 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));
+ }
+
+ //! 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();
+ }
+
+ //! Method used to execute prepared statements in a diverse context.
+ //! Will be wrapped in a transaction if valid object is present, otherwise executed standalone.
+ void ExecuteOrAppend(SQLTransaction& trans, PreparedStatement* stmt)
+ {
+ if (!trans)
+ Execute(stmt);
+ else
+ trans->Append(stmt);
+ }
+
+ //! Method used to execute ad-hoc statements in a diverse context.
+ //! Will be wrapped in a transaction if valid object is present, otherwise executed standalone.
+ void ExecuteOrAppend(SQLTransaction& trans, const char* sql)
+ {
+ if (!trans)
+ Execute(sql);
+ else
+ trans->Append(sql);
+ }
+
+ /**
+ Other
+ */
+
+ //! Automanaged (internally) pointer to a prepared statement object for usage in upper level code.
+ //! Pointer is deleted in this->DirectExecute(PreparedStatement*), this->Query(PreparedStatement*) or PreparedStatementTask::~PreparedStatementTask.
+ //! This object is not tied to the prepared statement on the MySQL context yet until execution.
+ PreparedStatement* GetPreparedStatement(uint32 index)
+ {
+ return new PreparedStatement(index);
+ }
+
+ //! 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;
+ }
+
+ //! 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);
+ }
+
+ 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
+ ASSERT(false);
+
+ _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;
+ }
+
+ unsigned long EscapeString(char *to, const char *from, unsigned long length)
+ {
+ if (!to || !from || !length)
+ return 0;
+
+ return mysql_real_escape_string(_connections[IDX_SYNCH][0]->GetHandle(), to, from, length);
+ }
+
+ void Enqueue(SQLOperation* op)
+ {
+ _queue->Push(op);
+ }
+
+ //! Gets a free connection in the synchronous connection pool.
+ //! Caller MUST call t->Unlock() after touching the MySQL context to prevent deadlocks.
+ T* GetFreeConnection()
+ {
+ 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;
+ }
+
+ char const* GetDatabaseName() const
+ {
+ return _connectionInfo->database.c_str();
+ }
+
+ //! 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::unique_ptr<MySQLConnectionInfo> _connectionInfo;
+ uint8 _async_threads, _synch_threads;
+};
+
+#endif
diff --git a/src/server/database/Database/Field.cpp b/src/server/database/Database/Field.cpp
new file mode 100644
index 00000000000..89195b699b7
--- /dev/null
+++ b/src/server/database/Database/Field.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2008-2015 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 "Field.h"
+
+Field::Field()
+{
+ data.value = NULL;
+ data.type = MYSQL_TYPE_NULL;
+ data.length = 0;
+ data.raw = false;
+}
+
+Field::~Field()
+{
+ CleanUp();
+}
+
+void Field::SetByteValue(const void* newValue, const size_t newSize, enum_field_types newType, uint32 length)
+{
+ if (data.value)
+ CleanUp();
+
+ // This value stores raw bytes that have to be explicitly cast later
+ if (newValue)
+ {
+ data.value = new char[newSize];
+ memcpy(data.value, newValue, newSize);
+ data.length = length;
+ }
+ data.type = newType;
+ data.raw = true;
+}
+
+void Field::SetStructuredValue(char* newValue, enum_field_types newType)
+{
+ if (data.value)
+ CleanUp();
+
+ // This value stores somewhat structured data that needs function style casting
+ if (newValue)
+ {
+ size_t size = strlen(newValue);
+ data.value = new char [size+1];
+ strcpy((char*)data.value, newValue);
+ data.length = size;
+ }
+
+ data.type = newType;
+ data.raw = false;
+}
diff --git a/src/server/database/Database/Field.h b/src/server/database/Database/Field.h
new file mode 100644
index 00000000000..1bbd264482f
--- /dev/null
+++ b/src/server/database/Database/Field.h
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) 2008-2015 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 _FIELD_H
+#define _FIELD_H
+
+#include "Common.h"
+#include "Log.h"
+
+#include <mysql.h>
+
+class Field
+{
+ friend class ResultSet;
+ friend class PreparedResultSet;
+
+ public:
+
+ bool GetBool() const // Wrapper, actually gets integer
+ {
+ return GetUInt8() == 1 ? true : false;
+ }
+
+ uint8 GetUInt8() const
+ {
+ if (!data.value)
+ return 0;
+
+ #ifdef TRINITY_DEBUG
+ if (!IsType(MYSQL_TYPE_TINY))
+ {
+ TC_LOG_WARN("sql.sql", "Warning: GetUInt8() on non-tinyint field. Using type: %s.", FieldTypeToString(data.type));
+ return 0;
+ }
+ #endif
+
+ if (data.raw)
+ return *reinterpret_cast<uint8*>(data.value);
+ return static_cast<uint8>(strtoul((char*)data.value, nullptr, 10));
+ }
+
+ int8 GetInt8() const
+ {
+ if (!data.value)
+ return 0;
+
+ #ifdef TRINITY_DEBUG
+ if (!IsType(MYSQL_TYPE_TINY))
+ {
+ TC_LOG_WARN("sql.sql", "Warning: GetInt8() on non-tinyint field. Using type: %s.", FieldTypeToString(data.type));
+ return 0;
+ }
+ #endif
+
+ if (data.raw)
+ return *reinterpret_cast<int8*>(data.value);
+ return static_cast<int8>(strtol((char*)data.value, NULL, 10));
+ }
+
+ uint16 GetUInt16() const
+ {
+ if (!data.value)
+ return 0;
+
+ #ifdef TRINITY_DEBUG
+ if (!IsType(MYSQL_TYPE_SHORT) && !IsType(MYSQL_TYPE_YEAR))
+ {
+ TC_LOG_WARN("sql.sql", "Warning: GetUInt16() on non-smallint field. Using type: %s.", FieldTypeToString(data.type));
+ return 0;
+ }
+ #endif
+
+ if (data.raw)
+ return *reinterpret_cast<uint16*>(data.value);
+ return static_cast<uint16>(strtoul((char*)data.value, nullptr, 10));
+ }
+
+ int16 GetInt16() const
+ {
+ if (!data.value)
+ return 0;
+
+ #ifdef TRINITY_DEBUG
+ if (!IsType(MYSQL_TYPE_SHORT) && !IsType(MYSQL_TYPE_YEAR))
+ {
+ TC_LOG_WARN("sql.sql", "Warning: GetInt16() on non-smallint field. Using type: %s.", FieldTypeToString(data.type));
+ return 0;
+ }
+ #endif
+
+ if (data.raw)
+ return *reinterpret_cast<int16*>(data.value);
+ return static_cast<int16>(strtol((char*)data.value, NULL, 10));
+ }
+
+ uint32 GetUInt32() const
+ {
+ if (!data.value)
+ return 0;
+
+ #ifdef TRINITY_DEBUG
+ if (!IsType(MYSQL_TYPE_INT24) && !IsType(MYSQL_TYPE_LONG))
+ {
+ TC_LOG_WARN("sql.sql", "Warning: GetUInt32() on non-(medium)int field. Using type: %s.", FieldTypeToString(data.type));
+ return 0;
+ }
+ #endif
+
+ if (data.raw)
+ return *reinterpret_cast<uint32*>(data.value);
+ return static_cast<uint32>(strtoul((char*)data.value, nullptr, 10));
+ }
+
+ int32 GetInt32() const
+ {
+ if (!data.value)
+ return 0;
+
+ #ifdef TRINITY_DEBUG
+ if (!IsType(MYSQL_TYPE_INT24) && !IsType(MYSQL_TYPE_LONG))
+ {
+ TC_LOG_WARN("sql.sql", "Warning: GetInt32() on non-(medium)int field. Using type: %s.", FieldTypeToString(data.type));
+ return 0;
+ }
+ #endif
+
+ if (data.raw)
+ return *reinterpret_cast<int32*>(data.value);
+ return static_cast<int32>(strtol((char*)data.value, NULL, 10));
+ }
+
+ uint64 GetUInt64() const
+ {
+ if (!data.value)
+ return 0;
+
+ #ifdef TRINITY_DEBUG
+ if (!IsType(MYSQL_TYPE_LONGLONG) && !IsType(MYSQL_TYPE_BIT))
+ {
+ TC_LOG_WARN("sql.sql", "Warning: GetUInt64() on non-bigint field. Using type: %s.", FieldTypeToString(data.type));
+ return 0;
+ }
+ #endif
+
+ if (data.raw)
+ return *reinterpret_cast<uint64*>(data.value);
+ return static_cast<uint64>(strtoull((char*)data.value, nullptr, 10));
+ }
+
+ int64 GetInt64() const
+ {
+ if (!data.value)
+ return 0;
+
+ #ifdef TRINITY_DEBUG
+ if (!IsType(MYSQL_TYPE_LONGLONG) && !IsType(MYSQL_TYPE_BIT))
+ {
+ TC_LOG_WARN("sql.sql", "Warning: GetInt64() on non-bigint field. Using type: %s.", FieldTypeToString(data.type));
+ return 0;
+ }
+ #endif
+
+ if (data.raw)
+ return *reinterpret_cast<int64*>(data.value);
+ return static_cast<int64>(strtoll((char*)data.value, NULL, 10));
+ }
+
+ float GetFloat() const
+ {
+ if (!data.value)
+ return 0.0f;
+
+ #ifdef TRINITY_DEBUG
+ if (!IsType(MYSQL_TYPE_FLOAT))
+ {
+ TC_LOG_WARN("sql.sql", "Warning: GetFloat() on non-float field. Using type: %s.", FieldTypeToString(data.type));
+ return 0.0f;
+ }
+ #endif
+
+ if (data.raw)
+ return *reinterpret_cast<float*>(data.value);
+ return static_cast<float>(atof((char*)data.value));
+ }
+
+ double GetDouble() const
+ {
+ if (!data.value)
+ return 0.0f;
+
+ #ifdef TRINITY_DEBUG
+ if (!IsType(MYSQL_TYPE_DOUBLE))
+ {
+ TC_LOG_WARN("sql.sql", "Warning: GetDouble() on non-double field. Using type: %s.", FieldTypeToString(data.type));
+ return 0.0f;
+ }
+ #endif
+
+ if (data.raw)
+ return *reinterpret_cast<double*>(data.value);
+ return static_cast<double>(atof((char*)data.value));
+ }
+
+ char const* GetCString() const
+ {
+ if (!data.value)
+ return NULL;
+
+ #ifdef TRINITY_DEBUG
+ if (IsNumeric())
+ {
+ TC_LOG_WARN("sql.sql", "Error: GetCString() on numeric field. Using type: %s.", FieldTypeToString(data.type));
+ return NULL;
+ }
+ #endif
+ return static_cast<char const*>(data.value);
+
+ }
+
+ std::string GetString() const
+ {
+ if (!data.value)
+ return "";
+
+ if (data.raw)
+ {
+ char const* string = GetCString();
+ if (!string)
+ string = "";
+ return std::string(string, data.length);
+ }
+ return std::string((char*)data.value);
+ }
+
+ bool IsNull() const
+ {
+ return data.value == NULL;
+ }
+
+ protected:
+ Field();
+ ~Field();
+
+ #pragma pack(push, 1)
+ struct
+ {
+ uint32 length; // Length (prepared strings only)
+ void* value; // Actual data in memory
+ enum_field_types type; // Field type
+ bool raw; // Raw bytes? (Prepared statement or ad hoc)
+ } data;
+ #pragma pack(pop)
+
+ void SetByteValue(void const* newValue, size_t const newSize, enum_field_types newType, uint32 length);
+ void SetStructuredValue(char* newValue, enum_field_types newType);
+
+ void CleanUp()
+ {
+ delete[] ((char*)data.value);
+ data.value = NULL;
+ }
+
+ static size_t SizeForType(MYSQL_FIELD* field)
+ {
+ switch (field->type)
+ {
+ case MYSQL_TYPE_NULL:
+ return 0;
+ case MYSQL_TYPE_TINY:
+ return 1;
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_SHORT:
+ return 2;
+ case MYSQL_TYPE_INT24:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_FLOAT:
+ return 4;
+ case MYSQL_TYPE_DOUBLE:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_BIT:
+ return 8;
+
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ return sizeof(MYSQL_TIME);
+
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_VAR_STRING:
+ return field->max_length + 1;
+
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_NEWDECIMAL:
+ return 64;
+
+ case MYSQL_TYPE_GEOMETRY:
+ /*
+ Following types are not sent over the wire:
+ MYSQL_TYPE_ENUM:
+ MYSQL_TYPE_SET:
+ */
+ default:
+ TC_LOG_WARN("sql.sql", "SQL::SizeForType(): invalid field type %u", uint32(field->type));
+ return 0;
+ }
+ }
+
+ bool IsType(enum_field_types type) const
+ {
+ return data.type == type;
+ }
+
+ bool IsNumeric() const
+ {
+ return (data.type == MYSQL_TYPE_TINY ||
+ data.type == MYSQL_TYPE_SHORT ||
+ data.type == MYSQL_TYPE_INT24 ||
+ data.type == MYSQL_TYPE_LONG ||
+ data.type == MYSQL_TYPE_FLOAT ||
+ data.type == MYSQL_TYPE_DOUBLE ||
+ data.type == MYSQL_TYPE_LONGLONG );
+ }
+
+ private:
+ #ifdef TRINITY_DEBUG
+ static char const* FieldTypeToString(enum_field_types type)
+ {
+ switch (type)
+ {
+ case MYSQL_TYPE_BIT: return "BIT";
+ case MYSQL_TYPE_BLOB: return "BLOB";
+ case MYSQL_TYPE_DATE: return "DATE";
+ case MYSQL_TYPE_DATETIME: return "DATETIME";
+ case MYSQL_TYPE_NEWDECIMAL: return "NEWDECIMAL";
+ case MYSQL_TYPE_DECIMAL: return "DECIMAL";
+ case MYSQL_TYPE_DOUBLE: return "DOUBLE";
+ case MYSQL_TYPE_ENUM: return "ENUM";
+ case MYSQL_TYPE_FLOAT: return "FLOAT";
+ case MYSQL_TYPE_GEOMETRY: return "GEOMETRY";
+ case MYSQL_TYPE_INT24: return "INT24";
+ case MYSQL_TYPE_LONG: return "LONG";
+ case MYSQL_TYPE_LONGLONG: return "LONGLONG";
+ case MYSQL_TYPE_LONG_BLOB: return "LONG_BLOB";
+ case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB";
+ case MYSQL_TYPE_NEWDATE: return "NEWDATE";
+ case MYSQL_TYPE_NULL: return "NULL";
+ case MYSQL_TYPE_SET: return "SET";
+ case MYSQL_TYPE_SHORT: return "SHORT";
+ case MYSQL_TYPE_STRING: return "STRING";
+ case MYSQL_TYPE_TIME: return "TIME";
+ case MYSQL_TYPE_TIMESTAMP: return "TIMESTAMP";
+ case MYSQL_TYPE_TINY: return "TINY";
+ case MYSQL_TYPE_TINY_BLOB: return "TINY_BLOB";
+ case MYSQL_TYPE_VAR_STRING: return "VAR_STRING";
+ case MYSQL_TYPE_YEAR: return "YEAR";
+ default: return "-Unknown-";
+ }
+ }
+ #endif
+};
+
+#endif
+
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
new file mode 100644
index 00000000000..1efdb16804f
--- /dev/null
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2008-2015 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 "CharacterDatabase.h"
+
+void CharacterDatabaseConnection::DoPrepareStatements()
+{
+ if (!m_reconnecting)
+ m_stmts.resize(MAX_CHARACTERDATABASE_STATEMENTS);
+
+ PrepareStatement(CHAR_DEL_QUEST_POOL_SAVE, "DELETE FROM pool_quest_save WHERE pool_id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_QUEST_POOL_SAVE, "INSERT INTO pool_quest_save (pool_id, quest_id) VALUES (?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM, "DELETE FROM guild_bank_item WHERE guildid = ? AND TabId = ? AND SlotId = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_EXPIRED_BANS, "UPDATE character_banned SET active = 0 WHERE unbandate <= UNIX_TIMESTAMP() AND unbandate <> bandate", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_GUID_BY_NAME, "SELECT guid FROM characters WHERE name = ?", CONNECTION_BOTH);
+ PrepareStatement(CHAR_SEL_CHECK_NAME, "SELECT 1 FROM characters WHERE name = ?", CONNECTION_BOTH);
+ PrepareStatement(CHAR_SEL_CHECK_GUID, "SELECT 1 FROM characters WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_SUM_CHARS, "SELECT COUNT(guid) FROM characters WHERE account = ?", CONNECTION_BOTH);
+ PrepareStatement(CHAR_SEL_CHAR_CREATE_INFO, "SELECT level, race, class FROM characters WHERE account = ? LIMIT 0, ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHARACTER_BAN, "INSERT INTO character_banned VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?, 1)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHARACTER_BAN, "UPDATE character_banned SET active = 0 WHERE guid = ? AND active != 0", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHARACTER_BAN, "DELETE cb FROM character_banned cb INNER JOIN characters c ON c.guid = cb.guid WHERE c.account = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_BANINFO, "SELECT bandate, unbandate-bandate, active, unbandate, banreason, bannedby FROM character_banned WHERE guid = ? ORDER BY bandate ASC", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_GUID_BY_NAME_FILTER, "SELECT guid, name FROM characters WHERE name LIKE CONCAT('%%', ?, '%%')", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_BANINFO_LIST, "SELECT bandate, unbandate, bannedby, banreason FROM character_banned WHERE guid = ? ORDER BY unbandate", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_BANNED_NAME, "SELECT characters.name FROM characters, character_banned WHERE character_banned.guid = ? AND character_banned.guid = characters.guid", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_MAIL_LIST_COUNT, "SELECT COUNT(id) FROM mail WHERE receiver = ? ", CONNECTION_SYNCH);
+ 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, "
+ "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, "
+ "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 "
+ "LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY c.guid", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT guid, name FROM characters WHERE guid = ? AND account = ? AND (at_login & ?) = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_GUID_RACE_ACC_BY_NAME, "SELECT guid, race, account FROM characters WHERE name = ?", CONNECTION_BOTH);
+ 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_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);
+
+ PrepareStatement(CHAR_DEL_BATTLEGROUND_RANDOM_ALL, "DELETE FROM character_battleground_random", CONNECTION_ASYNC);
+ 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, "
+ "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, "
+ "health, power1, power2, power3, power4, power5, power6, power7, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, knownTitles, actionBars, grantableLevels "
+ "FROM characters WHERE guid = ?", CONNECTION_ASYNC);
+
+ PrepareStatement(CHAR_SEL_GROUP_MEMBER, "SELECT guid FROM group_member WHERE memberGuid = ?", CONNECTION_BOTH);
+ PrepareStatement(CHAR_SEL_CHARACTER_INSTANCE, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_AURAS, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
+ "base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges FROM character_aura WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_SPELL, "SELECT spell, active, disabled FROM character_spell WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUS, "SELECT quest, status, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, "
+ "itemcount1, itemcount2, itemcount3, itemcount4, playercount FROM character_queststatus WHERE guid = ? AND status <> 0", CONNECTION_ASYNC);
+
+ PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_DAILY, "SELECT quest, time FROM character_queststatus_daily WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_WEEKLY, "SELECT quest FROM character_queststatus_weekly WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_MONTHLY, "SELECT quest FROM character_queststatus_monthly WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUS_SEASONAL, "SELECT quest, event FROM character_queststatus_seasonal WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_DAILY, "DELETE FROM character_queststatus_daily WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_WEEKLY, "DELETE FROM character_queststatus_weekly WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_MONTHLY, "DELETE FROM character_queststatus_monthly WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHARACTER_QUESTSTATUS_SEASONAL, "DELETE FROM character_queststatus_seasonal WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHARACTER_QUESTSTATUS_DAILY, "INSERT INTO character_queststatus_daily (guid, quest, time) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHARACTER_QUESTSTATUS_WEEKLY, "INSERT INTO character_queststatus_weekly (guid, quest) VALUES (?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHARACTER_QUESTSTATUS_MONTHLY, "INSERT INTO character_queststatus_monthly (guid, quest) VALUES (?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHARACTER_QUESTSTATUS_SEASONAL, "INSERT INTO character_queststatus_seasonal (guid, quest, event) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_DAILY, "DELETE FROM character_queststatus_daily", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_WEEKLY, "DELETE FROM character_queststatus_weekly", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_MONTHLY, "DELETE FROM character_queststatus_monthly", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_SEASONAL_BY_EVENT, "DELETE FROM character_queststatus_seasonal WHERE event = ?", CONNECTION_ASYNC);
+
+ PrepareStatement(CHAR_SEL_CHARACTER_REPUTATION, "SELECT faction, standing, flags FROM character_reputation WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_INVENTORY, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, bag, slot, "
+ "item, itemEntry FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS, "SELECT a.button, a.action, a.type FROM character_action as a, characters as c WHERE a.guid = c.guid AND a.spec = c.activeTalentGroup AND a.guid = ? ORDER BY button", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_MAILCOUNT, "SELECT COUNT(id) FROM mail WHERE receiver = ? AND (checked & 1) = 0 AND deliver_time <= ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_MAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = ? AND (checked & 1) = 0", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_MAIL_COUNT, "SELECT COUNT(*) FROM mail WHERE receiver = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHARACTER_SOCIALLIST, "SELECT friend, flags, note FROM character_social JOIN characters ON characters.guid = character_social.friend WHERE character_social.guid = ? AND deleteinfos_name IS NULL LIMIT 255", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, item, time FROM character_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_DECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_GUILD_MEMBER, "SELECT guildid, rank FROM guild_member WHERE guid = ?", CONNECTION_BOTH);
+ PrepareStatement(CHAR_SEL_GUILD_MEMBER_EXTENDED, "SELECT g.guildid, g.name, gr.rname, gr.rid, gm.pnote, gm.offnote "
+ "FROM guild g JOIN guild_member gm ON g.guildid = gm.guildid "
+ "JOIN guild_rank gr ON g.guildid = gr.guildid AND gm.rank = gr.rid WHERE gm.guid = ?", CONNECTION_BOTH);
+ PrepareStatement(CHAR_SEL_CHARACTER_ACHIEVEMENTS, "SELECT achievement, date FROM character_achievement WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_CRITERIAPROGRESS, "SELECT criteria, counter, date FROM character_achievement_progress WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_EQUIPMENTSETS, "SELECT setguid, setindex, name, iconname, ignore_mask, item0, item1, item2, item3, item4, item5, item6, item7, item8, "
+ "item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = ? ORDER BY setindex", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_BGDATA, "SELECT instanceId, team, joinX, joinY, joinZ, joinO, joinMapId, taxiStart, taxiEnd, mountSpell FROM character_battleground_data WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_GLYPHS, "SELECT talentGroup, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 FROM character_glyphs WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_TALENTS, "SELECT spell, talentGroup FROM character_talent WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_SKILLS, "SELECT skill, value, max FROM character_skills WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_RANDOMBG, "SELECT guid FROM character_battleground_random WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_BANNED, "SELECT guid FROM character_banned WHERE guid = ? AND active = 1", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_QUESTSTATUSREW, "SELECT quest FROM character_queststatus_rewarded WHERE guid = ? AND active = 1", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_ACCOUNT_INSTANCELOCKTIMES, "SELECT instanceId, releaseTime FROM account_instance_times WHERE accountId = ?", CONNECTION_ASYNC);
+
+ PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC, "SELECT button, action, type FROM character_action WHERE guid = ? AND spec = ? ORDER BY button", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_MAILITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, item_guid, itemEntry, owner_guid FROM mail_items mi JOIN item_instance ii ON mi.item_guid = ii.guid WHERE mail_id = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_AUCTION_ITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, itemguid, itemEntry FROM auctionhouse ah JOIN item_instance ii ON ah.itemguid = ii.guid", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_AUCTIONS, "SELECT id, auctioneerguid, itemguid, itemEntry, count, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit FROM auctionhouse ah INNER JOIN item_instance ii ON ii.guid = ah.itemguid", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_INS_AUCTION, "INSERT INTO auctionhouse (id, auctioneerguid, itemguid, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_AUCTION, "DELETE FROM auctionhouse WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_AUCTION_BID, "UPDATE auctionhouse SET buyguid = ?, lastbid = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_MAIL, "INSERT INTO mail(id, messageType, stationery, mailTemplateId, sender, receiver, subject, body, has_items, expire_time, deliver_time, money, cod, checked) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_MAIL_BY_ID, "DELETE FROM mail WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_MAIL_ITEM, "INSERT INTO mail_items(mail_id, item_guid, receiver) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_MAIL_ITEM, "DELETE FROM mail_items WHERE item_guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_INVALID_MAIL_ITEM, "DELETE FROM mail_items WHERE item_guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_EMPTY_EXPIRED_MAIL, "DELETE FROM mail WHERE expire_time < ? AND has_items = 0 AND body = ''", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_EXPIRED_MAIL, "SELECT id, messageType, sender, receiver, has_items, expire_time, cod, checked, mailTemplateId FROM mail WHERE expire_time < ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_EXPIRED_MAIL_ITEMS, "SELECT item_guid, itemEntry, mail_id FROM mail_items mi INNER JOIN item_instance ii ON ii.guid = mi.item_guid LEFT JOIN mail mm ON mi.mail_id = mm.id WHERE mm.id IS NOT NULL AND mm.expire_time < ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_UPD_MAIL_RETURNED, "UPDATE mail SET sender = ?, receiver = ?, expire_time = ?, deliver_time = ?, cod = 0, checked = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_MAIL_ITEM_RECEIVER, "UPDATE mail_items SET receiver = ? WHERE item_guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_ITEM_OWNER, "UPDATE item_instance SET owner_guid = ? WHERE guid = ?", CONNECTION_ASYNC);
+
+ PrepareStatement(CHAR_SEL_ITEM_REFUNDS, "SELECT player_guid, paidMoney, paidExtendedCost FROM item_refund_instance WHERE item_guid = ? AND player_guid = ? LIMIT 1", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_ITEM_BOP_TRADE, "SELECT allowedPlayers FROM item_soulbound_trade_data WHERE itemGuid = ? LIMIT 1", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_DEL_ITEM_BOP_TRADE, "DELETE FROM item_soulbound_trade_data WHERE itemGuid = ? LIMIT 1", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_ITEM_BOP_TRADE, "INSERT INTO item_soulbound_trade_data VALUES (?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_REP_INVENTORY_ITEM, "REPLACE INTO character_inventory (guid, bag, slot, item) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_REP_ITEM_INSTANCE, "REPLACE INTO item_instance (itemEntry, owner_guid, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, guid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_ITEM_INSTANCE, "UPDATE item_instance SET itemEntry = ?, owner_guid = ?, creatorGuid = ?, giftCreatorGuid = ?, count = ?, duration = ?, charges = ?, flags = ?, enchantments = ?, randomPropertyId = ?, durability = ?, playedTime = ?, text = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD, "UPDATE item_instance SET duration = ?, flags = ?, durability = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ITEM_INSTANCE, "DELETE FROM item_instance WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ITEM_INSTANCE_BY_OWNER, "DELETE FROM item_instance WHERE owner_guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_GIFT_OWNER, "UPDATE character_gifts SET guid = ? WHERE item_guid = ?", CONNECTION_ASYNC);
+ 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_ACCOUNT_BY_GUID, "SELECT account FROM characters WHERE guid = ?", 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);
+ PrepareStatement(CHAR_UPD_NAME_BY_GUID, "UPDATE characters SET name = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_DECLINED_NAME, "DELETE FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC);
+
+ // Guild handling
+ // 0: uint32, 1: string, 2: uint32, 3: string, 4: string, 5: uint64, 6-10: uint32, 11: uint64
+ PrepareStatement(CHAR_INS_GUILD, "INSERT INTO guild (guildid, name, leaderguid, info, motd, createdate, EmblemStyle, EmblemColor, BorderStyle, BorderColor, BackgroundColor, BankMoney) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GUILD, "DELETE FROM guild WHERE guildid = ?", CONNECTION_ASYNC); // 0: uint32
+ // 0: string, 1: uint32
+ PrepareStatement(CHAR_UPD_GUILD_NAME, "UPDATE guild SET name = ? WHERE guildid = ?", CONNECTION_ASYNC);
+ // 0: uint32, 1: uint32, 2: uint8, 4: string, 5: string
+ PrepareStatement(CHAR_INS_GUILD_MEMBER, "INSERT INTO guild_member (guildid, guid, rank, pnote, offnote) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GUILD_MEMBER, "DELETE FROM guild_member WHERE guid = ?", CONNECTION_ASYNC); // 0: uint32
+ PrepareStatement(CHAR_DEL_GUILD_MEMBERS, "DELETE FROM guild_member WHERE guildid = ?", CONNECTION_ASYNC); // 0: uint32
+ // 0: uint32, 1: uint8, 3: string, 4: uint32, 5: uint32
+ PrepareStatement(CHAR_INS_GUILD_RANK, "INSERT INTO guild_rank (guildid, rid, rname, rights, BankMoneyPerDay) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GUILD_RANKS, "DELETE FROM guild_rank WHERE guildid = ?", CONNECTION_ASYNC); // 0: uint32
+ PrepareStatement(CHAR_DEL_GUILD_LOWEST_RANK, "DELETE FROM guild_rank WHERE guildid = ? AND rid >= ?", CONNECTION_ASYNC); // 0: uint32, 1: uint8
+ PrepareStatement(CHAR_INS_GUILD_BANK_TAB, "INSERT INTO guild_bank_tab (guildid, TabId) VALUES (?, ?)", CONNECTION_ASYNC); // 0: uint32, 1: uint8
+ PrepareStatement(CHAR_DEL_GUILD_BANK_TAB, "DELETE FROM guild_bank_tab WHERE guildid = ? AND TabId = ?", CONNECTION_ASYNC); // 0: uint32, 1: uint8
+ PrepareStatement(CHAR_DEL_GUILD_BANK_TABS, "DELETE FROM guild_bank_tab WHERE guildid = ?", CONNECTION_ASYNC); // 0: uint32
+ // 0: uint32, 1: uint8, 2: uint8, 3: uint32, 4: uint32
+ PrepareStatement(CHAR_INS_GUILD_BANK_ITEM, "INSERT INTO guild_bank_item (guildid, TabId, SlotId, item_guid) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GUILD_BANK_ITEM, "DELETE FROM guild_bank_item WHERE guildid = ? AND TabId = ? AND SlotId = ?", CONNECTION_ASYNC); // 0: uint32, 1: uint8, 2: uint8
+ PrepareStatement(CHAR_DEL_GUILD_BANK_ITEMS, "DELETE FROM guild_bank_item WHERE guildid = ?", CONNECTION_ASYNC); // 0: uint32
+ // 0: uint32, 1: uint8, 2: uint8, 3: uint8, 4: uint32
+ PrepareStatement(CHAR_INS_GUILD_BANK_RIGHT, "INSERT INTO guild_bank_right (guildid, TabId, rid, gbright, SlotPerDay) VALUES (?, ?, ?, ?, ?) "
+ "ON DUPLICATE KEY UPDATE gbright = VALUES(gbright), SlotPerDay = VALUES(SlotPerDay)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GUILD_BANK_RIGHTS, "DELETE FROM guild_bank_right WHERE guildid = ?", CONNECTION_ASYNC); // 0: uint32
+ PrepareStatement(CHAR_DEL_GUILD_BANK_RIGHTS_FOR_RANK, "DELETE FROM guild_bank_right WHERE guildid = ? AND rid = ?", CONNECTION_ASYNC); // 0: uint32, 1: uint8
+ // 0-1: uint32, 2-3: uint8, 4-5: uint32, 6: uint16, 7: uint8, 8: uint64
+ PrepareStatement(CHAR_INS_GUILD_BANK_EVENTLOG, "INSERT INTO guild_bank_eventlog (guildid, LogGuid, TabId, EventType, PlayerGuid, ItemOrMoney, ItemStackCount, DestTabId, TimeStamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GUILD_BANK_EVENTLOG, "DELETE FROM guild_bank_eventlog WHERE guildid = ? AND LogGuid = ? AND TabId = ?", CONNECTION_ASYNC); // 0: uint32, 1: uint32, 2: uint8
+ PrepareStatement(CHAR_DEL_GUILD_BANK_EVENTLOGS, "DELETE FROM guild_bank_eventlog WHERE guildid = ?", CONNECTION_ASYNC); // 0: uint32
+ // 0-1: uint32, 2: uint8, 3-4: uint32, 5: uint8, 6: uint64
+ PrepareStatement(CHAR_INS_GUILD_EVENTLOG, "INSERT INTO guild_eventlog (guildid, LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GUILD_EVENTLOG, "DELETE FROM guild_eventlog WHERE guildid = ? AND LogGuid = ?", CONNECTION_ASYNC); // 0: uint32, 1: uint32
+ PrepareStatement(CHAR_DEL_GUILD_EVENTLOGS, "DELETE FROM guild_eventlog WHERE guildid = ?", CONNECTION_ASYNC); // 0: uint32
+ PrepareStatement(CHAR_UPD_GUILD_MEMBER_PNOTE, "UPDATE guild_member SET pnote = ? WHERE guid = ?", CONNECTION_ASYNC); // 0: string, 1: uint32
+ PrepareStatement(CHAR_UPD_GUILD_MEMBER_OFFNOTE, "UPDATE guild_member SET offnote = ? WHERE guid = ?", CONNECTION_ASYNC); // 0: string, 1: uint32
+ PrepareStatement(CHAR_UPD_GUILD_MEMBER_RANK, "UPDATE guild_member SET rank = ? WHERE guid = ?", CONNECTION_ASYNC); // 0: uint8, 1: uint32
+ PrepareStatement(CHAR_UPD_GUILD_MOTD, "UPDATE guild SET motd = ? WHERE guildid = ?", CONNECTION_ASYNC); // 0: string, 1: uint32
+ PrepareStatement(CHAR_UPD_GUILD_INFO, "UPDATE guild SET info = ? WHERE guildid = ?", CONNECTION_ASYNC); // 0: string, 1: uint32
+ PrepareStatement(CHAR_UPD_GUILD_LEADER, "UPDATE guild SET leaderguid = ? WHERE guildid = ?", CONNECTION_ASYNC); // 0: uint32, 1: uint32
+ PrepareStatement(CHAR_UPD_GUILD_RANK_NAME, "UPDATE guild_rank SET rname = ? WHERE rid = ? AND guildid = ?", CONNECTION_ASYNC); // 0: string, 1: uint8, 2: uint32
+ PrepareStatement(CHAR_UPD_GUILD_RANK_RIGHTS, "UPDATE guild_rank SET rights = ? WHERE rid = ? AND guildid = ?", CONNECTION_ASYNC); // 0: uint32, 1: uint8, 2: uint32
+ // 0-5: uint32
+ PrepareStatement(CHAR_UPD_GUILD_EMBLEM_INFO, "UPDATE guild SET EmblemStyle = ?, EmblemColor = ?, BorderStyle = ?, BorderColor = ?, BackgroundColor = ? WHERE guildid = ?", CONNECTION_ASYNC);
+ // 0: string, 1: string, 2: uint32, 3: uint8
+ PrepareStatement(CHAR_UPD_GUILD_BANK_TAB_INFO, "UPDATE guild_bank_tab SET TabName = ?, TabIcon = ? WHERE guildid = ? AND TabId = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_GUILD_BANK_MONEY, "UPDATE guild SET BankMoney = ? WHERE guildid = ?", CONNECTION_ASYNC); // 0: uint64, 1: uint32
+ // 0: uint8, 1: uint32, 2: uint8, 3: uint32
+ PrepareStatement(CHAR_UPD_GUILD_BANK_EVENTLOG_TAB, "UPDATE guild_bank_eventlog SET TabId = ? WHERE guildid = ? AND TabId = ? AND LogGuid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_GUILD_RANK_BANK_MONEY, "UPDATE guild_rank SET BankMoneyPerDay = ? WHERE rid = ? AND guildid = ?", CONNECTION_ASYNC); // 0: uint32, 1: uint8, 2: uint32
+ PrepareStatement(CHAR_UPD_GUILD_BANK_TAB_TEXT, "UPDATE guild_bank_tab SET TabText = ? WHERE guildid = ? AND TabId = ?", CONNECTION_ASYNC); // 0: string, 1: uint32, 2: uint8
+
+ PrepareStatement(CHAR_INS_GUILD_MEMBER_WITHDRAW,
+ "INSERT INTO guild_member_withdraw (guid, tab0, tab1, tab2, tab3, tab4, tab5, money) VALUES (?, ?, ?, ?, ?, ?, ?, ?) "
+ "ON DUPLICATE KEY UPDATE tab0 = VALUES (tab0), tab1 = VALUES (tab1), tab2 = VALUES (tab2), tab3 = VALUES (tab3), tab4 = VALUES (tab4), tab5 = VALUES (tab5)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GUILD_MEMBER_WITHDRAW, "TRUNCATE guild_member_withdraw", CONNECTION_ASYNC);
+
+ // 0: uint32, 1: uint32, 2: uint32
+ PrepareStatement(CHAR_SEL_CHAR_DATA_FOR_GUILD, "SELECT name, level, class, zone, account FROM characters WHERE guid = ?", CONNECTION_SYNCH);
+
+ // Chat channel handling
+ PrepareStatement(CHAR_SEL_CHANNEL, "SELECT announce, ownership, password, bannedList FROM channels WHERE name = ? AND team = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_INS_CHANNEL, "INSERT INTO channels(name, team, lastUsed) VALUES (?, ?, UNIX_TIMESTAMP())", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHANNEL, "UPDATE channels SET announce = ?, ownership = ?, password = ?, bannedList = ?, lastUsed = UNIX_TIMESTAMP() WHERE name = ? AND team = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHANNEL_USAGE, "UPDATE channels SET lastUsed = UNIX_TIMESTAMP() WHERE name = ? AND team = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHANNEL_OWNERSHIP, "UPDATE channels SET ownership = ? WHERE name LIKE ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_OLD_CHANNELS, "DELETE FROM channels WHERE ownership = 1 AND lastUsed + ? < UNIX_TIMESTAMP()", CONNECTION_ASYNC);
+
+ // Equipmentsets
+ PrepareStatement(CHAR_UPD_EQUIP_SET, "UPDATE character_equipmentsets SET name=?, iconname=?, ignore_mask=?, item0=?, item1=?, item2=?, item3=?, "
+ "item4=?, item5=?, item6=?, item7=?, item8=?, item9=?, item10=?, item11=?, item12=?, item13=?, item14=?, item15=?, item16=?, "
+ "item17=?, item18=? WHERE guid=? AND setguid=? AND setindex=?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_EQUIP_SET, "INSERT INTO character_equipmentsets (guid, setguid, setindex, name, iconname, ignore_mask, item0, item1, item2, item3, "
+ "item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_EQUIP_SET, "DELETE FROM character_equipmentsets WHERE setguid=?", CONNECTION_ASYNC);
+
+ // Auras
+ PrepareStatement(CHAR_INS_AURA, "INSERT INTO character_aura (guid, casterGuid, itemGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+
+ // Account data
+ PrepareStatement(CHAR_SEL_ACCOUNT_DATA, "SELECT type, time, data FROM account_data WHERE accountId = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_REP_ACCOUNT_DATA, "REPLACE INTO account_data (accountId, type, time, data) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ACCOUNT_DATA, "DELETE FROM account_data WHERE accountId = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PLAYER_ACCOUNT_DATA, "SELECT type, time, data FROM character_account_data WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_REP_PLAYER_ACCOUNT_DATA, "REPLACE INTO character_account_data(guid, type, time, data) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PLAYER_ACCOUNT_DATA, "DELETE FROM character_account_data WHERE guid = ?", CONNECTION_ASYNC);
+
+ // Tutorials
+ PrepareStatement(CHAR_SEL_TUTORIALS, "SELECT tut0, tut1, tut2, tut3, tut4, tut5, tut6, tut7 FROM account_tutorial WHERE accountId = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_HAS_TUTORIALS, "SELECT 1 FROM account_tutorial WHERE accountId = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_INS_TUTORIALS, "INSERT INTO account_tutorial(tut0, tut1, tut2, tut3, tut4, tut5, tut6, tut7, accountId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_TUTORIALS, "UPDATE account_tutorial SET tut0 = ?, tut1 = ?, tut2 = ?, tut3 = ?, tut4 = ?, tut5 = ?, tut6 = ?, tut7 = ? WHERE accountId = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_TUTORIALS, "DELETE FROM account_tutorial WHERE accountId = ?", CONNECTION_ASYNC);
+
+ // Instance saves
+ PrepareStatement(CHAR_INS_INSTANCE_SAVE, "INSERT INTO instance (id, map, resettime, difficulty, completedEncounters, data) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_INSTANCE_DATA, "UPDATE instance SET completedEncounters=?, data=? WHERE id=?", CONNECTION_ASYNC);
+
+ // Game event saves
+ PrepareStatement(CHAR_DEL_GAME_EVENT_SAVE, "DELETE FROM game_event_save WHERE eventEntry = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_GAME_EVENT_SAVE, "INSERT INTO game_event_save (eventEntry, state, next_start) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+
+ // Game event condition saves
+ PrepareStatement(CHAR_DEL_ALL_GAME_EVENT_CONDITION_SAVE, "DELETE FROM game_event_condition_save WHERE eventEntry = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GAME_EVENT_CONDITION_SAVE, "DELETE FROM game_event_condition_save WHERE eventEntry = ? AND condition_id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_GAME_EVENT_CONDITION_SAVE, "INSERT INTO game_event_condition_save (eventEntry, condition_id, done) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+
+ // Petitions
+ PrepareStatement(CHAR_SEL_PETITION, "SELECT ownerguid, name, type FROM petition WHERE petitionguid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PETITION_SIGNATURE, "SELECT playerguid FROM petition_sign WHERE petitionguid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_DEL_ALL_PETITION_SIGNATURES, "DELETE FROM petition_sign WHERE playerguid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PETITION_SIGNATURE, "DELETE FROM petition_sign WHERE playerguid = ? AND type = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PETITION_BY_OWNER, "SELECT petitionguid FROM petition WHERE ownerguid = ? AND type = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PETITION_TYPE, "SELECT type FROM petition WHERE petitionguid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PETITION_SIGNATURES, "SELECT ownerguid, (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = ?) AS signs, type FROM petition WHERE petitionguid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PETITION_SIG_BY_ACCOUNT, "SELECT playerguid FROM petition_sign WHERE player_account = ? AND petitionguid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PETITION_OWNER_BY_GUID, "SELECT ownerguid FROM petition WHERE petitionguid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PETITION_SIG_BY_GUID, "SELECT ownerguid, petitionguid FROM petition_sign WHERE playerguid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PETITION_SIG_BY_GUID_TYPE, "SELECT ownerguid, petitionguid FROM petition_sign WHERE playerguid = ? AND type = ?", CONNECTION_SYNCH);
+
+ // Arena teams
+ PrepareStatement(CHAR_SEL_CHARACTER_ARENAINFO, "SELECT arenaTeamId, weekGames, seasonGames, seasonWins, personalRating FROM arena_team_member WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_ARENA_TEAM, "INSERT INTO arena_team (arenaTeamId, name, captainGuid, type, rating, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_ARENA_TEAM_MEMBER, "INSERT INTO arena_team_member (arenaTeamId, guid) VALUES (?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ARENA_TEAM, "DELETE FROM arena_team where arenaTeamId = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ARENA_TEAM_MEMBERS, "DELETE FROM arena_team_member WHERE arenaTeamId = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_ARENA_TEAM_CAPTAIN, "UPDATE arena_team SET captainGuid = ? WHERE arenaTeamId = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ARENA_TEAM_MEMBER, "DELETE FROM arena_team_member WHERE arenaTeamId = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_ARENA_TEAM_STATS, "UPDATE arena_team SET rating = ?, weekGames = ?, weekWins = ?, seasonGames = ?, seasonWins = ?, rank = ? WHERE arenaTeamId = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_ARENA_TEAM_MEMBER, "UPDATE arena_team_member SET personalRating = ?, weekGames = ?, weekWins = ?, seasonGames = ?, seasonWins = ? WHERE arenaTeamId = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHARACTER_ARENA_STATS, "DELETE FROM character_arena_stats WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_REP_CHARACTER_ARENA_STATS, "REPLACE INTO character_arena_stats (guid, slot, matchMakerRating) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PLAYER_ARENA_TEAMS, "SELECT arena_team_member.arenaTeamId FROM arena_team_member JOIN arena_team ON arena_team_member.arenaTeamId = arena_team.arenaTeamId WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_UPD_ARENA_TEAM_NAME, "UPDATE arena_team SET name = ? WHERE arenaTeamId = ?", CONNECTION_ASYNC);
+
+ // Character battleground data
+ PrepareStatement(CHAR_INS_PLAYER_BGDATA, "INSERT INTO character_battleground_data (guid, instanceId, team, joinX, joinY, joinZ, joinO, joinMapId, taxiStart, taxiEnd, mountSpell) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PLAYER_BGDATA, "DELETE FROM character_battleground_data WHERE guid = ?", CONNECTION_ASYNC);
+
+ // Character homebind
+ PrepareStatement(CHAR_INS_PLAYER_HOMEBIND, "INSERT INTO character_homebind (guid, mapId, zoneId, posX, posY, posZ) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_PLAYER_HOMEBIND, "UPDATE character_homebind SET mapId = ?, zoneId = ?, posX = ?, posY = ?, posZ = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PLAYER_HOMEBIND, "DELETE FROM character_homebind WHERE guid = ?", CONNECTION_ASYNC);
+
+ // Corpse
+ PrepareStatement(CHAR_SEL_CORPSES, "SELECT posX, posY, posZ, orientation, mapId, displayId, itemCache, bytes1, bytes2, guildId, flags, dynFlags, time, corpseType, instanceId, phaseMask, corpseGuid, guid FROM corpse WHERE corpseType <> 0", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_INS_CORPSE, "INSERT INTO corpse (corpseGuid, guid, posX, posY, posZ, orientation, mapId, displayId, itemCache, bytes1, bytes2, guildId, flags, dynFlags, time, corpseType, instanceId, phaseMask) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CORPSE, "DELETE FROM corpse WHERE corpseGuid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PLAYER_CORPSES, "DELETE FROM corpse WHERE guid = ? AND corpseType <> 0", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_OLD_CORPSES, "DELETE FROM corpse WHERE corpseType = 0 OR time < (UNIX_TIMESTAMP(NOW()) - ?)", CONNECTION_ASYNC);
+
+ // Creature respawn
+ PrepareStatement(CHAR_SEL_CREATURE_RESPAWNS, "SELECT guid, respawnTime FROM creature_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_REP_CREATURE_RESPAWN, "REPLACE INTO creature_respawn (guid, respawnTime, mapId, instanceId) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CREATURE_RESPAWN, "DELETE FROM creature_respawn WHERE guid = ? AND mapId = ? AND instanceId = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE, "DELETE FROM creature_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_MAX_CREATURE_RESPAWNS, "SELECT MAX(respawnTime), instanceId FROM creature_respawn WHERE instanceId > 0 GROUP BY instanceId", CONNECTION_SYNCH);
+
+ // Gameobject respawn
+ PrepareStatement(CHAR_SEL_GO_RESPAWNS, "SELECT guid, respawnTime FROM gameobject_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_REP_GO_RESPAWN, "REPLACE INTO gameobject_respawn (guid, respawnTime, mapId, instanceId) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GO_RESPAWN, "DELETE FROM gameobject_respawn WHERE guid = ? AND mapId = ? AND instanceId = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GO_RESPAWN_BY_INSTANCE, "DELETE FROM gameobject_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_ASYNC);
+
+ // GM Tickets
+ PrepareStatement(CHAR_SEL_GM_TICKETS, "SELECT id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp FROM gm_ticket", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_REP_GM_TICKET, "REPLACE INTO gm_ticket (id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GM_TICKET, "DELETE FROM gm_ticket WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PLAYER_GM_TICKETS, "DELETE FROM gm_ticket WHERE playerGuid = ?", CONNECTION_ASYNC);
+
+ // GM Survey/subsurvey/lag report
+ PrepareStatement(CHAR_INS_GM_SURVEY, "INSERT INTO gm_survey (guid, surveyId, mainSurvey, comment, createTime) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP(NOW()))", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_GM_SUBSURVEY, "INSERT INTO gm_subsurvey (surveyId, questionId, answer, answerComment) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_LAG_REPORT, "INSERT INTO lag_reports (guid, lagType, mapId, posX, posY, posZ, latency, createTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+
+ // LFG Data
+ PrepareStatement(CHAR_INS_LFG_DATA, "INSERT INTO lfg_data (guid, dungeon, state) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ 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, "
+ "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, 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=?,"
+ "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=?,"
+ "watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,power6=?,power7=?,latency=?,talentGroupsCount=?,activeTalentGroup=?,exploredZones=?,"
+ "equipmentCache=?,ammoId=?,knownTitles=?,actionBars=?,grantableLevels=?,online=? WHERE guid=?", CONNECTION_ASYNC);
+
+ PrepareStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG, "UPDATE characters SET at_login = at_login | ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_REM_AT_LOGIN_FLAG, "UPDATE characters set at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_ALL_AT_LOGIN_FLAGS, "UPDATE characters SET at_login = at_login | ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_BUG_REPORT, "INSERT INTO bugreport (type, content) VALUES(?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_PETITION_NAME, "UPDATE petition SET name = ? WHERE petitionguid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_PETITION_SIGNATURE, "INSERT INTO petition_sign (ownerguid, petitionguid, playerguid, player_account) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_ACCOUNT_ONLINE, "UPDATE characters SET online = 0 WHERE account = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_GROUP, "INSERT INTO groups (guid, leaderGuid, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raidDifficulty, masterLooterGuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_GROUP_MEMBER, "INSERT INTO group_member (guid, memberGuid, memberFlags, subgroup, roles) VALUES(?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GROUP_MEMBER, "DELETE FROM group_member WHERE memberGuid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GROUP_INSTANCE_PERM_BINDING, "DELETE FROM group_instance WHERE guid = ? AND instance = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_GROUP_LEADER, "UPDATE groups SET leaderGuid = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_GROUP_TYPE, "UPDATE groups SET groupType = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_GROUP_MEMBER_SUBGROUP, "UPDATE group_member SET subgroup = ? WHERE memberGuid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_GROUP_MEMBER_FLAG, "UPDATE group_member SET memberFlags = ? WHERE memberGuid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_GROUP_DIFFICULTY, "UPDATE groups SET difficulty = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_GROUP_RAID_DIFFICULTY, "UPDATE groups SET raiddifficulty = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ALL_GM_TICKETS, "TRUNCATE TABLE gm_ticket", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_INVALID_SPELL_TALENTS, "DELETE FROM character_talent WHERE spell = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_INVALID_SPELL_SPELLS, "DELETE FROM character_spell WHERE spell = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_DELETE_INFO, "UPDATE characters SET deleteInfos_Name = name, deleteInfos_Account = account, deleteDate = UNIX_TIMESTAMP(), name = '', account = 0 WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_RESTORE_DELETE_INFO, "UPDATE characters SET name = ?, account = ?, deleteDate = NULL, deleteInfos_Name = NULL, deleteInfos_Account = NULL WHERE deleteDate IS NOT NULL AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_ZONE, "UPDATE characters SET zone = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_LEVEL, "UPDATE characters SET level = ?, xp = 0 WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA, "DELETE FROM character_achievement_progress WHERE criteria = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_INVALID_ACHIEVMENT, "DELETE FROM character_achievement WHERE achievement = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_ADDON, "INSERT INTO addons (name, crc) VALUES (?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_INVALID_PET_SPELL, "DELETE FROM pet_spell WHERE spell = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GROUP_INSTANCE_BY_INSTANCE, "DELETE FROM group_instance WHERE instance = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GROUP_INSTANCE_BY_GUID, "DELETE FROM group_instance WHERE guid = ? AND instance = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_REP_GROUP_INSTANCE, "REPLACE INTO group_instance (guid, instance, permanent) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_INSTANCE_RESETTIME, "UPDATE instance SET resettime = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_GLOBAL_INSTANCE_RESETTIME, "UPDATE instance_reset SET resettime = ? WHERE mapid = ? AND difficulty = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_ONLINE, "UPDATE characters SET online = 1 WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_NAME_AT_LOGIN, "UPDATE characters set name = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_WORLDSTATE, "UPDATE worldstates SET value = ? WHERE entry = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_WORLDSTATE, "INSERT INTO worldstates (entry, value) VALUES (?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID, "DELETE FROM character_instance WHERE guid = ? AND instance = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_INSTANCE, "UPDATE character_instance SET instance = ?, permanent = ? WHERE guid = ? AND instance = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_INSTANCE, "INSERT INTO character_instance (guid, instance, permanent) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_GENDER_PLAYERBYTES, "UPDATE characters SET gender = ?, playerBytes = ?, playerBytes2 = ? 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);
+ PrepareStatement(CHAR_INS_CHARACTER_SOCIAL, "INSERT INTO character_social (guid, friend, flags) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHARACTER_SOCIAL, "DELETE FROM character_social WHERE guid = ? AND friend = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHARACTER_SOCIAL_NOTE, "UPDATE character_social SET note = ? WHERE guid = ? AND friend = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHARACTER_POSITION, "UPDATE characters SET position_x = ?, position_y = ?, position_z = ?, orientation = ?, map = ?, zone = ?, trans_x = 0, trans_y = 0, trans_z = 0, transguid = 0, taxi_path = '' WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_AURA_FROZEN, "SELECT characters.name, character_aura.remainTime FROM characters LEFT JOIN character_aura ON (characters.guid = character_aura.guid) WHERE character_aura.spell = 9454", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHARACTER_ONLINE, "SELECT name, account, map, zone FROM characters WHERE online > 0", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_GUID, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_DEL_INFO_BY_NAME, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL AND deleteInfos_Name LIKE CONCAT('%%', ?, '%%')", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_DEL_INFO, "SELECT guid, deleteInfos_Name, deleteInfos_Account, deleteDate FROM characters WHERE deleteDate IS NOT NULL", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID, "SELECT guid FROM characters WHERE account = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_PINFO, "SELECT totaltime, level, money, account, race, class, map, zone, gender, health, playerFlags FROM characters WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PINFO_BANS, "SELECT unbandate, bandate = unbandate, bannedby, banreason FROM character_banned WHERE guid = ? AND active ORDER BY bandate ASC LIMIT 1", CONNECTION_SYNCH);
+ //0: lowGUID
+ PrepareStatement(CHAR_SEL_PINFO_MAILS, "SELECT SUM(CASE WHEN (checked & 1) THEN 1 ELSE 0 END) AS 'readmail', COUNT(*) AS 'totalmail' FROM mail WHERE `receiver` = ?", CONNECTION_SYNCH);
+ //0: lowGUID
+ PrepareStatement(CHAR_SEL_PINFO_XP, "SELECT a.xp, b.guid FROM characters a LEFT JOIN guild_member b ON a.guid = b.guid WHERE a.guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_GUID_NAME_BY_ACC, "SELECT guid, name FROM characters WHERE account = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_POOL_QUEST_SAVE, "SELECT quest_id FROM pool_quest_save WHERE pool_id = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHARACTER_AT_LOGIN, "SELECT at_login FROM characters WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN, "SELECT class, level, at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_AT_LOGIN_TITLES, "SELECT at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_INSTANCE, "SELECT data, completedEncounters FROM instance WHERE map = ? AND id = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL, "SELECT id, messageType, mailTemplateId, sender, subject, body, money, has_items FROM mail WHERE receiver = ? AND has_items <> 0 AND cod <> 0", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_SOCIAL, "SELECT DISTINCT guid FROM character_social WHERE friend = ?", CONNECTION_SYNCH);
+ 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);
+ PrepareStatement(CHAR_SEL_MAIL_COUNT_ITEM, "SELECT COUNT(itemEntry) FROM mail_items mi INNER JOIN item_instance ii ON ii.guid = mi.item_guid WHERE itemEntry = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_AUCTIONHOUSE_COUNT_ITEM,"SELECT COUNT(itemEntry) FROM auctionhouse ah INNER JOIN item_instance ii ON ii.guid = ah.itemguid WHERE itemEntry = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_GUILD_BANK_COUNT_ITEM, "SELECT COUNT(itemEntry) FROM guild_bank_item gbi INNER JOIN item_instance ii ON ii.guid = gbi.item_guid WHERE itemEntry = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_INVENTORY_ITEM_BY_ENTRY, "SELECT ci.item, cb.slot AS bag, ci.slot, ci.guid, c.account, c.name FROM characters c "
+ "INNER JOIN character_inventory ci ON ci.guid = c.guid "
+ "INNER JOIN item_instance ii ON ii.guid = ci.item "
+ "LEFT JOIN character_inventory cb ON cb.item = ci.bag WHERE ii.itemEntry = ? LIMIT ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_MAIL_ITEMS_BY_ENTRY, "SELECT mi.item_guid, m.sender, m.receiver, cs.account, cs.name, cr.account, cr.name "
+ "FROM mail m INNER JOIN mail_items mi ON mi.mail_id = m.id INNER JOIN item_instance ii ON ii.guid = mi.item_guid "
+ "INNER JOIN characters cs ON cs.guid = m.sender INNER JOIN characters cr ON cr.guid = m.receiver WHERE ii.itemEntry = ? LIMIT ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_AUCTIONHOUSE_ITEM_BY_ENTRY, "SELECT ah.itemguid, ah.itemowner, c.account, c.name FROM auctionhouse ah INNER JOIN characters c ON c.guid = ah.itemowner INNER JOIN item_instance ii ON ii.guid = ah.itemguid WHERE ii.itemEntry = ? LIMIT ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_GUILD_BANK_ITEM_BY_ENTRY, "SELECT gi.item_guid, gi.guildid, g.name FROM guild_bank_item gi INNER JOIN guild g ON g.guildid = gi.guildid INNER JOIN item_instance ii ON ii.guid = gi.item_guid WHERE ii.itemEntry = ? LIMIT ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_DEL_CHAR_ACHIEVEMENT, "DELETE FROM character_achievement WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS, "DELETE FROM character_achievement_progress WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_ACHIEVEMENT, "INSERT INTO character_achievement (guid, achievement, date) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS_BY_CRITERIA, "DELETE FROM character_achievement_progress WHERE guid = ? AND criteria = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_ACHIEVEMENT_PROGRESS, "INSERT INTO character_achievement_progress (guid, criteria, counter, date) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_REPUTATION_BY_FACTION, "DELETE FROM character_reputation WHERE guid = ? AND faction = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_REPUTATION_BY_FACTION, "INSERT INTO character_reputation (guid, faction, standing, flags) VALUES (?, ?, ? , ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_ADD_CHAR_ARENA_POINTS, "UPDATE characters SET arenaPoints = (arenaPoints + ?) WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ITEM_REFUND_INSTANCE, "DELETE FROM item_refund_instance WHERE item_guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_ITEM_REFUND_INSTANCE, "INSERT INTO item_refund_instance (item_guid, player_guid, paidMoney, paidExtendedCost) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GROUP, "DELETE FROM groups WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GROUP_MEMBER_ALL, "DELETE FROM group_member WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_GIFT, "INSERT INTO character_gifts (guid, item_guid, entry, flags) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_INSTANCE_BY_INSTANCE, "DELETE FROM instance WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE, "DELETE FROM character_instance WHERE instance = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_MAP_DIFF, "DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE map = ? and difficulty = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GROUP_INSTANCE_BY_MAP_DIFF, "DELETE FROM group_instance USING group_instance LEFT JOIN instance ON group_instance.instance = id WHERE map = ? and difficulty = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_INSTANCE_BY_MAP_DIFF, "DELETE FROM instance WHERE map = ? and difficulty = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_MAIL_ITEM_BY_ID, "DELETE FROM mail_items WHERE mail_id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_PETITION, "INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PETITION_BY_GUID, "DELETE FROM petition WHERE petitionguid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_GUID, "DELETE FROM petition_sign WHERE petitionguid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_DECLINED_NAME, "DELETE FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_DECLINED_NAME, "INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_FACTION_OR_RACE, "UPDATE characters SET name = ?, race = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_SKILL_LANGUAGES, "DELETE FROM character_skills WHERE skill IN (98, 113, 759, 111, 313, 109, 115, 315, 673, 137) AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_SKILL_LANGUAGE, "INSERT INTO `character_skills` (guid, skill, value, max) VALUES (?, ?, 300, 300)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_TAXI_PATH, "UPDATE characters SET taxi_path = '' WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_TAXIMASK, "UPDATE characters SET taximask = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_QUESTSTATUS, "DELETE FROM character_queststatus WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_SOCIAL_BY_GUID, "DELETE FROM character_social WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_SOCIAL_BY_FRIEND, "DELETE FROM character_social WHERE friend = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT, "DELETE FROM character_achievement WHERE achievement = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_ACHIEVEMENT, "UPDATE character_achievement SET achievement = ? where achievement = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_INVENTORY_FACTION_CHANGE, "UPDATE item_instance ii, character_inventory ci SET ii.itemEntry = ? WHERE ii.itemEntry = ? AND ci.guid = ? AND ci.item = ii.guid", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_SPELL_BY_SPELL, "DELETE FROM character_spell WHERE spell = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_SPELL_FACTION_CHANGE, "UPDATE character_spell SET spell = ? where spell = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHAR_REP_BY_FACTION, "SELECT standing FROM character_reputation WHERE faction = ? AND guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_DEL_CHAR_REP_BY_FACTION, "DELETE FROM character_reputation WHERE faction = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_REP_FACTION_CHANGE, "UPDATE character_reputation SET faction = ?, standing = ? WHERE faction = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_TITLES_FACTION_CHANGE, "UPDATE characters SET knownTitles = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_RES_CHAR_TITLES_FACTION_CHANGE, "UPDATE characters SET chosenTitle = 0 WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_SPELL_COOLDOWNS, "DELETE FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_SPELL_COOLDOWN, "INSERT INTO character_spell_cooldown (guid, spell, item, time) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHARACTER, "DELETE FROM characters WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_ACTION, "DELETE FROM character_action WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_AURA, "DELETE FROM character_aura WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_GIFT, "DELETE FROM character_gifts WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_INSTANCE, "DELETE FROM character_instance WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_INVENTORY, "DELETE FROM character_inventory WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_QUESTSTATUS_REWARDED, "DELETE FROM character_queststatus_rewarded WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_REPUTATION, "DELETE FROM character_reputation WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_SPELL, "DELETE FROM character_spell WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_MAIL, "DELETE FROM mail WHERE receiver = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_MAIL_ITEMS, "DELETE FROM mail_items WHERE receiver = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_ACHIEVEMENTS, "DELETE FROM character_achievement WHERE guid = ? AND achievement NOT BETWEEN '456' AND '467' AND achievement NOT BETWEEN '1400' AND '1427' AND achievement NOT IN(1463, 3117, 3259)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_EQUIPMENTSETS, "DELETE FROM character_equipmentsets WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GUILD_EVENTLOG_BY_PLAYER, "DELETE FROM guild_eventlog WHERE PlayerGuid1 = ? OR PlayerGuid2 = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_GUILD_BANK_EVENTLOG_BY_PLAYER, "DELETE FROM guild_bank_eventlog WHERE PlayerGuid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_GLYPHS, "DELETE FROM character_glyphs WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_TALENT, "DELETE FROM character_talent WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_SKILLS, "DELETE FROM character_skills WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_HONOR_POINTS, "UPDATE characters SET totalHonorPoints = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_ARENA_POINTS, "UPDATE characters SET arenaPoints = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_MONEY, "UPDATE characters SET money = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_ACTION, "INSERT INTO character_action (guid, spec, button, action, type) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_ACTION, "UPDATE character_action SET action = ?, type = ? WHERE guid = ? AND button = ? AND spec = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_ACTION_BY_BUTTON_SPEC, "DELETE FROM character_action WHERE guid = ? and button = ? and spec = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_INVENTORY_BY_ITEM, "DELETE FROM character_inventory WHERE item = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_INVENTORY_BY_BAG_SLOT, "DELETE FROM character_inventory WHERE bag = ? AND slot = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_MAIL, "UPDATE mail SET has_items = ?, expire_time = ?, deliver_time = ?, money = ?, cod = ?, checked = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_REP_CHAR_QUESTSTATUS, "REPLACE INTO character_queststatus (guid, quest, status, explored, timer, mobcount1, mobcount2, mobcount3, mobcount4, itemcount1, itemcount2, itemcount3, itemcount4, playercount) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_QUESTSTATUS_BY_QUEST, "DELETE FROM character_queststatus WHERE guid = ? AND quest = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_QUESTSTATUS_REWARDED, "INSERT IGNORE INTO character_queststatus_rewarded (guid, quest, active) VALUES (?, ?, 1)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_QUESTSTATUS_REWARDED_BY_QUEST, "DELETE FROM character_queststatus_rewarded WHERE guid = ? AND quest = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_QUESTSTATUS_REWARDED_FACTION_CHANGE, "UPDATE character_queststatus_rewarded SET quest = ? WHERE quest = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE, "UPDATE character_queststatus_rewarded SET active = 1 WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE_BY_QUEST, "UPDATE character_queststatus_rewarded SET active = 0 WHERE quest = ? AND guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_SKILL_BY_SKILL, "DELETE FROM character_skills WHERE guid = ? AND skill = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_SKILLS, "INSERT INTO character_skills (guid, skill, value, max) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_SKILLS, "UPDATE character_skills SET value = ?, max = ? WHERE guid = ? AND skill = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_SPELL, "INSERT INTO character_spell (guid, spell, active, disabled) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_STATS, "DELETE FROM character_stats WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_STATS, "INSERT INTO character_stats (guid, maxhealth, maxpower1, maxpower2, maxpower3, maxpower4, maxpower5, maxpower6, maxpower7, strength, agility, stamina, intellect, spirit, "
+ "armor, resHoly, resFire, resNature, resFrost, resShadow, resArcane, blockPct, dodgePct, parryPct, critPct, rangedCritPct, spellCritPct, attackPower, rangedAttackPower, "
+ "spellPower, resilience) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PETITION_BY_OWNER, "DELETE FROM petition WHERE ownerguid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_OWNER, "DELETE FROM petition_sign WHERE ownerguid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PETITION_BY_OWNER_AND_TYPE, "DELETE FROM petition WHERE ownerguid = ? AND type = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_OWNER_AND_TYPE, "DELETE FROM petition_sign WHERE ownerguid = ? AND type = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_GLYPHS, "INSERT INTO character_glyphs VALUES(?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_TALENT_BY_SPELL_SPEC, "DELETE FROM character_talent WHERE guid = ? AND spell = ? AND talentGroup = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_TALENT, "INSERT INTO character_talent (guid, spell, talentGroup) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC, "DELETE FROM character_action WHERE spec<>? AND guid = ?", CONNECTION_ASYNC);
+
+ // Items that hold loot or money
+ PrepareStatement(CHAR_SEL_ITEMCONTAINER_ITEMS, "SELECT item_id, item_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix FROM item_loot_items WHERE container_id = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_DEL_ITEMCONTAINER_ITEMS, "DELETE FROM item_loot_items WHERE container_id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ITEMCONTAINER_ITEM, "DELETE FROM item_loot_items WHERE container_id = ? AND item_id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_ITEMCONTAINER_ITEMS, "INSERT INTO item_loot_items (container_id, item_id, item_count, follow_rules, ffa, blocked, counted, under_threshold, needs_quest, rnd_prop, rnd_suffix) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_ITEMCONTAINER_MONEY, "SELECT money FROM item_loot_money WHERE container_id = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_DEL_ITEMCONTAINER_MONEY, "DELETE FROM item_loot_money WHERE container_id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_ITEMCONTAINER_MONEY, "INSERT INTO item_loot_money (container_id, money) VALUES (?, ?)", CONNECTION_ASYNC);
+
+ // Calendar
+ PrepareStatement(CHAR_REP_CALENDAR_EVENT, "REPLACE INTO calendar_events (id, creator, title, description, type, dungeon, eventtime, flags, time2) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CALENDAR_EVENT, "DELETE FROM calendar_events WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_REP_CALENDAR_INVITE, "REPLACE INTO calendar_invites (id, event, invitee, sender, status, statustime, rank, text) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CALENDAR_INVITE, "DELETE FROM calendar_invites WHERE id = ?", CONNECTION_ASYNC);
+
+ // Pet
+ PrepareStatement(CHAR_SEL_PET_SLOTS, "SELECT owner, slot FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PET_SLOTS_DETAIL, "SELECT owner, id, entry, level, name FROM character_pet WHERE owner = ? AND slot >= ? AND slot <= ? ORDER BY slot", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PET_ENTRY, "SELECT entry FROM character_pet WHERE owner = ? AND id = ? AND slot >= ? AND slot <= ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PET_SLOT_BY_ID, "SELECT slot, entry FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PET_SPELL_LIST, "SELECT DISTINCT pet_spell.spell FROM pet_spell, character_pet WHERE character_pet.owner = ? AND character_pet.id = pet_spell.guid AND character_pet.id <> ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_PET, "SELECT id FROM character_pet WHERE owner = ? AND id <> ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_PETS, "SELECT id FROM character_pet WHERE owner = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER, "DELETE FROM character_pet_declinedname WHERE owner = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME, "DELETE FROM character_pet_declinedname WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PET_AURA, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges FROM pet_aura WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time FROM pet_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = ? AND id = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_DEL_PET_AURAS, "DELETE FROM pet_aura WHERE guid = ?", CONNECTION_BOTH);
+ PrepareStatement(CHAR_DEL_PET_SPELLS, "DELETE FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_PET_SPELL_COOLDOWNS, "DELETE FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_BOTH);
+ PrepareStatement(CHAR_INS_PET_SPELL_COOLDOWN, "INSERT INTO pet_spell_cooldown (guid, spell, time) VALUES (?, ?, ?)", CONNECTION_BOTH);
+ PrepareStatement(CHAR_DEL_PET_SPELL_BY_SPELL, "DELETE FROM pet_spell WHERE guid = ? and spell = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_PET_SPELL, "INSERT INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_BOTH);
+ PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
+ "base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH);
+ PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND entry = ? AND (slot = ? OR slot > ?)", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_PET_BY_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?) ", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_DEL_CHAR_PET_BY_OWNER, "DELETE FROM character_pet WHERE owner = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_PET_NAME, "UPDATE character_pet SET name = ?, renamed = 1 WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ? AND id <> ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_PET_BY_ID, "DELETE FROM character_pet WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_PET_BY_SLOT, "DELETE FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?)", CONNECTION_ASYNC);
+
+ // PvPstats
+ PrepareStatement(CHAR_SEL_PVPSTATS_MAXID, "SELECT MAX(id) FROM pvpstats_battlegrounds", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_INS_PVPSTATS_BATTLEGROUND, "INSERT INTO pvpstats_battlegrounds (id, winner_faction, bracket_id, type, date) VALUES (?, ?, ?, ?, NOW())", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_PVPSTATS_PLAYER, "INSERT INTO pvpstats_players (battleground_id, character_guid, score_killing_blows, score_deaths, score_honorable_kills, score_bonus_honor, score_damage_done, score_healing_done, attr_1, attr_2, attr_3, attr_4, attr_5) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_PVPSTATS_FACTIONS_OVERALL, "SELECT winner_faction, COUNT(*) AS count FROM pvpstats_battlegrounds WHERE DATEDIFF(NOW(), date) < 7 GROUP BY winner_faction ORDER BY winner_faction ASC", CONNECTION_SYNCH);
+
+ // QuestTracker
+ PrepareStatement(CHAR_INS_QUEST_TRACK, "INSERT INTO quest_tracker (id, character_guid, quest_accept_time, core_hash, core_revision) VALUES (?, ?, NOW(), ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_QUEST_TRACK_GM_COMPLETE, "UPDATE quest_tracker SET completed_by_gm = 1 WHERE id = ? AND character_guid = ? ORDER BY quest_accept_time DESC LIMIT 1", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_QUEST_TRACK_COMPLETE_TIME, "UPDATE quest_tracker SET quest_complete_time = NOW() WHERE id = ? AND character_guid = ? ORDER BY quest_accept_time DESC LIMIT 1", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_QUEST_TRACK_ABANDON_TIME, "UPDATE quest_tracker SET quest_abandon_time = NOW() WHERE id = ? AND character_guid = ? ORDER BY quest_accept_time DESC LIMIT 1", CONNECTION_ASYNC);
+}
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
new file mode 100644
index 00000000000..f88a912e022
--- /dev/null
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2008-2015 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 _CHARACTERDATABASE_H
+#define _CHARACTERDATABASE_H
+
+#include "DatabaseWorkerPool.h"
+#include "MySQLConnection.h"
+
+class CharacterDatabaseConnection : public MySQLConnection
+{
+ public:
+ //- Constructors for sync and async connections
+ CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { }
+ CharacterDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { }
+
+ //- Loads database type specific prepared statements
+ void DoPrepareStatements() override;
+};
+
+typedef DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabaseWorkerPool;
+
+enum CharacterDatabaseStatements
+{
+ /* Naming standard for defines:
+ {DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed}
+ When updating more than one field, consider looking at the calling function
+ name for a suiting suffix.
+ */
+
+ CHAR_DEL_QUEST_POOL_SAVE,
+ CHAR_INS_QUEST_POOL_SAVE,
+ CHAR_DEL_NONEXISTENT_GUILD_BANK_ITEM,
+ CHAR_DEL_EXPIRED_BANS,
+ CHAR_SEL_GUID_BY_NAME,
+ CHAR_SEL_CHECK_NAME,
+ CHAR_SEL_CHECK_GUID,
+ CHAR_SEL_SUM_CHARS,
+ CHAR_SEL_CHAR_CREATE_INFO,
+ CHAR_INS_CHARACTER_BAN,
+ CHAR_UPD_CHARACTER_BAN,
+ CHAR_DEL_CHARACTER_BAN,
+ CHAR_SEL_BANINFO,
+ CHAR_SEL_GUID_BY_NAME_FILTER,
+ CHAR_SEL_BANINFO_LIST,
+ CHAR_SEL_BANNED_NAME,
+ CHAR_SEL_MAIL_LIST_COUNT,
+ CHAR_SEL_MAIL_LIST_INFO,
+ CHAR_SEL_MAIL_LIST_ITEMS,
+ CHAR_SEL_ENUM,
+ CHAR_SEL_ENUM_DECLINED_NAME,
+ CHAR_SEL_FREE_NAME,
+ CHAR_SEL_GUID_RACE_ACC_BY_NAME,
+ CHAR_SEL_CHAR_RACE,
+ CHAR_SEL_CHAR_LEVEL,
+ CHAR_SEL_CHAR_ZONE,
+ CHAR_SEL_CHARACTER_NAME_DATA,
+ CHAR_SEL_CHAR_POSITION_XYZ,
+ CHAR_SEL_CHAR_POSITION,
+
+ CHAR_DEL_BATTLEGROUND_RANDOM_ALL,
+ CHAR_DEL_BATTLEGROUND_RANDOM,
+ CHAR_INS_BATTLEGROUND_RANDOM,
+
+ CHAR_SEL_CHARACTER,
+ CHAR_SEL_GROUP_MEMBER,
+ CHAR_SEL_CHARACTER_INSTANCE,
+ CHAR_SEL_CHARACTER_AURAS,
+ CHAR_SEL_CHARACTER_SPELL,
+ CHAR_SEL_CHARACTER_QUESTSTATUS,
+
+ CHAR_SEL_CHARACTER_QUESTSTATUS_DAILY,
+ CHAR_SEL_CHARACTER_QUESTSTATUS_WEEKLY,
+ CHAR_SEL_CHARACTER_QUESTSTATUS_MONTHLY,
+ CHAR_SEL_CHARACTER_QUESTSTATUS_SEASONAL,
+ CHAR_DEL_CHARACTER_QUESTSTATUS_DAILY,
+ CHAR_DEL_CHARACTER_QUESTSTATUS_WEEKLY,
+ CHAR_DEL_CHARACTER_QUESTSTATUS_MONTHLY,
+ CHAR_DEL_CHARACTER_QUESTSTATUS_SEASONAL,
+ CHAR_INS_CHARACTER_QUESTSTATUS_DAILY,
+ CHAR_INS_CHARACTER_QUESTSTATUS_WEEKLY,
+ CHAR_INS_CHARACTER_QUESTSTATUS_MONTHLY,
+ CHAR_INS_CHARACTER_QUESTSTATUS_SEASONAL,
+ CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_DAILY,
+ CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_WEEKLY,
+ CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_MONTHLY,
+ CHAR_DEL_RESET_CHARACTER_QUESTSTATUS_SEASONAL_BY_EVENT,
+
+ CHAR_SEL_CHARACTER_REPUTATION,
+ CHAR_SEL_CHARACTER_INVENTORY,
+ CHAR_SEL_CHARACTER_ACTIONS,
+ CHAR_SEL_CHARACTER_ACTIONS_SPEC,
+ CHAR_SEL_CHARACTER_MAILCOUNT,
+ CHAR_SEL_CHARACTER_MAILDATE,
+ CHAR_SEL_MAIL_COUNT,
+ CHAR_SEL_CHARACTER_SOCIALLIST,
+ CHAR_SEL_CHARACTER_HOMEBIND,
+ CHAR_SEL_CHARACTER_SPELLCOOLDOWNS,
+ CHAR_SEL_CHARACTER_DECLINEDNAMES,
+ CHAR_SEL_GUILD_MEMBER,
+ CHAR_SEL_GUILD_MEMBER_EXTENDED,
+ CHAR_SEL_CHARACTER_ARENAINFO,
+ CHAR_SEL_CHARACTER_ACHIEVEMENTS,
+ CHAR_SEL_CHARACTER_CRITERIAPROGRESS,
+ CHAR_SEL_CHARACTER_EQUIPMENTSETS,
+ CHAR_SEL_CHARACTER_BGDATA,
+ CHAR_SEL_CHARACTER_GLYPHS,
+ CHAR_SEL_CHARACTER_TALENTS,
+ CHAR_SEL_CHARACTER_SKILLS,
+ CHAR_SEL_CHARACTER_RANDOMBG,
+ CHAR_SEL_CHARACTER_BANNED,
+ CHAR_SEL_CHARACTER_QUESTSTATUSREW,
+ CHAR_SEL_ACCOUNT_INSTANCELOCKTIMES,
+ CHAR_SEL_MAILITEMS,
+ CHAR_SEL_AUCTION_ITEMS,
+ CHAR_INS_AUCTION,
+ CHAR_DEL_AUCTION,
+ CHAR_UPD_AUCTION_BID,
+ CHAR_SEL_AUCTIONS,
+ CHAR_INS_MAIL,
+ CHAR_DEL_MAIL_BY_ID,
+ CHAR_INS_MAIL_ITEM,
+ CHAR_DEL_MAIL_ITEM,
+ CHAR_DEL_INVALID_MAIL_ITEM,
+ CHAR_DEL_EMPTY_EXPIRED_MAIL,
+ CHAR_SEL_EXPIRED_MAIL,
+ CHAR_SEL_EXPIRED_MAIL_ITEMS,
+ CHAR_UPD_MAIL_RETURNED,
+ CHAR_UPD_MAIL_ITEM_RECEIVER,
+ CHAR_UPD_ITEM_OWNER,
+ CHAR_SEL_ITEM_REFUNDS,
+ CHAR_SEL_ITEM_BOP_TRADE,
+ CHAR_DEL_ITEM_BOP_TRADE,
+ CHAR_INS_ITEM_BOP_TRADE,
+ CHAR_REP_INVENTORY_ITEM,
+ CHAR_REP_ITEM_INSTANCE,
+ CHAR_UPD_ITEM_INSTANCE,
+ CHAR_UPD_ITEM_INSTANCE_ON_LOAD,
+ CHAR_DEL_ITEM_INSTANCE,
+ CHAR_DEL_ITEM_INSTANCE_BY_OWNER,
+ CHAR_UPD_GIFT_OWNER,
+ CHAR_DEL_GIFT,
+ CHAR_SEL_CHARACTER_GIFT_BY_ITEM,
+ CHAR_SEL_ACCOUNT_BY_NAME,
+ CHAR_SEL_ACCOUNT_BY_GUID,
+ 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,
+ CHAR_UPD_GUILD_NAME,
+ CHAR_INS_GUILD_MEMBER,
+ CHAR_DEL_GUILD_MEMBER,
+ CHAR_DEL_GUILD_MEMBERS,
+ CHAR_INS_GUILD_RANK,
+ CHAR_DEL_GUILD_RANKS,
+ CHAR_DEL_GUILD_LOWEST_RANK,
+ CHAR_INS_GUILD_BANK_TAB,
+ CHAR_DEL_GUILD_BANK_TAB,
+ CHAR_DEL_GUILD_BANK_TABS,
+ CHAR_INS_GUILD_BANK_ITEM,
+ CHAR_DEL_GUILD_BANK_ITEM,
+ CHAR_DEL_GUILD_BANK_ITEMS,
+ CHAR_INS_GUILD_BANK_RIGHT,
+ CHAR_DEL_GUILD_BANK_RIGHTS,
+ CHAR_DEL_GUILD_BANK_RIGHTS_FOR_RANK,
+ CHAR_INS_GUILD_BANK_EVENTLOG,
+ CHAR_DEL_GUILD_BANK_EVENTLOG,
+ CHAR_DEL_GUILD_BANK_EVENTLOGS,
+ CHAR_INS_GUILD_EVENTLOG,
+ CHAR_DEL_GUILD_EVENTLOG,
+ CHAR_DEL_GUILD_EVENTLOGS,
+ CHAR_UPD_GUILD_MEMBER_PNOTE,
+ CHAR_UPD_GUILD_MEMBER_OFFNOTE,
+ CHAR_UPD_GUILD_MEMBER_RANK,
+ CHAR_UPD_GUILD_MOTD,
+ CHAR_UPD_GUILD_INFO,
+ CHAR_UPD_GUILD_LEADER,
+ CHAR_UPD_GUILD_RANK_NAME,
+ CHAR_UPD_GUILD_RANK_RIGHTS,
+ CHAR_UPD_GUILD_EMBLEM_INFO,
+ CHAR_UPD_GUILD_BANK_TAB_INFO,
+ CHAR_UPD_GUILD_BANK_MONEY,
+ CHAR_UPD_GUILD_BANK_EVENTLOG_TAB,
+ CHAR_UPD_GUILD_RANK_BANK_MONEY,
+ CHAR_UPD_GUILD_BANK_TAB_TEXT,
+ CHAR_INS_GUILD_MEMBER_WITHDRAW,
+ CHAR_DEL_GUILD_MEMBER_WITHDRAW,
+ CHAR_SEL_CHAR_DATA_FOR_GUILD,
+
+ CHAR_SEL_CHANNEL,
+ CHAR_INS_CHANNEL,
+ CHAR_UPD_CHANNEL,
+ CHAR_UPD_CHANNEL_USAGE,
+ CHAR_UPD_CHANNEL_OWNERSHIP,
+ CHAR_DEL_OLD_CHANNELS,
+
+ CHAR_UPD_EQUIP_SET,
+ CHAR_INS_EQUIP_SET,
+ CHAR_DEL_EQUIP_SET,
+
+ CHAR_INS_AURA,
+
+ CHAR_SEL_ACCOUNT_DATA,
+ CHAR_REP_ACCOUNT_DATA,
+ CHAR_DEL_ACCOUNT_DATA,
+ CHAR_SEL_PLAYER_ACCOUNT_DATA,
+ CHAR_REP_PLAYER_ACCOUNT_DATA,
+ CHAR_DEL_PLAYER_ACCOUNT_DATA,
+
+ CHAR_SEL_TUTORIALS,
+ CHAR_SEL_HAS_TUTORIALS,
+ CHAR_INS_TUTORIALS,
+ CHAR_UPD_TUTORIALS,
+ CHAR_DEL_TUTORIALS,
+
+ CHAR_INS_INSTANCE_SAVE,
+ CHAR_UPD_INSTANCE_DATA,
+
+ CHAR_DEL_GAME_EVENT_SAVE,
+ CHAR_INS_GAME_EVENT_SAVE,
+
+ CHAR_DEL_ALL_GAME_EVENT_CONDITION_SAVE,
+ CHAR_DEL_GAME_EVENT_CONDITION_SAVE,
+ CHAR_INS_GAME_EVENT_CONDITION_SAVE,
+
+ CHAR_INS_ARENA_TEAM,
+ CHAR_INS_ARENA_TEAM_MEMBER,
+ CHAR_DEL_ARENA_TEAM,
+ CHAR_DEL_ARENA_TEAM_MEMBERS,
+ CHAR_UPD_ARENA_TEAM_CAPTAIN,
+ CHAR_DEL_ARENA_TEAM_MEMBER,
+ CHAR_UPD_ARENA_TEAM_STATS,
+ CHAR_UPD_ARENA_TEAM_MEMBER,
+ CHAR_DEL_CHARACTER_ARENA_STATS,
+ CHAR_REP_CHARACTER_ARENA_STATS,
+ CHAR_SEL_PLAYER_ARENA_TEAMS,
+ CHAR_UPD_ARENA_TEAM_NAME,
+
+ CHAR_SEL_PETITION,
+ CHAR_SEL_PETITION_SIGNATURE,
+ CHAR_DEL_ALL_PETITION_SIGNATURES,
+ CHAR_DEL_PETITION_SIGNATURE,
+ CHAR_SEL_PETITION_BY_OWNER,
+ CHAR_SEL_PETITION_TYPE,
+ CHAR_SEL_PETITION_SIGNATURES,
+ CHAR_SEL_PETITION_SIG_BY_ACCOUNT,
+ CHAR_SEL_PETITION_OWNER_BY_GUID,
+ CHAR_SEL_PETITION_SIG_BY_GUID,
+ CHAR_SEL_PETITION_SIG_BY_GUID_TYPE,
+
+ CHAR_INS_PLAYER_BGDATA,
+ CHAR_DEL_PLAYER_BGDATA,
+
+ CHAR_INS_PLAYER_HOMEBIND,
+ CHAR_UPD_PLAYER_HOMEBIND,
+ CHAR_DEL_PLAYER_HOMEBIND,
+
+ CHAR_SEL_CORPSES,
+ CHAR_INS_CORPSE,
+ CHAR_DEL_CORPSE,
+ CHAR_DEL_PLAYER_CORPSES,
+ CHAR_DEL_OLD_CORPSES,
+
+ CHAR_SEL_CREATURE_RESPAWNS,
+ CHAR_REP_CREATURE_RESPAWN,
+ CHAR_DEL_CREATURE_RESPAWN,
+ CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE,
+ CHAR_SEL_MAX_CREATURE_RESPAWNS,
+
+ CHAR_SEL_GO_RESPAWNS,
+ CHAR_REP_GO_RESPAWN,
+ CHAR_DEL_GO_RESPAWN,
+ CHAR_DEL_GO_RESPAWN_BY_INSTANCE,
+
+ CHAR_SEL_GM_TICKETS,
+ CHAR_REP_GM_TICKET,
+ CHAR_DEL_GM_TICKET,
+ CHAR_DEL_ALL_GM_TICKETS,
+ CHAR_DEL_PLAYER_GM_TICKETS,
+
+ CHAR_INS_GM_SURVEY,
+ CHAR_INS_GM_SUBSURVEY,
+ CHAR_INS_LAG_REPORT,
+
+ CHAR_INS_CHARACTER,
+ CHAR_UPD_CHARACTER,
+
+ CHAR_UPD_ADD_AT_LOGIN_FLAG,
+ CHAR_UPD_REM_AT_LOGIN_FLAG,
+ CHAR_UPD_ALL_AT_LOGIN_FLAGS,
+ CHAR_INS_BUG_REPORT,
+ CHAR_UPD_PETITION_NAME,
+ CHAR_INS_PETITION_SIGNATURE,
+ CHAR_UPD_ACCOUNT_ONLINE,
+ CHAR_INS_GROUP,
+ CHAR_INS_GROUP_MEMBER,
+ CHAR_DEL_GROUP_MEMBER,
+ CHAR_DEL_GROUP_INSTANCE_PERM_BINDING,
+ CHAR_UPD_GROUP_LEADER,
+ CHAR_UPD_GROUP_TYPE,
+ CHAR_UPD_GROUP_MEMBER_SUBGROUP,
+ CHAR_UPD_GROUP_MEMBER_FLAG,
+ CHAR_UPD_GROUP_DIFFICULTY,
+ CHAR_UPD_GROUP_RAID_DIFFICULTY,
+ CHAR_DEL_INVALID_SPELL_SPELLS,
+ CHAR_DEL_INVALID_SPELL_TALENTS,
+ CHAR_UPD_DELETE_INFO,
+ CHAR_UPD_RESTORE_DELETE_INFO,
+ CHAR_UPD_ZONE,
+ CHAR_UPD_LEVEL,
+ CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA,
+ CHAR_DEL_INVALID_ACHIEVMENT,
+ CHAR_INS_ADDON,
+ CHAR_DEL_INVALID_PET_SPELL,
+ CHAR_DEL_GROUP_INSTANCE_BY_INSTANCE,
+ CHAR_DEL_GROUP_INSTANCE_BY_GUID,
+ CHAR_REP_GROUP_INSTANCE,
+ CHAR_UPD_INSTANCE_RESETTIME,
+ CHAR_UPD_GLOBAL_INSTANCE_RESETTIME,
+ CHAR_UPD_CHAR_ONLINE,
+ CHAR_UPD_CHAR_NAME_AT_LOGIN,
+ CHAR_UPD_WORLDSTATE,
+ CHAR_INS_WORLDSTATE,
+ CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID,
+ CHAR_UPD_CHAR_INSTANCE,
+ CHAR_INS_CHAR_INSTANCE,
+ CHAR_UPD_GENDER_PLAYERBYTES,
+ CHAR_DEL_CHARACTER_SKILL,
+ CHAR_UPD_ADD_CHARACTER_SOCIAL_FLAGS,
+ CHAR_UPD_REM_CHARACTER_SOCIAL_FLAGS,
+ CHAR_INS_CHARACTER_SOCIAL,
+ CHAR_DEL_CHARACTER_SOCIAL,
+ CHAR_UPD_CHARACTER_SOCIAL_NOTE,
+ CHAR_UPD_CHARACTER_POSITION,
+
+ CHAR_INS_LFG_DATA,
+ CHAR_DEL_LFG_DATA,
+
+ CHAR_SEL_CHARACTER_AURA_FROZEN,
+ CHAR_SEL_CHARACTER_ONLINE,
+
+ CHAR_SEL_CHAR_DEL_INFO_BY_GUID,
+ CHAR_SEL_CHAR_DEL_INFO_BY_NAME,
+ CHAR_SEL_CHAR_DEL_INFO,
+
+ CHAR_SEL_CHARS_BY_ACCOUNT_ID,
+ CHAR_SEL_CHAR_PINFO,
+ CHAR_SEL_PINFO_XP,
+ CHAR_SEL_PINFO_MAILS,
+ CHAR_SEL_PINFO_BANS,
+ CHAR_SEL_CHAR_HOMEBIND,
+ CHAR_SEL_CHAR_GUID_NAME_BY_ACC,
+ CHAR_SEL_POOL_QUEST_SAVE,
+ CHAR_SEL_CHARACTER_AT_LOGIN,
+ CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN,
+ CHAR_SEL_CHAR_AT_LOGIN_TITLES,
+ CHAR_SEL_INSTANCE,
+ CHAR_SEL_CHAR_COD_ITEM_MAIL,
+ CHAR_SEL_CHAR_SOCIAL,
+ 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,
+ CHAR_SEL_MAIL_COUNT_ITEM,
+ CHAR_SEL_AUCTIONHOUSE_COUNT_ITEM,
+ CHAR_SEL_GUILD_BANK_COUNT_ITEM,
+ CHAR_SEL_CHAR_INVENTORY_ITEM_BY_ENTRY,
+ CHAR_SEL_MAIL_ITEMS_BY_ENTRY,
+ CHAR_SEL_AUCTIONHOUSE_ITEM_BY_ENTRY,
+ CHAR_SEL_GUILD_BANK_ITEM_BY_ENTRY,
+ CHAR_DEL_CHAR_ACHIEVEMENT,
+ CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS,
+ CHAR_INS_CHAR_ACHIEVEMENT,
+ CHAR_DEL_CHAR_ACHIEVEMENT_PROGRESS_BY_CRITERIA,
+ CHAR_INS_CHAR_ACHIEVEMENT_PROGRESS,
+ CHAR_DEL_CHAR_REPUTATION_BY_FACTION,
+ CHAR_INS_CHAR_REPUTATION_BY_FACTION,
+ CHAR_UPD_ADD_CHAR_ARENA_POINTS,
+ CHAR_DEL_ITEM_REFUND_INSTANCE,
+ CHAR_INS_ITEM_REFUND_INSTANCE,
+ CHAR_DEL_GROUP,
+ CHAR_DEL_GROUP_MEMBER_ALL,
+ CHAR_INS_CHAR_GIFT,
+ CHAR_DEL_INSTANCE_BY_INSTANCE,
+ CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE,
+ CHAR_DEL_CHAR_INSTANCE_BY_MAP_DIFF,
+ CHAR_DEL_GROUP_INSTANCE_BY_MAP_DIFF,
+ CHAR_DEL_INSTANCE_BY_MAP_DIFF,
+ CHAR_DEL_MAIL_ITEM_BY_ID,
+ CHAR_INS_PETITION,
+ CHAR_DEL_PETITION_BY_GUID,
+ CHAR_DEL_PETITION_SIGNATURE_BY_GUID,
+ CHAR_DEL_CHAR_DECLINED_NAME,
+ CHAR_INS_CHAR_DECLINED_NAME,
+ CHAR_UPD_FACTION_OR_RACE,
+ CHAR_DEL_CHAR_SKILL_LANGUAGES,
+ CHAR_INS_CHAR_SKILL_LANGUAGE,
+ CHAR_UPD_CHAR_TAXI_PATH,
+ CHAR_UPD_CHAR_TAXIMASK,
+ CHAR_DEL_CHAR_QUESTSTATUS,
+ CHAR_DEL_CHAR_SOCIAL_BY_GUID,
+ CHAR_DEL_CHAR_SOCIAL_BY_FRIEND,
+ CHAR_DEL_CHAR_ACHIEVEMENT_BY_ACHIEVEMENT,
+ CHAR_UPD_CHAR_ACHIEVEMENT,
+ CHAR_UPD_CHAR_INVENTORY_FACTION_CHANGE,
+ CHAR_DEL_CHAR_SPELL_BY_SPELL,
+ CHAR_UPD_CHAR_SPELL_FACTION_CHANGE,
+ CHAR_SEL_CHAR_REP_BY_FACTION,
+ CHAR_DEL_CHAR_REP_BY_FACTION,
+ CHAR_UPD_CHAR_REP_FACTION_CHANGE,
+ CHAR_UPD_CHAR_TITLES_FACTION_CHANGE,
+ CHAR_RES_CHAR_TITLES_FACTION_CHANGE,
+ CHAR_DEL_CHAR_SPELL_COOLDOWNS,
+ CHAR_INS_CHAR_SPELL_COOLDOWN,
+ CHAR_DEL_CHARACTER,
+ CHAR_DEL_CHAR_ACTION,
+ CHAR_DEL_CHAR_AURA,
+ CHAR_DEL_CHAR_GIFT,
+ CHAR_DEL_CHAR_INSTANCE,
+ CHAR_DEL_CHAR_INVENTORY,
+ CHAR_DEL_CHAR_QUESTSTATUS_REWARDED,
+ CHAR_DEL_CHAR_REPUTATION,
+ CHAR_DEL_CHAR_SPELL,
+ CHAR_DEL_MAIL,
+ CHAR_DEL_MAIL_ITEMS,
+ CHAR_DEL_CHAR_ACHIEVEMENTS,
+ CHAR_DEL_CHAR_EQUIPMENTSETS,
+ CHAR_DEL_GUILD_EVENTLOG_BY_PLAYER,
+ CHAR_DEL_GUILD_BANK_EVENTLOG_BY_PLAYER,
+ CHAR_DEL_CHAR_GLYPHS,
+ CHAR_DEL_CHAR_TALENT,
+ CHAR_DEL_CHAR_SKILLS,
+ CHAR_UPD_CHAR_HONOR_POINTS,
+ CHAR_UPD_CHAR_ARENA_POINTS,
+ CHAR_UPD_CHAR_MONEY,
+ CHAR_INS_CHAR_ACTION,
+ CHAR_UPD_CHAR_ACTION,
+ CHAR_DEL_CHAR_ACTION_BY_BUTTON_SPEC,
+ CHAR_DEL_CHAR_INVENTORY_BY_ITEM,
+ CHAR_DEL_CHAR_INVENTORY_BY_BAG_SLOT,
+ CHAR_UPD_MAIL,
+ CHAR_REP_CHAR_QUESTSTATUS,
+ CHAR_DEL_CHAR_QUESTSTATUS_BY_QUEST,
+ CHAR_INS_CHAR_QUESTSTATUS_REWARDED,
+ CHAR_DEL_CHAR_QUESTSTATUS_REWARDED_BY_QUEST,
+ CHAR_UPD_CHAR_QUESTSTATUS_REWARDED_FACTION_CHANGE,
+ CHAR_UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE,
+ CHAR_UPD_CHAR_QUESTSTATUS_REWARDED_ACTIVE_BY_QUEST,
+ CHAR_DEL_CHAR_SKILL_BY_SKILL,
+ CHAR_INS_CHAR_SKILLS,
+ CHAR_UPD_CHAR_SKILLS,
+ CHAR_INS_CHAR_SPELL,
+ CHAR_DEL_CHAR_STATS,
+ CHAR_INS_CHAR_STATS,
+ CHAR_DEL_PETITION_BY_OWNER,
+ CHAR_DEL_PETITION_SIGNATURE_BY_OWNER,
+ CHAR_DEL_PETITION_BY_OWNER_AND_TYPE,
+ CHAR_DEL_PETITION_SIGNATURE_BY_OWNER_AND_TYPE,
+ CHAR_INS_CHAR_GLYPHS,
+ CHAR_DEL_CHAR_TALENT_BY_SPELL_SPEC,
+ CHAR_INS_CHAR_TALENT,
+ CHAR_DEL_CHAR_ACTION_EXCEPT_SPEC,
+
+ CHAR_REP_CALENDAR_EVENT,
+ CHAR_DEL_CALENDAR_EVENT,
+ CHAR_REP_CALENDAR_INVITE,
+ CHAR_DEL_CALENDAR_INVITE,
+
+ CHAR_SEL_PET_AURA,
+ CHAR_SEL_PET_SPELL,
+ CHAR_SEL_PET_SPELL_COOLDOWN,
+ CHAR_SEL_PET_DECLINED_NAME,
+ CHAR_DEL_PET_AURAS,
+ CHAR_DEL_PET_SPELL_COOLDOWNS,
+ CHAR_INS_PET_SPELL_COOLDOWN,
+ CHAR_DEL_PET_SPELL_BY_SPELL,
+ CHAR_INS_PET_SPELL,
+ CHAR_INS_PET_AURA,
+
+ CHAR_DEL_PET_SPELLS,
+ CHAR_DEL_CHAR_PET_BY_OWNER,
+ CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER,
+ CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT,
+ CHAR_SEL_PET_SLOTS,
+ CHAR_SEL_PET_SLOTS_DETAIL,
+ CHAR_SEL_PET_ENTRY,
+ CHAR_SEL_PET_SLOT_BY_ID,
+ CHAR_SEL_PET_SPELL_LIST,
+ CHAR_SEL_CHAR_PET,
+ CHAR_SEL_CHAR_PETS,
+ CHAR_SEL_CHAR_PET_BY_ENTRY,
+ CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2,
+ CHAR_SEL_CHAR_PET_BY_SLOT,
+ CHAR_DEL_CHAR_PET_DECLINEDNAME,
+ CHAR_INS_CHAR_PET_DECLINEDNAME,
+ CHAR_UPD_CHAR_PET_NAME,
+ CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID,
+ CHAR_UPD_CHAR_PET_SLOT_BY_SLOT,
+ CHAR_UPD_CHAR_PET_SLOT_BY_ID,
+ CHAR_DEL_CHAR_PET_BY_ID,
+ CHAR_DEL_CHAR_PET_BY_SLOT,
+
+ CHAR_SEL_ITEMCONTAINER_ITEMS,
+ CHAR_DEL_ITEMCONTAINER_ITEMS,
+ CHAR_DEL_ITEMCONTAINER_ITEM,
+ CHAR_INS_ITEMCONTAINER_ITEMS,
+ CHAR_SEL_ITEMCONTAINER_MONEY,
+ CHAR_DEL_ITEMCONTAINER_MONEY,
+ CHAR_INS_ITEMCONTAINER_MONEY,
+
+ CHAR_SEL_PVPSTATS_MAXID,
+ CHAR_INS_PVPSTATS_BATTLEGROUND,
+ CHAR_INS_PVPSTATS_PLAYER,
+ CHAR_SEL_PVPSTATS_FACTIONS_OVERALL,
+
+ CHAR_INS_QUEST_TRACK,
+ CHAR_UPD_QUEST_TRACK_GM_COMPLETE,
+ CHAR_UPD_QUEST_TRACK_COMPLETE_TIME,
+ CHAR_UPD_QUEST_TRACK_ABANDON_TIME,
+
+ MAX_CHARACTERDATABASE_STATEMENTS
+};
+
+#endif
diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp
new file mode 100644
index 00000000000..8f0b5067957
--- /dev/null
+++ b/src/server/database/Database/Implementation/LoginDatabase.cpp
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2008-2015 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 "LoginDatabase.h"
+
+void LoginDatabaseConnection::DoPrepareStatements()
+{
+ if (!m_reconnecting)
+ m_stmts.resize(MAX_LOGINDATABASE_STATEMENTS);
+
+ 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_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_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 id, sessionkey, last_ip, locked, expansion, mutetime, locale, recruiter, os FROM account WHERE username = ?", CONNECTION_SYNCH);
+ 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_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);
+ PrepareStatement(LOGIN_DEL_IP_NOT_BANNED, "DELETE FROM ip_banned WHERE ip = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_INS_ACCOUNT_BANNED, "INSERT INTO account_banned VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?, 1)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_ACCOUNT_NOT_BANNED, "UPDATE account_banned SET active = 0 WHERE id = ? AND active != 0", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM, "DELETE FROM realmcharacters WHERE acctid = ? AND realmid = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_DEL_REALM_CHARACTERS, "DELETE FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_INS_REALM_CHARACTERS, "INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_SUM_REALM_CHARACTERS, "SELECT SUM(numchars) FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_INS_ACCOUNT, "INSERT INTO account(username, sha_pass_hash, reg_mail, email, joindate) VALUES(?, ?, ?, ?, NOW())", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_INS_REALM_CHARACTERS_INIT, "INSERT INTO realmcharacters (realmid, acctid, numchars) SELECT realmlist.id, account.id, 0 FROM realmlist, account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_EXPANSION, "UPDATE account SET expansion = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_ACCOUNT_LOCK, "UPDATE account SET locked = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_ACCOUNT_LOCK_CONTRY, "UPDATE account SET lock_country = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_INS_LOG, "INSERT INTO logs (time, realm, type, level, string) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_USERNAME, "UPDATE account SET v = 0, s = 0, username = ?, sha_pass_hash = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_PASSWORD, "UPDATE account SET v = 0, s = 0, sha_pass_hash = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_EMAIL, "UPDATE account SET email = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_REG_EMAIL, "UPDATE account SET reg_mail = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_MUTE_TIME, "UPDATE account SET mutetime = ? , mutereason = ? , muteby = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_MUTE_TIME_LOGIN, "UPDATE account SET mutetime = ? WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_LAST_IP, "UPDATE account SET last_ip = ? WHERE username = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_LAST_ATTEMPT_IP, "UPDATE account SET last_attempt_ip = ? WHERE username = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_ACCOUNT_ONLINE, "UPDATE account SET online = 1 WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_UPD_UPTIME_PLAYERS, "UPDATE uptime SET uptime = ?, maxplayers = ? WHERE realmid = ? AND starttime = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_DEL_OLD_LOGS, "DELETE FROM logs WHERE (time + ?) < ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_DEL_ACCOUNT_ACCESS, "DELETE FROM account_access WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM, "DELETE FROM account_access WHERE id = ? AND (RealmID = ? OR RealmID = -1)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_INS_ACCOUNT_ACCESS, "INSERT INTO account_access (id,gmlevel,RealmID) VALUES (?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL, "SELECT gmlevel FROM account_access WHERE id = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_GET_GMLEVEL_BY_REALMID, "SELECT gmlevel FROM account_access WHERE id = ? AND (RealmID = ? OR RealmID = -1)", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_GET_USERNAME_BY_ID, "SELECT username FROM account WHERE id = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_CHECK_PASSWORD, "SELECT 1 FROM account WHERE id = ? AND sha_pass_hash = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME, "SELECT 1 FROM account WHERE username = ? AND sha_pass_hash = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_PINFO, "SELECT a.username, aa.gmlevel, a.email, a.reg_mail, a.last_ip, DATE_FORMAT(a.last_login, '%Y-%m-%d %T'), a.mutetime, a.mutereason, a.muteby, a.failed_logins, a.locked, a.OS FROM account a LEFT JOIN account_access aa ON (a.id = aa.id AND (aa.RealmID = ? OR aa.RealmID = -1)) WHERE a.id = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_PINFO_BANS, "SELECT unbandate, bandate = unbandate, bannedby, banreason FROM account_banned WHERE id = ? AND active ORDER BY bandate ASC LIMIT 1", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_GM_ACCOUNTS, "SELECT a.username, aa.gmlevel FROM account a, account_access aa WHERE a.id=aa.id AND aa.gmlevel >= ? AND (aa.realmid = -1 OR aa.realmid = ?)", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_ACCOUNT_INFO, "SELECT a.username, a.last_ip, aa.gmlevel, a.expansion FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.id = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS_GMLEVEL_TEST, "SELECT 1 FROM account_access WHERE id = ? AND gmlevel > ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS, "SELECT a.id, aa.gmlevel, aa.RealmID FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_ACCOUNT_RECRUITER, "SELECT 1 FROM account WHERE recruiter = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_BANS, "SELECT 1 FROM account_banned WHERE id = ? AND active = 1 UNION SELECT 1 FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_ACCOUNT_WHOIS, "SELECT username, email, last_ip FROM account WHERE id = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_LAST_ATTEMPT_IP, "SELECT last_attempt_ip FROM account WHERE id = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_LAST_IP, "SELECT last_ip FROM account WHERE id = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_REALMLIST_SECURITY_LEVEL, "SELECT allowedSecurityLevel from realmlist WHERE id = ?", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_DEL_ACCOUNT, "DELETE FROM account WHERE id = ?", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_IP2NATION_COUNTRY, "SELECT c.country FROM ip2nationCountries c, ip2nation i WHERE i.ip < ? AND c.code = i.country ORDER BY i.ip DESC LIMIT 0,1", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_SEL_AUTOBROADCAST, "SELECT id, weight, text FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_GET_EMAIL_BY_ID, "SELECT email FROM account WHERE id = ?", CONNECTION_SYNCH);
+ // 0: uint32, 1: uint32, 2: uint8, 3: uint32, 4: string // Complete name: "Login_Insert_AccountLoginDeLete_IP_Logging"
+ PrepareStatement(LOGIN_INS_ALDL_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, (SELECT last_ip FROM account WHERE id = ?), ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC);
+ // 0: uint32, 1: uint32, 2: uint8, 3: uint32, 4: string // Complete name: "Login_Insert_FailedAccountLogin_IP_Logging"
+ PrepareStatement(LOGIN_INS_FACL_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, (SELECT last_attempt_ip FROM account WHERE id = ?), ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC);
+ // 0: uint32, 1: uint32, 2: uint8, 3: string, 4: string // Complete name: "Login_Insert_CharacterDelete_IP_Logging"
+ PrepareStatement(LOGIN_INS_CHAR_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES (?, ?, ?, ?, ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC);
+ // 0: string, 1: string, 2: string // Complete name: "Login_Insert_Failed_Account_Login_due_password_IP_Logging"
+ PrepareStatement(LOGIN_INS_FALP_IP_LOGGING, "INSERT INTO logs_ip_actions (account_id,character_guid,type,ip,systemnote,unixtime,time) VALUES ((SELECT id FROM account WHERE username = ?), 0, 1, ?, ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_ACCOUNT_ACCESS_BY_ID, "SELECT gmlevel, RealmID FROM account_access WHERE id = ? and (RealmID = ? OR RealmID = -1) ORDER BY gmlevel desc", CONNECTION_SYNCH);
+
+ PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, "SELECT permissionId, granted FROM rbac_account_permissions WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY permissionId, realmId", CONNECTION_SYNCH);
+ PrepareStatement(LOGIN_INS_RBAC_ACCOUNT_PERMISSION, "INSERT INTO rbac_account_permissions (accountId, permissionId, granted, realmId) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE granted = VALUES(granted)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_DEL_RBAC_ACCOUNT_PERMISSION, "DELETE FROM rbac_account_permissions WHERE accountId = ? AND permissionId = ? AND (realmId = ? OR realmId = -1)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_INS_ACCOUNT_MUTE, "INSERT INTO account_muted VALUES (?, UNIX_TIMESTAMP(), ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(LOGIN_SEL_ACCOUNT_MUTE_INFO, "SELECT mutedate, mutetime, mutereason, mutedby FROM account_muted WHERE guid = ? ORDER BY mutedate ASC", CONNECTION_SYNCH);
+}
diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h
new file mode 100644
index 00000000000..7f6cffa520f
--- /dev/null
+++ b/src/server/database/Database/Implementation/LoginDatabase.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2008-2015 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 _LOGINDATABASE_H
+#define _LOGINDATABASE_H
+
+#include "DatabaseWorkerPool.h"
+#include "MySQLConnection.h"
+
+class LoginDatabaseConnection : public MySQLConnection
+{
+ public:
+ //- Constructors for sync and async connections
+ LoginDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { }
+ LoginDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { }
+
+ //- Loads database type specific prepared statements
+ void DoPrepareStatements() override;
+};
+
+typedef DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabaseWorkerPool;
+
+enum LoginDatabaseStatements
+{
+ /* Naming standard for defines:
+ {DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed}
+ When updating more than one field, consider looking at the calling function
+ name for a suiting suffix.
+ */
+
+ LOGIN_SEL_REALMLIST,
+ LOGIN_DEL_EXPIRED_IP_BANS,
+ LOGIN_UPD_EXPIRED_ACCOUNT_BANS,
+ 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_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_ACCOUNT_BY_IP,
+ LOGIN_INS_IP_BANNED,
+ LOGIN_DEL_IP_NOT_BANNED,
+ LOGIN_SEL_IP_BANNED_ALL,
+ LOGIN_SEL_IP_BANNED_BY_IP,
+ LOGIN_SEL_ACCOUNT_BY_ID,
+ LOGIN_INS_ACCOUNT_BANNED,
+ LOGIN_UPD_ACCOUNT_NOT_BANNED,
+ LOGIN_DEL_REALM_CHARACTERS_BY_REALM,
+ LOGIN_DEL_REALM_CHARACTERS,
+ LOGIN_INS_REALM_CHARACTERS,
+ LOGIN_SEL_SUM_REALM_CHARACTERS,
+ LOGIN_INS_ACCOUNT,
+ LOGIN_INS_REALM_CHARACTERS_INIT,
+ LOGIN_UPD_EXPANSION,
+ LOGIN_UPD_ACCOUNT_LOCK,
+ LOGIN_UPD_ACCOUNT_LOCK_CONTRY,
+ LOGIN_INS_LOG,
+ LOGIN_UPD_USERNAME,
+ LOGIN_UPD_PASSWORD,
+ LOGIN_UPD_EMAIL,
+ LOGIN_UPD_REG_EMAIL,
+ LOGIN_UPD_MUTE_TIME,
+ LOGIN_UPD_MUTE_TIME_LOGIN,
+ LOGIN_UPD_LAST_IP,
+ LOGIN_UPD_LAST_ATTEMPT_IP,
+ LOGIN_UPD_ACCOUNT_ONLINE,
+ LOGIN_UPD_UPTIME_PLAYERS,
+ LOGIN_DEL_OLD_LOGS,
+ LOGIN_DEL_ACCOUNT_ACCESS,
+ LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM,
+ LOGIN_INS_ACCOUNT_ACCESS,
+ LOGIN_GET_ACCOUNT_ID_BY_USERNAME,
+ LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL,
+ LOGIN_GET_GMLEVEL_BY_REALMID,
+ LOGIN_GET_USERNAME_BY_ID,
+ LOGIN_SEL_CHECK_PASSWORD,
+ LOGIN_SEL_CHECK_PASSWORD_BY_NAME,
+ LOGIN_SEL_PINFO,
+ LOGIN_SEL_PINFO_BANS,
+ LOGIN_SEL_GM_ACCOUNTS,
+ LOGIN_SEL_ACCOUNT_INFO,
+ LOGIN_SEL_ACCOUNT_ACCESS_GMLEVEL_TEST,
+ LOGIN_SEL_ACCOUNT_ACCESS,
+ LOGIN_SEL_ACCOUNT_RECRUITER,
+ LOGIN_SEL_BANS,
+ LOGIN_SEL_ACCOUNT_WHOIS,
+ LOGIN_SEL_REALMLIST_SECURITY_LEVEL,
+ LOGIN_DEL_ACCOUNT,
+ LOGIN_SEL_IP2NATION_COUNTRY,
+ LOGIN_SEL_AUTOBROADCAST,
+ LOGIN_SEL_LAST_ATTEMPT_IP,
+ LOGIN_SEL_LAST_IP,
+ LOGIN_GET_EMAIL_BY_ID,
+ LOGIN_INS_ALDL_IP_LOGGING,
+ LOGIN_INS_FACL_IP_LOGGING,
+ LOGIN_INS_CHAR_IP_LOGGING,
+ LOGIN_INS_FALP_IP_LOGGING,
+
+ LOGIN_SEL_ACCOUNT_ACCESS_BY_ID,
+ LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS,
+ LOGIN_INS_RBAC_ACCOUNT_PERMISSION,
+ LOGIN_DEL_RBAC_ACCOUNT_PERMISSION,
+
+ LOGIN_INS_ACCOUNT_MUTE,
+ LOGIN_SEL_ACCOUNT_MUTE_INFO,
+ MAX_LOGINDATABASE_STATEMENTS
+};
+
+#endif
diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp
new file mode 100644
index 00000000000..d9aad94293e
--- /dev/null
+++ b/src/server/database/Database/Implementation/WorldDatabase.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2008-2015 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 "WorldDatabase.h"
+
+void WorldDatabaseConnection::DoPrepareStatements()
+{
+ if (!m_reconnecting)
+ m_stmts.resize(MAX_WORLDDATABASE_STATEMENTS);
+
+ PrepareStatement(WORLD_SEL_QUEST_POOLS, "SELECT entry, pool_entry FROM pool_quest", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_DEL_CRELINKED_RESPAWN, "DELETE FROM linked_respawn WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_REP_CREATURE_LINKED_RESPAWN, "REPLACE INTO linked_respawn (guid, linkedGuid) VALUES (?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_SEL_CREATURE_TEXT, "SELECT entry, groupid, id, text, type, language, probability, emote, duration, sound, BroadcastTextId, TextRange FROM creature_text", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_SMART_SCRIPTS, "SELECT entryorguid, source_type, id, link, event_type, event_phase_mask, event_chance, event_flags, event_param1, event_param2, event_param3, event_param4, action_type, action_param1, action_param2, action_param3, action_param4, action_param5, action_param6, target_type, target_param1, target_param2, target_param3, target_x, target_y, target_z, target_o FROM smart_scripts ORDER BY entryorguid, source_type, id, link", CONNECTION_SYNCH);
+ 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_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);
+ PrepareStatement(WORLD_DEL_NPC_VENDOR, "DELETE FROM npc_vendor WHERE entry = ? AND item = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_SEL_NPC_VENDOR_REF, "SELECT item, maxcount, incrtime, ExtendedCost FROM npc_vendor WHERE entry = ? ORDER BY slot ASC", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_UPD_CREATURE_MOVEMENT_TYPE, "UPDATE creature SET MovementType = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_CREATURE_FACTION, "UPDATE creature_template SET faction = ? WHERE entry = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_CREATURE_NPCFLAG, "UPDATE creature_template SET npcflag = ? WHERE entry = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_CREATURE_POSITION, "UPDATE creature SET position_x = ?, position_y = ?, position_z = ?, orientation = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_CREATURE_SPAWN_DISTANCE, "UPDATE creature SET spawndist = ?, MovementType = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_CREATURE_SPAWN_TIME_SECS, "UPDATE creature SET spawntimesecs = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_INS_CREATURE_FORMATION, "INSERT INTO creature_formations (leaderGUID, memberGUID, dist, angle, groupAI) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_INS_WAYPOINT_DATA, "INSERT INTO waypoint_data (id, point, position_x, position_y, position_z) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_DEL_WAYPOINT_DATA, "DELETE FROM waypoint_data WHERE id = ? AND point = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_WAYPOINT_DATA_POINT, "UPDATE waypoint_data SET point = point - 1 WHERE id = ? AND point > ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_WAYPOINT_DATA_POSITION, "UPDATE waypoint_data SET position_x = ?, position_y = ?, position_z = ? where id = ? AND point = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_WAYPOINT_DATA_WPGUID, "UPDATE waypoint_data SET wpguid = ? WHERE id = ? and point = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_SEL_WAYPOINT_DATA_MAX_ID, "SELECT MAX(id) FROM waypoint_data", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_WAYPOINT_DATA_MAX_POINT, "SELECT MAX(point) FROM waypoint_data WHERE id = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID, "SELECT point, position_x, position_y, position_z, orientation, move_type, delay, action, action_chance FROM waypoint_data WHERE id = ? ORDER BY point", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_BY_ID, "SELECT point, position_x, position_y, position_z FROM waypoint_data WHERE id = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_FIRST_BY_ID, "SELECT position_x, position_y, position_z FROM waypoint_data WHERE point = 1 AND id = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_LAST_BY_ID, "SELECT position_x, position_y, position_z, orientation FROM waypoint_data WHERE id = ? ORDER BY point DESC LIMIT 1", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_WPGUID, "SELECT id, point FROM waypoint_data WHERE wpguid = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_WAYPOINT_DATA_ALL_BY_WPGUID, "SELECT id, point, delay, move_type, action, action_chance FROM waypoint_data WHERE wpguid = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_UPD_WAYPOINT_DATA_ALL_WPGUID, "UPDATE waypoint_data SET wpguid = 0", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_POS, "SELECT id, point FROM waypoint_data WHERE (abs(position_x - ?) <= ?) and (abs(position_y - ?) <= ?) and (abs(position_z - ?) <= ?)", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_WAYPOINT_DATA_WPGUID_BY_ID, "SELECT wpguid FROM waypoint_data WHERE id = ? and wpguid <> 0", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_WAYPOINT_DATA_ACTION, "SELECT DISTINCT action FROM waypoint_data", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPTS_MAX_ID, "SELECT MAX(guid) FROM waypoint_scripts", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_INS_CREATURE_ADDON, "INSERT INTO creature_addon(guid, path_id) VALUES (?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_CREATURE_ADDON_PATH, "UPDATE creature_addon SET path_id = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_DEL_CREATURE_ADDON, "DELETE FROM creature_addon WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_SEL_CREATURE_ADDON_BY_GUID, "SELECT guid FROM creature_addon WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_INS_WAYPOINT_SCRIPT, "INSERT INTO waypoint_scripts (guid) VALUES (?)", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_DEL_WAYPOINT_SCRIPT, "DELETE FROM waypoint_scripts WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_WAYPOINT_SCRIPT_ID, "UPDATE waypoint_scripts SET id = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_WAYPOINT_SCRIPT_X, "UPDATE waypoint_scripts SET x = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_WAYPOINT_SCRIPT_Y, "UPDATE waypoint_scripts SET y = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_WAYPOINT_SCRIPT_Z, "UPDATE waypoint_scripts SET z = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_WAYPOINT_SCRIPT_O, "UPDATE waypoint_scripts SET o = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, permission, help FROM command", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, modelid1, modelid2, modelid3, modelid4, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, scale, rank, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, trainer_type, trainer_spell, trainer_class, trainer_race, type, type_flags, lootid, pickpocketloot, skinloot, resistance1, resistance2, resistance3, resistance4, resistance5, resistance6, spell1, spell2, spell3, spell4, spell5, spell6, spell7, spell8, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, InhabitType, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, DamageModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, flags_extra, ScriptName FROM creature_template WHERE entry = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_CREATURE_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM creature WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_INS_CREATURE, "INSERT INTO creature (guid, id , map, spawnMask, phaseMask, modelid, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, spawndist, currentwaypoint, curhealth, curmana, MovementType, npcflag, unit_flags, dynamicflags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_DEL_GAME_EVENT_CREATURE, "DELETE FROM game_event_creature WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_DEL_GAME_EVENT_MODEL_EQUIP, "DELETE FROM game_event_model_equip WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_INS_GAMEOBJECT, "INSERT INTO gameobject (guid, id, map, spawnMask, phaseMask, position_x, position_y, position_z, orientation, rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_INS_DISABLES, "INSERT INTO disables (entry, sourceType, flags, comment) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_SEL_DISABLES, "SELECT entry FROM disables WHERE entry = ? AND sourceType = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_DEL_DISABLES, "DELETE FROM disables WHERE entry = ? AND sourceType = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA, "UPDATE creature SET zoneId = ?, areaId = ? WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA, "UPDATE gameobject SET zoneId = ?, areaId = ? WHERE guid = ?", CONNECTION_ASYNC);
+}
diff --git a/src/server/database/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h
new file mode 100644
index 00000000000..36fd6fbb186
--- /dev/null
+++ b/src/server/database/Database/Implementation/WorldDatabase.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2008-2015 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 _WORLDDATABASE_H
+#define _WORLDDATABASE_H
+
+#include "DatabaseWorkerPool.h"
+#include "MySQLConnection.h"
+
+class WorldDatabaseConnection : public MySQLConnection
+{
+ public:
+ //- Constructors for sync and async connections
+ WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) { }
+ WorldDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo) { }
+
+ //- Loads database type specific prepared statements
+ void DoPrepareStatements() override;
+};
+
+typedef DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabaseWorkerPool;
+
+enum WorldDatabaseStatements
+{
+ /* Naming standard for defines:
+ {DB}_{SEL/INS/UPD/DEL/REP}_{Summary of data changed}
+ When updating more than one field, consider looking at the calling function
+ name for a suiting suffix.
+ */
+
+ WORLD_SEL_QUEST_POOLS,
+ WORLD_DEL_CRELINKED_RESPAWN,
+ WORLD_REP_CREATURE_LINKED_RESPAWN,
+ WORLD_SEL_CREATURE_TEXT,
+ WORLD_SEL_SMART_SCRIPTS,
+ WORLD_SEL_SMARTAI_WP,
+ WORLD_DEL_GAMEOBJECT,
+ WORLD_DEL_EVENT_GAMEOBJECT,
+ WORLD_INS_GRAVEYARD_ZONE,
+ WORLD_DEL_GRAVEYARD_ZONE,
+ WORLD_INS_GAME_TELE,
+ WORLD_DEL_GAME_TELE,
+ WORLD_INS_NPC_VENDOR,
+ WORLD_DEL_NPC_VENDOR,
+ WORLD_SEL_NPC_VENDOR_REF,
+ WORLD_UPD_CREATURE_MOVEMENT_TYPE,
+ WORLD_UPD_CREATURE_FACTION,
+ WORLD_UPD_CREATURE_NPCFLAG,
+ WORLD_UPD_CREATURE_POSITION,
+ WORLD_UPD_CREATURE_SPAWN_DISTANCE,
+ WORLD_UPD_CREATURE_SPAWN_TIME_SECS,
+ WORLD_INS_CREATURE_FORMATION,
+ WORLD_INS_WAYPOINT_DATA,
+ WORLD_DEL_WAYPOINT_DATA,
+ WORLD_UPD_WAYPOINT_DATA_POINT,
+ WORLD_UPD_WAYPOINT_DATA_POSITION,
+ WORLD_UPD_WAYPOINT_DATA_WPGUID,
+ WORLD_UPD_WAYPOINT_DATA_ALL_WPGUID,
+ WORLD_SEL_WAYPOINT_DATA_MAX_ID,
+ WORLD_SEL_WAYPOINT_DATA_BY_ID,
+ WORLD_SEL_WAYPOINT_DATA_POS_BY_ID,
+ WORLD_SEL_WAYPOINT_DATA_POS_FIRST_BY_ID,
+ WORLD_SEL_WAYPOINT_DATA_POS_LAST_BY_ID,
+ WORLD_SEL_WAYPOINT_DATA_BY_WPGUID,
+ WORLD_SEL_WAYPOINT_DATA_ALL_BY_WPGUID,
+ WORLD_SEL_WAYPOINT_DATA_MAX_POINT,
+ WORLD_SEL_WAYPOINT_DATA_BY_POS,
+ WORLD_SEL_WAYPOINT_DATA_WPGUID_BY_ID,
+ WORLD_SEL_WAYPOINT_DATA_ACTION,
+ WORLD_SEL_WAYPOINT_SCRIPTS_MAX_ID,
+ WORLD_UPD_CREATURE_ADDON_PATH,
+ WORLD_INS_CREATURE_ADDON,
+ WORLD_DEL_CREATURE_ADDON,
+ WORLD_SEL_CREATURE_ADDON_BY_GUID,
+ WORLD_INS_WAYPOINT_SCRIPT,
+ WORLD_DEL_WAYPOINT_SCRIPT,
+ WORLD_UPD_WAYPOINT_SCRIPT_ID,
+ WORLD_UPD_WAYPOINT_SCRIPT_X,
+ WORLD_UPD_WAYPOINT_SCRIPT_Y,
+ WORLD_UPD_WAYPOINT_SCRIPT_Z,
+ WORLD_UPD_WAYPOINT_SCRIPT_O,
+ WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID,
+ WORLD_DEL_CREATURE,
+ WORLD_SEL_COMMANDS,
+ WORLD_SEL_CREATURE_TEMPLATE,
+ WORLD_SEL_WAYPOINT_SCRIPT_BY_ID,
+ WORLD_SEL_ITEM_TEMPLATE_BY_NAME,
+ WORLD_SEL_CREATURE_BY_ID,
+ WORLD_SEL_GAMEOBJECT_NEAREST,
+ WORLD_SEL_CREATURE_NEAREST,
+ WORLD_SEL_GAMEOBJECT_TARGET,
+ WORLD_INS_CREATURE,
+ WORLD_DEL_GAME_EVENT_CREATURE,
+ WORLD_DEL_GAME_EVENT_MODEL_EQUIP,
+ WORLD_INS_GAMEOBJECT,
+ WORLD_SEL_DISABLES,
+ WORLD_INS_DISABLES,
+ WORLD_DEL_DISABLES,
+ WORLD_UPD_CREATURE_ZONE_AREA_DATA,
+ WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA,
+
+ MAX_WORLDDATABASE_STATEMENTS
+};
+
+#endif
diff --git a/src/server/database/Database/MySQLConnection.cpp b/src/server/database/Database/MySQLConnection.cpp
new file mode 100644
index 00000000000..10f4a7baa18
--- /dev/null
+++ b/src/server/database/Database/MySQLConnection.cpp
@@ -0,0 +1,542 @@
+/*
+ * Copyright (C) 2008-2015 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 "Common.h"
+
+#ifdef _WIN32
+ #include <winsock2.h>
+#endif
+#include <mysql.h>
+#include <errmsg.h>
+
+#include "MySQLConnection.h"
+#include "QueryResult.h"
+#include "SQLOperation.h"
+#include "PreparedStatement.h"
+#include "DatabaseWorker.h"
+#include "Timer.h"
+#include "Log.h"
+#include "ProducerConsumerQueue.h"
+
+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) { }
+
+MySQLConnection::MySQLConnection(ProducerConsumerQueue<SQLOperation*>* queue, MySQLConnectionInfo& connInfo) :
+m_reconnecting(false),
+m_prepareError(false),
+m_queue(queue),
+m_Mysql(NULL),
+m_connectionInfo(connInfo),
+m_connectionFlags(CONNECTION_ASYNC)
+{
+ m_worker = new 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);
+}
+
+void MySQLConnection::Close()
+{
+ /// Only close us if we're not operating
+ delete this;
+}
+
+uint32 MySQLConnection::Open()
+{
+ MYSQL *mysqlInit;
+ mysqlInit = mysql_init(NULL);
+ if (!mysqlInit)
+ {
+ TC_LOG_ERROR("sql.sql", "Could not initialize Mysql connection to database `%s`", m_connectionInfo.database.c_str());
+ return CR_UNKNOWN_ERROR;
+ }
+
+ int port;
+ char const* unix_socket;
+ //unsigned int timeout = 10;
+
+ mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8");
+ //mysql_options(mysqlInit, MYSQL_OPT_READ_TIMEOUT, (char const*)&timeout);
+ #ifdef _WIN32
+ if (m_connectionInfo.host == ".") // named pipe use option (Windows)
+ {
+ unsigned int opt = MYSQL_PROTOCOL_PIPE;
+ mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (char const*)&opt);
+ port = 0;
+ unix_socket = 0;
+ }
+ else // generic case
+ {
+ port = atoi(m_connectionInfo.port_or_socket.c_str());
+ unix_socket = 0;
+ }
+ #else
+ if (m_connectionInfo.host == ".") // socket use option (Unix/Linux)
+ {
+ unsigned int opt = MYSQL_PROTOCOL_SOCKET;
+ mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (char const*)&opt);
+ m_connectionInfo.host = "localhost";
+ port = 0;
+ unix_socket = m_connectionInfo.port_or_socket.c_str();
+ }
+ else // generic case
+ {
+ port = atoi(m_connectionInfo.port_or_socket.c_str());
+ unix_socket = nullptr;
+ }
+ #endif
+
+ m_Mysql = mysql_real_connect(mysqlInit, m_connectionInfo.host.c_str(), m_connectionInfo.user.c_str(),
+ m_connectionInfo.password.c_str(), m_connectionInfo.database.c_str(), port, unix_socket, 0);
+
+ if (m_Mysql)
+ {
+ if (!m_reconnecting)
+ {
+ TC_LOG_INFO("sql.sql", "MySQL client library: %s", mysql_get_client_info());
+ TC_LOG_INFO("sql.sql", "MySQL server ver: %s ", mysql_get_server_info(m_Mysql));
+ // MySQL version above 5.1 IS required in both client and server and there is no known issue with different versions above 5.1
+ // if (mysql_get_server_version(m_Mysql) != mysql_get_client_version())
+ // TC_LOG_INFO("sql.sql", "[WARNING] MySQL client/server version mismatch; may conflict with behaviour of prepared statements.");
+ }
+
+ TC_LOG_INFO("sql.sql", "Connected to MySQL database at %s", m_connectionInfo.host.c_str());
+ mysql_autocommit(m_Mysql, 1);
+
+ // set connection properties to UTF8 to properly handle locales for different
+ // server configs - core sends data in UTF8, so MySQL must expect UTF8 too
+ mysql_set_character_set(m_Mysql, "utf8");
+ return 0;
+ }
+ else
+ {
+ TC_LOG_ERROR("sql.sql", "Could not connect to MySQL database at %s: %s", m_connectionInfo.host.c_str(), mysql_error(mysqlInit));
+ mysql_close(mysqlInit);
+ return mysql_errno(mysqlInit);
+ }
+}
+
+bool MySQLConnection::PrepareStatements()
+{
+ DoPrepareStatements();
+ return !m_prepareError;
+}
+
+bool MySQLConnection::Execute(const char* sql)
+{
+ if (!m_Mysql)
+ return false;
+
+ {
+ uint32 _s = getMSTime();
+
+ if (mysql_query(m_Mysql, sql))
+ {
+ uint32 lErrno = mysql_errno(m_Mysql);
+
+ TC_LOG_INFO("sql.sql", "SQL: %s", sql);
+ TC_LOG_ERROR("sql.sql", "[%u] %s", lErrno, mysql_error(m_Mysql));
+
+ if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
+ return Execute(sql); // Try again
+
+ return false;
+ }
+ else
+ TC_LOG_DEBUG("sql.sql", "[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql);
+ }
+
+ return true;
+}
+
+bool MySQLConnection::Execute(PreparedStatement* stmt)
+{
+ if (!m_Mysql)
+ return false;
+
+ uint32 index = stmt->m_index;
+ {
+ MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index);
+ ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query
+ m_mStmt->m_stmt = stmt; // Cross reference them for debug output
+ stmt->m_stmt = m_mStmt; /// @todo Cleaner way
+
+ stmt->BindParameters();
+
+ MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT();
+ MYSQL_BIND* msql_BIND = m_mStmt->GetBind();
+
+ uint32 _s = getMSTime();
+
+ if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
+ {
+ uint32 lErrno = mysql_errno(m_Mysql);
+ TC_LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT));
+
+ if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
+ return Execute(stmt); // Try again
+
+ m_mStmt->ClearParameters();
+ return false;
+ }
+
+ if (mysql_stmt_execute(msql_STMT))
+ {
+ uint32 lErrno = mysql_errno(m_Mysql);
+ TC_LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT));
+
+ if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
+ return Execute(stmt); // Try again
+
+ m_mStmt->ClearParameters();
+ return false;
+ }
+
+ TC_LOG_DEBUG("sql.sql", "[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString(m_queries[index].first).c_str());
+
+ m_mStmt->ClearParameters();
+ return true;
+ }
+}
+
+bool MySQLConnection::_Query(PreparedStatement* stmt, MYSQL_RES **pResult, uint64* pRowCount, uint32* pFieldCount)
+{
+ if (!m_Mysql)
+ return false;
+
+ uint32 index = stmt->m_index;
+ {
+ MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index);
+ ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query
+ m_mStmt->m_stmt = stmt; // Cross reference them for debug output
+ stmt->m_stmt = m_mStmt; /// @todo Cleaner way
+
+ stmt->BindParameters();
+
+ MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT();
+ MYSQL_BIND* msql_BIND = m_mStmt->GetBind();
+
+ uint32 _s = getMSTime();
+
+ if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
+ {
+ uint32 lErrno = mysql_errno(m_Mysql);
+ TC_LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s", m_mStmt->getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT));
+
+ if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
+ return _Query(stmt, pResult, pRowCount, pFieldCount); // Try again
+
+ m_mStmt->ClearParameters();
+ return false;
+ }
+
+ if (mysql_stmt_execute(msql_STMT))
+ {
+ uint32 lErrno = mysql_errno(m_Mysql);
+ TC_LOG_ERROR("sql.sql", "SQL(p): %s\n [ERROR]: [%u] %s",
+ m_mStmt->getQueryString(m_queries[index].first).c_str(), lErrno, mysql_stmt_error(msql_STMT));
+
+ if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
+ return _Query(stmt, pResult, pRowCount, pFieldCount); // Try again
+
+ m_mStmt->ClearParameters();
+ return false;
+ }
+
+ TC_LOG_DEBUG("sql.sql", "[%u ms] SQL(p): %s", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString(m_queries[index].first).c_str());
+
+ m_mStmt->ClearParameters();
+
+ *pResult = mysql_stmt_result_metadata(msql_STMT);
+ *pRowCount = mysql_stmt_num_rows(msql_STMT);
+ *pFieldCount = mysql_stmt_field_count(msql_STMT);
+
+ return true;
+
+ }
+}
+
+ResultSet* MySQLConnection::Query(const char* sql)
+{
+ if (!sql)
+ return NULL;
+
+ MYSQL_RES *result = NULL;
+ MYSQL_FIELD *fields = NULL;
+ uint64 rowCount = 0;
+ uint32 fieldCount = 0;
+
+ if (!_Query(sql, &result, &fields, &rowCount, &fieldCount))
+ return NULL;
+
+ return new ResultSet(result, fields, rowCount, fieldCount);
+}
+
+bool MySQLConnection::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount)
+{
+ if (!m_Mysql)
+ return false;
+
+ {
+ uint32 _s = getMSTime();
+
+ if (mysql_query(m_Mysql, sql))
+ {
+ uint32 lErrno = mysql_errno(m_Mysql);
+ TC_LOG_INFO("sql.sql", "SQL: %s", sql);
+ TC_LOG_ERROR("sql.sql", "[%u] %s", lErrno, mysql_error(m_Mysql));
+
+ if (_HandleMySQLErrno(lErrno)) // If it returns true, an error was handled successfully (i.e. reconnection)
+ return _Query(sql, pResult, pFields, pRowCount, pFieldCount); // We try again
+
+ return false;
+ }
+ else
+ TC_LOG_DEBUG("sql.sql", "[%u ms] SQL: %s", getMSTimeDiff(_s, getMSTime()), sql);
+
+ *pResult = mysql_store_result(m_Mysql);
+ *pRowCount = mysql_affected_rows(m_Mysql);
+ *pFieldCount = mysql_field_count(m_Mysql);
+ }
+
+ if (!*pResult )
+ return false;
+
+ if (!*pRowCount)
+ {
+ mysql_free_result(*pResult);
+ return false;
+ }
+
+ *pFields = mysql_fetch_fields(*pResult);
+
+ return true;
+}
+
+void MySQLConnection::BeginTransaction()
+{
+ Execute("START TRANSACTION");
+}
+
+void MySQLConnection::RollbackTransaction()
+{
+ Execute("ROLLBACK");
+}
+
+void MySQLConnection::CommitTransaction()
+{
+ Execute("COMMIT");
+}
+
+int MySQLConnection::ExecuteTransaction(SQLTransaction& transaction)
+{
+ std::list<SQLElementData> const& queries = transaction->m_queries;
+ if (queries.empty())
+ return -1;
+
+ BeginTransaction();
+
+ std::list<SQLElementData>::const_iterator itr;
+ for (itr = queries.begin(); itr != queries.end(); ++itr)
+ {
+ SQLElementData const& data = *itr;
+ switch (itr->type)
+ {
+ case SQL_ELEMENT_PREPARED:
+ {
+ PreparedStatement* stmt = data.element.stmt;
+ ASSERT(stmt);
+ if (!Execute(stmt))
+ {
+ TC_LOG_WARN("sql.sql", "Transaction aborted. %u queries not executed.", (uint32)queries.size());
+ int errorCode = GetLastError();
+ RollbackTransaction();
+ return errorCode;
+ }
+ }
+ break;
+ case SQL_ELEMENT_RAW:
+ {
+ const char* sql = data.element.query;
+ ASSERT(sql);
+ if (!Execute(sql))
+ {
+ TC_LOG_WARN("sql.sql", "Transaction aborted. %u queries not executed.", (uint32)queries.size());
+ int errorCode = GetLastError();
+ RollbackTransaction();
+ return errorCode;
+ }
+ }
+ break;
+ }
+ }
+
+ // we might encounter errors during certain queries, and depending on the kind of error
+ // we might want to restart the transaction. So to prevent data loss, we only clean up when it's all done.
+ // This is done in calling functions DatabaseWorkerPool<T>::DirectCommitTransaction and TransactionTask::Execute,
+ // and not while iterating over every element.
+
+ CommitTransaction();
+ return 0;
+}
+
+MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index)
+{
+ ASSERT(index < m_stmts.size());
+ MySQLPreparedStatement* ret = m_stmts[index];
+ 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");
+
+ return ret;
+}
+
+void MySQLConnection::PrepareStatement(uint32 index, const char* sql, ConnectionFlags flags)
+{
+ m_queries.insert(PreparedStatementMap::value_type(index, std::make_pair(sql, flags)));
+
+ // For reconnection case
+ if (m_reconnecting)
+ delete m_stmts[index];
+
+ // Check if specified query should be prepared on this connection
+ // i.e. don't prepare async statements on synchronous connections
+ // to save memory that will not be used.
+ if (!(m_connectionFlags & flags))
+ {
+ m_stmts[index] = NULL;
+ return;
+ }
+
+ MYSQL_STMT* stmt = mysql_stmt_init(m_Mysql);
+ if (!stmt)
+ {
+ TC_LOG_ERROR("sql.sql", "In mysql_stmt_init() id: %u, sql: \"%s\"", index, sql);
+ TC_LOG_ERROR("sql.sql", "%s", mysql_error(m_Mysql));
+ m_prepareError = true;
+ }
+ else
+ {
+ if (mysql_stmt_prepare(stmt, sql, static_cast<unsigned long>(strlen(sql))))
+ {
+ TC_LOG_ERROR("sql.sql", "In mysql_stmt_prepare() id: %u, sql: \"%s\"", index, sql);
+ TC_LOG_ERROR("sql.sql", "%s", mysql_stmt_error(stmt));
+ mysql_stmt_close(stmt);
+ m_prepareError = true;
+ }
+ else
+ {
+ MySQLPreparedStatement* mStmt = new MySQLPreparedStatement(stmt);
+ m_stmts[index] = mStmt;
+ }
+ }
+}
+
+PreparedResultSet* MySQLConnection::Query(PreparedStatement* stmt)
+{
+ MYSQL_RES *result = NULL;
+ uint64 rowCount = 0;
+ uint32 fieldCount = 0;
+
+ if (!_Query(stmt, &result, &rowCount, &fieldCount))
+ return NULL;
+
+ if (mysql_more_results(m_Mysql))
+ {
+ mysql_next_result(m_Mysql);
+ }
+ return new PreparedResultSet(stmt->m_stmt->GetSTMT(), result, rowCount, fieldCount);
+}
+
+bool MySQLConnection::_HandleMySQLErrno(uint32 errNo)
+{
+ switch (errNo)
+ {
+ case CR_SERVER_GONE_ERROR:
+ case CR_SERVER_LOST:
+ case CR_INVALID_CONN_HANDLE:
+ case CR_SERVER_LOST_EXTENDED:
+ {
+ m_reconnecting = true;
+ uint64 oldThreadId = mysql_thread_id(GetHandle());
+ mysql_close(GetHandle());
+
+ uint32 const lErrno = Open();
+ if (!lErrno)
+ {
+ // 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_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");
+
+ 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)
+ }
+
+ case ER_LOCK_DEADLOCK:
+ return false; // Implemented in TransactionTask::Execute and DatabaseWorkerPool<T>::DirectCommitTransaction
+ // Query related errors - skip query
+ case ER_WRONG_VALUE_COUNT:
+ case ER_DUP_ENTRY:
+ return false;
+
+ // Outdated table or database structure - terminate core
+ case ER_BAD_FIELD_ERROR:
+ case ER_NO_SUCH_TABLE:
+ TC_LOG_ERROR("sql.sql", "Your database structure is not up to date. Please make sure you've executed all queries in the sql/updates folders.");
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+ std::abort();
+ return false;
+ case ER_PARSE_ERROR:
+ TC_LOG_ERROR("sql.sql", "Error while parsing SQL. Core fix required.");
+ std::this_thread::sleep_for(std::chrono::seconds(10));
+ std::abort();
+ return false;
+ default:
+ TC_LOG_ERROR("sql.sql", "Unhandled MySQL errno %u. Unexpected behaviour possible.", errNo);
+ return false;
+ }
+}
diff --git a/src/server/database/Database/MySQLConnection.h b/src/server/database/Database/MySQLConnection.h
new file mode 100644
index 00000000000..78d8d2fb5dd
--- /dev/null
+++ b/src/server/database/Database/MySQLConnection.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2008-2015 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 "Transaction.h"
+#include "Util.h"
+#include "ProducerConsumerQueue.h"
+
+#ifndef _MYSQLCONNECTION_H
+#define _MYSQLCONNECTION_H
+
+class DatabaseWorker;
+class PreparedStatement;
+class MySQLPreparedStatement;
+class PingOperation;
+
+enum ConnectionFlags
+{
+ CONNECTION_ASYNC = 0x1,
+ CONNECTION_SYNCH = 0x2,
+ CONNECTION_BOTH = CONNECTION_ASYNC | CONNECTION_SYNCH
+};
+
+struct MySQLConnectionInfo
+{
+ explicit MySQLConnectionInfo(std::string const& infoString)
+ {
+ Tokenizer tokens(infoString, ';');
+
+ if (tokens.size() != 5)
+ return;
+
+ uint8 i = 0;
+
+ host.assign(tokens[i++]);
+ port_or_socket.assign(tokens[i++]);
+ user.assign(tokens[i++]);
+ password.assign(tokens[i++]);
+ database.assign(tokens[i++]);
+ }
+
+ std::string user;
+ std::string password;
+ std::string database;
+ std::string host;
+ std::string port_or_socket;
+};
+
+typedef std::map<uint32 /*index*/, std::pair<std::string /*query*/, ConnectionFlags /*sync/async*/> > PreparedStatementMap;
+
+class MySQLConnection
+{
+ template <class T> friend class DatabaseWorkerPool;
+ friend class PingOperation;
+
+ public:
+ MySQLConnection(MySQLConnectionInfo& connInfo); //! Constructor for synchronous connections.
+ MySQLConnection(ProducerConsumerQueue<SQLOperation*>* queue, MySQLConnectionInfo& connInfo); //! Constructor for asynchronous connections.
+ virtual ~MySQLConnection();
+
+ virtual uint32 Open();
+ void Close();
+
+ bool PrepareStatements();
+
+ public:
+ bool Execute(const char* sql);
+ bool Execute(PreparedStatement* stmt);
+ ResultSet* Query(const char* sql);
+ PreparedResultSet* Query(PreparedStatement* stmt);
+ bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount);
+ bool _Query(PreparedStatement* stmt, MYSQL_RES **pResult, uint64* pRowCount, uint32* pFieldCount);
+
+ void BeginTransaction();
+ void RollbackTransaction();
+ void CommitTransaction();
+ int ExecuteTransaction(SQLTransaction& transaction);
+
+ operator bool () const { return m_Mysql != NULL; }
+ void Ping() { mysql_ping(m_Mysql); }
+
+ uint32 GetLastError() { return mysql_errno(m_Mysql); }
+
+ protected:
+ bool LockIfReady()
+ {
+ /// Tries to acquire lock. If lock is acquired by another thread
+ /// the calling parent will just try another connection
+ return m_Mutex.try_lock();
+ }
+
+ void Unlock()
+ {
+ /// Called by parent databasepool. Will let other threads access this connection
+ m_Mutex.unlock();
+ }
+
+ MYSQL* GetHandle() { return m_Mysql; }
+ MySQLPreparedStatement* GetPreparedStatement(uint32 index);
+ void PrepareStatement(uint32 index, const char* sql, ConnectionFlags flags);
+
+ virtual void DoPrepareStatements() = 0;
+
+ protected:
+ std::vector<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);
+
+ private:
+ ProducerConsumerQueue<SQLOperation*>* m_queue; //! Queue shared with other asynchronous connections.
+ 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;
+
+ MySQLConnection(MySQLConnection const& right) = delete;
+ MySQLConnection& operator=(MySQLConnection const& right) = delete;
+};
+
+#endif
diff --git a/src/server/database/Database/MySQLThreading.h b/src/server/database/Database/MySQLThreading.h
new file mode 100644
index 00000000000..441f30cb6ec
--- /dev/null
+++ b/src/server/database/Database/MySQLThreading.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008-2015 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 _MYSQLTHREADING_H
+#define _MYSQLTHREADING_H
+
+#include "Log.h"
+
+class MySQL
+{
+ public:
+ static void Library_Init()
+ {
+ mysql_library_init(-1, NULL, NULL);
+ }
+
+ static void Library_End()
+ {
+ mysql_library_end();
+ }
+};
+
+#endif
diff --git a/src/server/database/Database/PreparedStatement.cpp b/src/server/database/Database/PreparedStatement.cpp
new file mode 100644
index 00000000000..1938005c712
--- /dev/null
+++ b/src/server/database/Database/PreparedStatement.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2008-2015 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 "PreparedStatement.h"
+#include "MySQLConnection.h"
+#include "Log.h"
+
+PreparedStatement::PreparedStatement(uint32 index) :
+m_stmt(NULL),
+m_index(index) { }
+
+PreparedStatement::~PreparedStatement() { }
+
+void PreparedStatement::BindParameters()
+{
+ ASSERT (m_stmt);
+
+ uint8 i = 0;
+ for (; i < statement_data.size(); i++)
+ {
+ switch (statement_data[i].type)
+ {
+ case TYPE_BOOL:
+ m_stmt->setBool(i, statement_data[i].data.boolean);
+ break;
+ case TYPE_UI8:
+ m_stmt->setUInt8(i, statement_data[i].data.ui8);
+ break;
+ case TYPE_UI16:
+ m_stmt->setUInt16(i, statement_data[i].data.ui16);
+ break;
+ case TYPE_UI32:
+ m_stmt->setUInt32(i, statement_data[i].data.ui32);
+ break;
+ case TYPE_I8:
+ m_stmt->setInt8(i, statement_data[i].data.i8);
+ break;
+ case TYPE_I16:
+ m_stmt->setInt16(i, statement_data[i].data.i16);
+ break;
+ case TYPE_I32:
+ m_stmt->setInt32(i, statement_data[i].data.i32);
+ break;
+ case TYPE_UI64:
+ m_stmt->setUInt64(i, statement_data[i].data.ui64);
+ break;
+ case TYPE_I64:
+ m_stmt->setInt64(i, statement_data[i].data.i64);
+ break;
+ case TYPE_FLOAT:
+ m_stmt->setFloat(i, statement_data[i].data.f);
+ break;
+ case TYPE_DOUBLE:
+ m_stmt->setDouble(i, statement_data[i].data.d);
+ break;
+ case TYPE_STRING:
+ m_stmt->setString(i, statement_data[i].str.c_str());
+ break;
+ case TYPE_NULL:
+ m_stmt->setNull(i);
+ break;
+ }
+ }
+ #ifdef _DEBUG
+ if (i < m_stmt->m_paramCount)
+ TC_LOG_WARN("sql.sql", "[WARNING]: BindParameters() for statement %u did not bind all allocated parameters", m_index);
+ #endif
+}
+
+//- Bind to buffer
+void PreparedStatement::setBool(const uint8 index, const bool value)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].data.boolean = value;
+ statement_data[index].type = TYPE_BOOL;
+}
+
+void PreparedStatement::setUInt8(const uint8 index, const uint8 value)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].data.ui8 = value;
+ statement_data[index].type = TYPE_UI8;
+}
+
+void PreparedStatement::setUInt16(const uint8 index, const uint16 value)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].data.ui16 = value;
+ statement_data[index].type = TYPE_UI16;
+}
+
+void PreparedStatement::setUInt32(const uint8 index, const uint32 value)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].data.ui32 = value;
+ statement_data[index].type = TYPE_UI32;
+}
+
+void PreparedStatement::setUInt64(const uint8 index, const uint64 value)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].data.ui64 = value;
+ statement_data[index].type = TYPE_UI64;
+}
+
+void PreparedStatement::setInt8(const uint8 index, const int8 value)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].data.i8 = value;
+ statement_data[index].type = TYPE_I8;
+}
+
+void PreparedStatement::setInt16(const uint8 index, const int16 value)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].data.i16 = value;
+ statement_data[index].type = TYPE_I16;
+}
+
+void PreparedStatement::setInt32(const uint8 index, const int32 value)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].data.i32 = value;
+ statement_data[index].type = TYPE_I32;
+}
+
+void PreparedStatement::setInt64(const uint8 index, const int64 value)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].data.i64 = value;
+ statement_data[index].type = TYPE_I64;
+}
+
+void PreparedStatement::setFloat(const uint8 index, const float value)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].data.f = value;
+ statement_data[index].type = TYPE_FLOAT;
+}
+
+void PreparedStatement::setDouble(const uint8 index, const double value)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].data.d = value;
+ statement_data[index].type = TYPE_DOUBLE;
+}
+
+void PreparedStatement::setString(const uint8 index, const std::string& value)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].str = value;
+ statement_data[index].type = TYPE_STRING;
+}
+
+void PreparedStatement::setNull(const uint8 index)
+{
+ if (index >= statement_data.size())
+ statement_data.resize(index+1);
+
+ statement_data[index].type = TYPE_NULL;
+}
+
+MySQLPreparedStatement::MySQLPreparedStatement(MYSQL_STMT* stmt) :
+m_stmt(NULL),
+m_Mstmt(stmt),
+m_bind(NULL)
+{
+ /// Initialize variable parameters
+ m_paramCount = mysql_stmt_param_count(stmt);
+ m_paramsSet.assign(m_paramCount, false);
+ m_bind = new MYSQL_BIND[m_paramCount];
+ memset(m_bind, 0, sizeof(MYSQL_BIND)*m_paramCount);
+
+ /// "If set to 1, causes mysql_stmt_store_result() to update the metadata MYSQL_FIELD->max_length value."
+ my_bool bool_tmp = 1;
+ mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &bool_tmp);
+}
+
+MySQLPreparedStatement::~MySQLPreparedStatement()
+{
+ ClearParameters();
+ if (m_Mstmt->bind_result_done)
+ {
+ delete[] m_Mstmt->bind->length;
+ delete[] m_Mstmt->bind->is_null;
+ }
+ mysql_stmt_close(m_Mstmt);
+ delete[] m_bind;
+}
+
+void MySQLPreparedStatement::ClearParameters()
+{
+ for (uint32 i=0; i < m_paramCount; ++i)
+ {
+ delete m_bind[i].length;
+ m_bind[i].length = NULL;
+ delete[] (char*) m_bind[i].buffer;
+ m_bind[i].buffer = NULL;
+ m_paramsSet[i] = false;
+ }
+}
+
+static bool ParamenterIndexAssertFail(uint32 stmtIndex, uint8 index, uint32 paramCount)
+{
+ TC_LOG_ERROR("sql.driver", "Attempted to bind parameter %u%s on a PreparedStatement %u (statement has only %u parameters)", uint32(index) + 1, (index == 1 ? "st" : (index == 2 ? "nd" : (index == 3 ? "rd" : "nd"))), stmtIndex, paramCount);
+ return false;
+}
+
+//- Bind on mysql level
+bool MySQLPreparedStatement::CheckValidIndex(uint8 index)
+{
+ ASSERT(index < m_paramCount || ParamenterIndexAssertFail(m_stmt->m_index, index, m_paramCount));
+
+ if (m_paramsSet[index])
+ TC_LOG_WARN("sql.sql", "[WARNING] Prepared Statement (id: %u) trying to bind value on already bound index (%u).", m_stmt->m_index, index);
+ return true;
+}
+
+void MySQLPreparedStatement::setBool(const uint8 index, const bool value)
+{
+ setUInt8(index, value ? 1 : 0);
+}
+
+void MySQLPreparedStatement::setUInt8(const uint8 index, const uint8 value)
+{
+ CheckValidIndex(index);
+ m_paramsSet[index] = true;
+ MYSQL_BIND* param = &m_bind[index];
+ setValue(param, MYSQL_TYPE_TINY, &value, sizeof(uint8), true);
+}
+
+void MySQLPreparedStatement::setUInt16(const uint8 index, const uint16 value)
+{
+ CheckValidIndex(index);
+ m_paramsSet[index] = true;
+ MYSQL_BIND* param = &m_bind[index];
+ setValue(param, MYSQL_TYPE_SHORT, &value, sizeof(uint16), true);
+}
+
+void MySQLPreparedStatement::setUInt32(const uint8 index, const uint32 value)
+{
+ CheckValidIndex(index);
+ m_paramsSet[index] = true;
+ MYSQL_BIND* param = &m_bind[index];
+ setValue(param, MYSQL_TYPE_LONG, &value, sizeof(uint32), true);
+}
+
+void MySQLPreparedStatement::setUInt64(const uint8 index, const uint64 value)
+{
+ CheckValidIndex(index);
+ m_paramsSet[index] = true;
+ MYSQL_BIND* param = &m_bind[index];
+ setValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(uint64), true);
+}
+
+void MySQLPreparedStatement::setInt8(const uint8 index, const int8 value)
+{
+ CheckValidIndex(index);
+ m_paramsSet[index] = true;
+ MYSQL_BIND* param = &m_bind[index];
+ setValue(param, MYSQL_TYPE_TINY, &value, sizeof(int8), false);
+}
+
+void MySQLPreparedStatement::setInt16(const uint8 index, const int16 value)
+{
+ CheckValidIndex(index);
+ m_paramsSet[index] = true;
+ MYSQL_BIND* param = &m_bind[index];
+ setValue(param, MYSQL_TYPE_SHORT, &value, sizeof(int16), false);
+}
+
+void MySQLPreparedStatement::setInt32(const uint8 index, const int32 value)
+{
+ CheckValidIndex(index);
+ m_paramsSet[index] = true;
+ MYSQL_BIND* param = &m_bind[index];
+ setValue(param, MYSQL_TYPE_LONG, &value, sizeof(int32), false);
+}
+
+void MySQLPreparedStatement::setInt64(const uint8 index, const int64 value)
+{
+ CheckValidIndex(index);
+ m_paramsSet[index] = true;
+ MYSQL_BIND* param = &m_bind[index];
+ setValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(int64), false);
+}
+
+void MySQLPreparedStatement::setFloat(const uint8 index, const float value)
+{
+ CheckValidIndex(index);
+ m_paramsSet[index] = true;
+ MYSQL_BIND* param = &m_bind[index];
+ setValue(param, MYSQL_TYPE_FLOAT, &value, sizeof(float), (value > 0.0f));
+}
+
+void MySQLPreparedStatement::setDouble(const uint8 index, const double value)
+{
+ CheckValidIndex(index);
+ m_paramsSet[index] = true;
+ MYSQL_BIND* param = &m_bind[index];
+ setValue(param, MYSQL_TYPE_DOUBLE, &value, sizeof(double), (value > 0.0f));
+}
+
+void MySQLPreparedStatement::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;
+ param->buffer_type = MYSQL_TYPE_VAR_STRING;
+ delete [] static_cast<char *>(param->buffer);
+ param->buffer = new char[len];
+ param->buffer_length = len;
+ param->is_null_value = 0;
+ delete param->length;
+ param->length = new unsigned long(len-1);
+
+ memcpy(param->buffer, value, len);
+}
+
+void MySQLPreparedStatement::setNull(const uint8 index)
+{
+ CheckValidIndex(index);
+ m_paramsSet[index] = true;
+ MYSQL_BIND* param = &m_bind[index];
+ param->buffer_type = MYSQL_TYPE_NULL;
+ delete [] static_cast<char *>(param->buffer);
+ param->buffer = NULL;
+ param->buffer_length = 0;
+ param->is_null_value = 1;
+ delete param->length;
+ param->length = NULL;
+}
+
+void MySQLPreparedStatement::setValue(MYSQL_BIND* param, enum_field_types type, const void* value, uint32 len, bool isUnsigned)
+{
+ param->buffer_type = type;
+ delete [] static_cast<char *>(param->buffer);
+ param->buffer = new char[len];
+ param->buffer_length = 0;
+ param->is_null_value = 0;
+ param->length = NULL; // Only != NULL for strings
+ param->is_unsigned = isUnsigned;
+
+ memcpy(param->buffer, value, len);
+}
+
+std::string MySQLPreparedStatement::getQueryString(std::string const& sqlPattern) const
+{
+ std::string queryString = sqlPattern;
+
+ size_t pos = 0;
+ for (uint32 i = 0; i < m_stmt->statement_data.size(); i++)
+ {
+ pos = queryString.find('?', pos);
+ std::stringstream ss;
+
+ switch (m_stmt->statement_data[i].type)
+ {
+ case TYPE_BOOL:
+ ss << uint16(m_stmt->statement_data[i].data.boolean);
+ break;
+ case TYPE_UI8:
+ ss << uint16(m_stmt->statement_data[i].data.ui8); // stringstream will append a character with that code instead of numeric representation
+ break;
+ case TYPE_UI16:
+ ss << m_stmt->statement_data[i].data.ui16;
+ break;
+ case TYPE_UI32:
+ ss << m_stmt->statement_data[i].data.ui32;
+ break;
+ case TYPE_I8:
+ ss << int16(m_stmt->statement_data[i].data.i8); // stringstream will append a character with that code instead of numeric representation
+ break;
+ case TYPE_I16:
+ ss << m_stmt->statement_data[i].data.i16;
+ break;
+ case TYPE_I32:
+ ss << m_stmt->statement_data[i].data.i32;
+ break;
+ case TYPE_UI64:
+ ss << m_stmt->statement_data[i].data.ui64;
+ break;
+ case TYPE_I64:
+ ss << m_stmt->statement_data[i].data.i64;
+ break;
+ case TYPE_FLOAT:
+ ss << m_stmt->statement_data[i].data.f;
+ break;
+ case TYPE_DOUBLE:
+ ss << m_stmt->statement_data[i].data.d;
+ break;
+ case TYPE_STRING:
+ ss << '\'' << m_stmt->statement_data[i].str << '\'';
+ break;
+ case TYPE_NULL:
+ ss << "NULL";
+ break;
+ }
+
+ std::string replaceStr = ss.str();
+ queryString.replace(pos, 1, replaceStr);
+ pos += replaceStr.length();
+ }
+
+ return queryString;
+}
+
+//- Execution
+PreparedStatementTask::PreparedStatementTask(PreparedStatement* stmt, bool async) :
+m_stmt(stmt), m_result(nullptr)
+{
+ m_has_result = async; // If it's async, then there's a result
+ if (async)
+ m_result = new PreparedQueryResultPromise();
+}
+
+PreparedStatementTask::~PreparedStatementTask()
+{
+ delete m_stmt;
+ if (m_has_result && m_result != nullptr)
+ delete m_result;
+}
+
+bool PreparedStatementTask::Execute()
+{
+ if (m_has_result)
+ {
+ PreparedResultSet* result = m_conn->Query(m_stmt);
+ if (!result || !result->GetRowCount())
+ {
+ delete result;
+ m_result->set_value(PreparedQueryResult(NULL));
+ return false;
+ }
+ m_result->set_value(PreparedQueryResult(result));
+ return true;
+ }
+
+ return m_conn->Execute(m_stmt);
+}
diff --git a/src/server/database/Database/PreparedStatement.h b/src/server/database/Database/PreparedStatement.h
new file mode 100644
index 00000000000..2b8ce8aac2d
--- /dev/null
+++ b/src/server/database/Database/PreparedStatement.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2008-2015 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 _PREPAREDSTATEMENT_H
+#define _PREPAREDSTATEMENT_H
+
+#include <future>
+#include "SQLOperation.h"
+
+#ifdef __APPLE__
+#undef TYPE_BOOL
+#endif
+
+//- Union for data buffer (upper-level bind -> queue -> lower-level bind)
+union PreparedStatementDataUnion
+{
+ bool boolean;
+ uint8 ui8;
+ int8 i8;
+ uint16 ui16;
+ int16 i16;
+ uint32 ui32;
+ int32 i32;
+ uint64 ui64;
+ int64 i64;
+ float f;
+ double d;
+};
+
+//- This enum helps us differ data held in above union
+enum PreparedStatementValueType
+{
+ TYPE_BOOL,
+ TYPE_UI8,
+ TYPE_UI16,
+ TYPE_UI32,
+ TYPE_UI64,
+ TYPE_I8,
+ TYPE_I16,
+ TYPE_I32,
+ TYPE_I64,
+ TYPE_FLOAT,
+ TYPE_DOUBLE,
+ TYPE_STRING,
+ TYPE_NULL
+};
+
+struct PreparedStatementData
+{
+ PreparedStatementDataUnion data;
+ PreparedStatementValueType type;
+ std::string str;
+};
+
+//- Forward declare
+class MySQLPreparedStatement;
+
+//- Upper-level class that is used in code
+class PreparedStatement
+{
+ friend class PreparedStatementTask;
+ friend class MySQLPreparedStatement;
+ friend class MySQLConnection;
+
+ public:
+ explicit PreparedStatement(uint32 index);
+ ~PreparedStatement();
+
+ void setBool(const uint8 index, const bool value);
+ void setUInt8(const uint8 index, const uint8 value);
+ void setUInt16(const uint8 index, const uint16 value);
+ void setUInt32(const uint8 index, const uint32 value);
+ void setUInt64(const uint8 index, const uint64 value);
+ void setInt8(const uint8 index, const int8 value);
+ void setInt16(const uint8 index, const int16 value);
+ void setInt32(const uint8 index, const int32 value);
+ void setInt64(const uint8 index, const int64 value);
+ void setFloat(const uint8 index, const float value);
+ void setDouble(const uint8 index, const double value);
+ void setString(const uint8 index, const std::string& value);
+ void setNull(const uint8 index);
+
+ protected:
+ void BindParameters();
+
+ protected:
+ MySQLPreparedStatement* m_stmt;
+ uint32 m_index;
+ std::vector<PreparedStatementData> statement_data; //- Buffer of parameters, not tied to MySQL in any way yet
+
+ PreparedStatement(PreparedStatement const& right) = delete;
+ PreparedStatement& operator=(PreparedStatement const& right) = delete;
+};
+
+//- Class of which the instances are unique per MySQLConnection
+//- access to these class objects is only done when a prepared statement task
+//- is executed.
+class MySQLPreparedStatement
+{
+ friend class MySQLConnection;
+ friend class PreparedStatement;
+
+ public:
+ MySQLPreparedStatement(MYSQL_STMT* stmt);
+ ~MySQLPreparedStatement();
+
+ void setBool(const uint8 index, const bool value);
+ void setUInt8(const uint8 index, const uint8 value);
+ void setUInt16(const uint8 index, const uint16 value);
+ void setUInt32(const uint8 index, const uint32 value);
+ void setUInt64(const uint8 index, const uint64 value);
+ void setInt8(const uint8 index, const int8 value);
+ void setInt16(const uint8 index, const int16 value);
+ void setInt32(const uint8 index, const int32 value);
+ void setInt64(const uint8 index, const int64 value);
+ void setFloat(const uint8 index, const float value);
+ void setDouble(const uint8 index, const double value);
+ void setString(const uint8 index, const char* value);
+ void setNull(const uint8 index);
+
+ protected:
+ MYSQL_STMT* GetSTMT() { return m_Mstmt; }
+ MYSQL_BIND* GetBind() { return m_bind; }
+ PreparedStatement* m_stmt;
+ void ClearParameters();
+ bool CheckValidIndex(uint8 index);
+ std::string getQueryString(std::string const& sqlPattern) const;
+
+ private:
+ void setValue(MYSQL_BIND* param, enum_field_types type, const void* value, uint32 len, bool isUnsigned);
+
+ private:
+ MYSQL_STMT* m_Mstmt;
+ uint32 m_paramCount;
+ std::vector<bool> m_paramsSet;
+ MYSQL_BIND* m_bind;
+
+ MySQLPreparedStatement(MySQLPreparedStatement const& right) = delete;
+ MySQLPreparedStatement& operator=(MySQLPreparedStatement const& right) = delete;
+};
+
+typedef std::future<PreparedQueryResult> PreparedQueryResultFuture;
+typedef std::promise<PreparedQueryResult> PreparedQueryResultPromise;
+
+//- Lower-level class, enqueuable operation
+class PreparedStatementTask : public SQLOperation
+{
+ public:
+ PreparedStatementTask(PreparedStatement* stmt, bool async = false);
+ ~PreparedStatementTask();
+
+ bool Execute() override;
+ PreparedQueryResultFuture GetFuture() { return m_result->get_future(); }
+
+ protected:
+ PreparedStatement* m_stmt;
+ bool m_has_result;
+ PreparedQueryResultPromise* m_result;
+};
+#endif
diff --git a/src/server/database/Database/QueryHolder.cpp b/src/server/database/Database/QueryHolder.cpp
new file mode 100644
index 00000000000..2fdb3825526
--- /dev/null
+++ b/src/server/database/Database/QueryHolder.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2008-2015 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 "MySQLConnection.h"
+#include "QueryHolder.h"
+#include "PreparedStatement.h"
+#include "Log.h"
+
+bool SQLQueryHolder::SetQuery(size_t index, const char *sql)
+{
+ if (m_queries.size() <= index)
+ {
+ TC_LOG_ERROR("sql.sql", "Query index (%u) out of range (size: %u) for query: %s", uint32(index), (uint32)m_queries.size(), sql);
+ return false;
+ }
+
+ /// not executed yet, just stored (it's not called a holder for nothing)
+ SQLElementData element;
+ element.type = SQL_ELEMENT_RAW;
+ element.element.query = strdup(sql);
+
+ SQLResultSetUnion result;
+ result.qresult = NULL;
+
+ m_queries[index] = SQLResultPair(element, result);
+ return true;
+}
+
+bool SQLQueryHolder::SetPreparedQuery(size_t index, PreparedStatement* stmt)
+{
+ if (m_queries.size() <= index)
+ {
+ TC_LOG_ERROR("sql.sql", "Query index (%u) out of range (size: %u) for prepared statement", uint32(index), (uint32)m_queries.size());
+ return false;
+ }
+
+ /// not executed yet, just stored (it's not called a holder for nothing)
+ SQLElementData element;
+ element.type = SQL_ELEMENT_PREPARED;
+ element.element.stmt = stmt;
+
+ SQLResultSetUnion result;
+ result.presult = NULL;
+
+ m_queries[index] = SQLResultPair(element, result);
+ return true;
+}
+
+QueryResult SQLQueryHolder::GetResult(size_t index)
+{
+ // Don't call to this function if the index is of an ad-hoc statement
+ if (index < m_queries.size())
+ {
+ ResultSet* result = m_queries[index].second.qresult;
+ if (!result || !result->GetRowCount() || !result->NextRow())
+ return QueryResult(NULL);
+
+ return QueryResult(result);
+ }
+ else
+ return QueryResult(NULL);
+}
+
+PreparedQueryResult SQLQueryHolder::GetPreparedResult(size_t index)
+{
+ // Don't call to this function if the index is of a prepared statement
+ if (index < m_queries.size())
+ {
+ PreparedResultSet* result = m_queries[index].second.presult;
+ if (!result || !result->GetRowCount())
+ return PreparedQueryResult(NULL);
+
+ return PreparedQueryResult(result);
+ }
+ else
+ return PreparedQueryResult(NULL);
+}
+
+void SQLQueryHolder::SetResult(size_t index, ResultSet* result)
+{
+ if (result && !result->GetRowCount())
+ {
+ delete result;
+ result = NULL;
+ }
+
+ /// store the result in the holder
+ if (index < m_queries.size())
+ m_queries[index].second.qresult = result;
+}
+
+void SQLQueryHolder::SetPreparedResult(size_t index, PreparedResultSet* result)
+{
+ if (result && !result->GetRowCount())
+ {
+ delete result;
+ result = NULL;
+ }
+
+ /// store the result in the holder
+ if (index < m_queries.size())
+ m_queries[index].second.presult = result;
+}
+
+SQLQueryHolder::~SQLQueryHolder()
+{
+ for (size_t i = 0; i < m_queries.size(); i++)
+ {
+ /// if the result was never used, free the resources
+ /// results used already (getresult called) are expected to be deleted
+ if (SQLElementData* data = &m_queries[i].first)
+ {
+ switch (data->type)
+ {
+ case SQL_ELEMENT_RAW:
+ free((void*)(const_cast<char*>(data->element.query)));
+ break;
+ case SQL_ELEMENT_PREPARED:
+ delete data->element.stmt;
+ break;
+ }
+ }
+ }
+}
+
+void SQLQueryHolder::SetSize(size_t size)
+{
+ /// to optimize push_back, reserve the number of queries about to be executed
+ m_queries.resize(size);
+}
+
+SQLQueryHolderTask::~SQLQueryHolderTask()
+{
+ if (!m_executed)
+ delete m_holder;
+}
+
+bool SQLQueryHolderTask::Execute()
+{
+ m_executed = true;
+
+ if (!m_holder)
+ return false;
+
+ /// we can do this, we are friends
+ std::vector<SQLQueryHolder::SQLResultPair> &queries = m_holder->m_queries;
+
+ for (size_t i = 0; i < queries.size(); i++)
+ {
+ /// execute all queries in the holder and pass the results
+ if (SQLElementData* data = &queries[i].first)
+ {
+ switch (data->type)
+ {
+ case SQL_ELEMENT_RAW:
+ {
+ char const* sql = data->element.query;
+ if (sql)
+ m_holder->SetResult(i, m_conn->Query(sql));
+ break;
+ }
+ case SQL_ELEMENT_PREPARED:
+ {
+ PreparedStatement* stmt = data->element.stmt;
+ if (stmt)
+ m_holder->SetPreparedResult(i, m_conn->Query(stmt));
+ break;
+ }
+ }
+ }
+ }
+
+ m_result.set_value(m_holder);
+ return true;
+}
diff --git a/src/server/database/Database/QueryHolder.h b/src/server/database/Database/QueryHolder.h
new file mode 100644
index 00000000000..b64da948a16
--- /dev/null
+++ b/src/server/database/Database/QueryHolder.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008-2015 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 _QUERYHOLDER_H
+#define _QUERYHOLDER_H
+
+#include <future>
+
+class SQLQueryHolder
+{
+ friend class SQLQueryHolderTask;
+ private:
+ typedef std::pair<SQLElementData, SQLResultSetUnion> SQLResultPair;
+ std::vector<SQLResultPair> m_queries;
+ public:
+ SQLQueryHolder() { }
+ ~SQLQueryHolder();
+ bool SetQuery(size_t index, const char* sql);
+ template<typename Format, typename... Args>
+ bool SetPQuery(size_t index, Format&& sql, Args&&... args)
+ {
+ return SetQuery(index, Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
+ }
+ bool SetPreparedQuery(size_t index, PreparedStatement* stmt);
+ void SetSize(size_t size);
+ QueryResult GetResult(size_t index);
+ PreparedQueryResult GetPreparedResult(size_t index);
+ void SetResult(size_t index, ResultSet* result);
+ void SetPreparedResult(size_t index, PreparedResultSet* result);
+};
+
+typedef std::future<SQLQueryHolder*> QueryResultHolderFuture;
+typedef std::promise<SQLQueryHolder*> QueryResultHolderPromise;
+
+class SQLQueryHolderTask : public SQLOperation
+{
+ private:
+ SQLQueryHolder* m_holder;
+ QueryResultHolderPromise m_result;
+ bool m_executed;
+
+ public:
+ SQLQueryHolderTask(SQLQueryHolder* holder)
+ : m_holder(holder), m_executed(false) { }
+
+ ~SQLQueryHolderTask();
+
+ bool Execute() override;
+ QueryResultHolderFuture GetFuture() { return m_result.get_future(); }
+};
+
+#endif
diff --git a/src/server/database/Database/QueryResult.cpp b/src/server/database/Database/QueryResult.cpp
new file mode 100644
index 00000000000..02352f221a0
--- /dev/null
+++ b/src/server/database/Database/QueryResult.cpp
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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"
+#include "Log.h"
+
+ResultSet::ResultSet(MYSQL_RES *result, MYSQL_FIELD *fields, uint64 rowCount, uint32 fieldCount) :
+_rowCount(rowCount),
+_fieldCount(fieldCount),
+_result(result),
+_fields(fields)
+{
+ _currentRow = new Field[_fieldCount];
+ ASSERT(_currentRow);
+}
+
+PreparedResultSet::PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES *result, uint64 rowCount, uint32 fieldCount) :
+m_rowCount(rowCount),
+m_rowPosition(0),
+m_fieldCount(fieldCount),
+m_rBind(NULL),
+m_stmt(stmt),
+m_res(result),
+m_isNull(NULL),
+m_length(NULL)
+{
+ if (!m_res)
+ return;
+
+ if (m_stmt->bind_result_done)
+ {
+ delete[] m_stmt->bind->length;
+ delete[] m_stmt->bind->is_null;
+ }
+
+ m_rBind = new MYSQL_BIND[m_fieldCount];
+ m_isNull = new my_bool[m_fieldCount];
+ m_length = new unsigned long[m_fieldCount];
+
+ memset(m_isNull, 0, sizeof(my_bool) * m_fieldCount);
+ memset(m_rBind, 0, sizeof(MYSQL_BIND) * m_fieldCount);
+ memset(m_length, 0, sizeof(unsigned long) * m_fieldCount);
+
+ //- This is where we store the (entire) resultset
+ if (mysql_stmt_store_result(m_stmt))
+ {
+ TC_LOG_WARN("sql.sql", "%s:mysql_stmt_store_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt));
+ delete[] m_rBind;
+ delete[] m_isNull;
+ delete[] m_length;
+ return;
+ }
+
+ //- This is where we prepare the buffer based on metadata
+ uint32 i = 0;
+ MYSQL_FIELD* field = mysql_fetch_field(m_res);
+ while (field)
+ {
+ size_t size = Field::SizeForType(field);
+
+ m_rBind[i].buffer_type = field->type;
+ m_rBind[i].buffer = malloc(size);
+ memset(m_rBind[i].buffer, 0, size);
+ m_rBind[i].buffer_length = size;
+ m_rBind[i].length = &m_length[i];
+ m_rBind[i].is_null = &m_isNull[i];
+ m_rBind[i].error = NULL;
+ m_rBind[i].is_unsigned = field->flags & UNSIGNED_FLAG;
+
+ ++i;
+ field = mysql_fetch_field(m_res);
+ }
+
+ //- This is where we bind the bind the buffer to the statement
+ if (mysql_stmt_bind_result(m_stmt, m_rBind))
+ {
+ TC_LOG_WARN("sql.sql", "%s:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt));
+ delete[] m_rBind;
+ delete[] m_isNull;
+ delete[] m_length;
+ return;
+ }
+
+ m_rowCount = mysql_stmt_num_rows(m_stmt);
+
+ m_rows.resize(uint32(m_rowCount));
+ while (_NextRow())
+ {
+ m_rows[uint32(m_rowPosition)] = new Field[m_fieldCount];
+ for (uint64 fIndex = 0; fIndex < m_fieldCount; ++fIndex)
+ {
+ if (!*m_rBind[fIndex].is_null)
+ m_rows[uint32(m_rowPosition)][fIndex].SetByteValue(m_rBind[fIndex].buffer,
+ m_rBind[fIndex].buffer_length,
+ m_rBind[fIndex].buffer_type,
+ *m_rBind[fIndex].length);
+ else
+ switch (m_rBind[fIndex].buffer_type)
+ {
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_VAR_STRING:
+ m_rows[uint32(m_rowPosition)][fIndex].SetByteValue("",
+ m_rBind[fIndex].buffer_length,
+ m_rBind[fIndex].buffer_type,
+ *m_rBind[fIndex].length);
+ break;
+ default:
+ m_rows[uint32(m_rowPosition)][fIndex].SetByteValue(nullptr,
+ m_rBind[fIndex].buffer_length,
+ m_rBind[fIndex].buffer_type,
+ *m_rBind[fIndex].length);
+ }
+ }
+ m_rowPosition++;
+ }
+ m_rowPosition = 0;
+
+ /// All data is buffered, let go of mysql c api structures
+ CleanUp();
+}
+
+ResultSet::~ResultSet()
+{
+ CleanUp();
+}
+
+PreparedResultSet::~PreparedResultSet()
+{
+ for (uint32 i = 0; i < uint32(m_rowCount); ++i)
+ delete[] m_rows[i];
+}
+
+bool ResultSet::NextRow()
+{
+ MYSQL_ROW row;
+
+ if (!_result)
+ return false;
+
+ row = mysql_fetch_row(_result);
+ if (!row)
+ {
+ CleanUp();
+ return false;
+ }
+
+ for (uint32 i = 0; i < _fieldCount; i++)
+ _currentRow[i].SetStructuredValue(row[i], _fields[i].type);
+
+ return true;
+}
+
+bool PreparedResultSet::NextRow()
+{
+ /// Only updates the m_rowPosition so upper level code knows in which element
+ /// of the rows vector to look
+ if (++m_rowPosition >= m_rowCount)
+ return false;
+
+ return true;
+}
+
+bool PreparedResultSet::_NextRow()
+{
+ /// Only called in low-level code, namely the constructor
+ /// Will iterate over every row of data and buffer it
+ if (m_rowPosition >= m_rowCount)
+ return false;
+
+ int retval = mysql_stmt_fetch(m_stmt);
+ return retval == 0 || retval == MYSQL_DATA_TRUNCATED;
+}
+
+void ResultSet::CleanUp()
+{
+ if (_currentRow)
+ {
+ delete [] _currentRow;
+ _currentRow = NULL;
+ }
+
+ if (_result)
+ {
+ mysql_free_result(_result);
+ _result = NULL;
+ }
+}
+
+void PreparedResultSet::CleanUp()
+{
+ /// More of the in our code allocated sources are deallocated by the poorly documented mysql c api
+ if (m_res)
+ mysql_free_result(m_res);
+
+ FreeBindBuffer();
+ mysql_stmt_free_result(m_stmt);
+
+ delete[] m_rBind;
+}
+
+void PreparedResultSet::FreeBindBuffer()
+{
+ for (uint32 i = 0; i < m_fieldCount; ++i)
+ free (m_rBind[i].buffer);
+}
diff --git a/src/server/database/Database/QueryResult.h b/src/server/database/Database/QueryResult.h
new file mode 100644
index 00000000000..a61fb6331c1
--- /dev/null
+++ b/src/server/database/Database/QueryResult.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
+ *
+ * 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 QUERYRESULT_H
+#define QUERYRESULT_H
+
+#include <memory>
+#include "Field.h"
+
+#ifdef _WIN32
+ #include <winsock2.h>
+#endif
+#include <mysql.h>
+
+class ResultSet
+{
+ public:
+ ResultSet(MYSQL_RES* result, MYSQL_FIELD* fields, uint64 rowCount, uint32 fieldCount);
+ ~ResultSet();
+
+ bool NextRow();
+ uint64 GetRowCount() const { return _rowCount; }
+ uint32 GetFieldCount() const { return _fieldCount; }
+
+ Field* Fetch() const { return _currentRow; }
+ const Field & operator [] (uint32 index) const
+ {
+ ASSERT(index < _fieldCount);
+ return _currentRow[index];
+ }
+
+ protected:
+ uint64 _rowCount;
+ Field* _currentRow;
+ uint32 _fieldCount;
+
+ private:
+ void CleanUp();
+ MYSQL_RES* _result;
+ MYSQL_FIELD* _fields;
+
+ ResultSet(ResultSet const& right) = delete;
+ ResultSet& operator=(ResultSet const& right) = delete;
+};
+
+typedef std::shared_ptr<ResultSet> QueryResult;
+
+class PreparedResultSet
+{
+ public:
+ PreparedResultSet(MYSQL_STMT* stmt, MYSQL_RES* result, uint64 rowCount, uint32 fieldCount);
+ ~PreparedResultSet();
+
+ bool NextRow();
+ uint64 GetRowCount() const { return m_rowCount; }
+ uint32 GetFieldCount() const { return m_fieldCount; }
+
+ Field* Fetch() const
+ {
+ ASSERT(m_rowPosition < m_rowCount);
+ return m_rows[uint32(m_rowPosition)];
+ }
+
+ const Field & operator [] (uint32 index) const
+ {
+ ASSERT(m_rowPosition < m_rowCount);
+ ASSERT(index < m_fieldCount);
+ return m_rows[uint32(m_rowPosition)][index];
+ }
+
+ protected:
+ std::vector<Field*> m_rows;
+ uint64 m_rowCount;
+ uint64 m_rowPosition;
+ uint32 m_fieldCount;
+
+ private:
+ MYSQL_BIND* m_rBind;
+ MYSQL_STMT* m_stmt;
+ MYSQL_RES* m_res;
+
+ my_bool* m_isNull;
+ unsigned long* m_length;
+
+ void FreeBindBuffer();
+ void CleanUp();
+ bool _NextRow();
+
+ PreparedResultSet(PreparedResultSet const& right) = delete;
+ PreparedResultSet& operator=(PreparedResultSet const& right) = delete;
+};
+
+typedef std::shared_ptr<PreparedResultSet> PreparedQueryResult;
+
+#endif
+
diff --git a/src/server/database/Database/SQLOperation.h b/src/server/database/Database/SQLOperation.h
new file mode 100644
index 00000000000..c5667288ec0
--- /dev/null
+++ b/src/server/database/Database/SQLOperation.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008-2015 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 _SQLOPERATION_H
+#define _SQLOPERATION_H
+
+#include "QueryResult.h"
+
+//- Forward declare (don't include header to prevent circular includes)
+class PreparedStatement;
+
+//- Union that holds element data
+union SQLElementUnion
+{
+ PreparedStatement* stmt;
+ const char* query;
+};
+
+//- Type specifier of our element data
+enum SQLElementDataType
+{
+ SQL_ELEMENT_RAW,
+ SQL_ELEMENT_PREPARED
+};
+
+//- The element
+struct SQLElementData
+{
+ SQLElementUnion element;
+ SQLElementDataType type;
+};
+
+//- For ambigious resultsets
+union SQLResultSetUnion
+{
+ PreparedResultSet* presult;
+ ResultSet* qresult;
+};
+
+class MySQLConnection;
+
+class SQLOperation
+{
+ public:
+ SQLOperation(): m_conn(NULL) { }
+ virtual ~SQLOperation() { }
+
+ virtual int call()
+ {
+ Execute();
+ return 0;
+ }
+ virtual bool Execute() = 0;
+ virtual void SetConnection(MySQLConnection* con) { m_conn = con; }
+
+ MySQLConnection* m_conn;
+
+ private:
+ SQLOperation(SQLOperation const& right) = delete;
+ SQLOperation& operator=(SQLOperation const& right) = delete;
+};
+
+#endif
diff --git a/src/server/database/Database/Transaction.cpp b/src/server/database/Database/Transaction.cpp
new file mode 100644
index 00000000000..f657411f716
--- /dev/null
+++ b/src/server/database/Database/Transaction.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008-2015 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"
+#include "Transaction.h"
+#include <mysqld_error.h>
+
+std::mutex TransactionTask::_deadlockLock;
+
+//- Append a raw ad-hoc query to the transaction
+void Transaction::Append(const char* sql)
+{
+ SQLElementData data;
+ data.type = SQL_ELEMENT_RAW;
+ data.element.query = strdup(sql);
+ m_queries.push_back(data);
+}
+
+//- Append a prepared statement to the transaction
+void Transaction::Append(PreparedStatement* stmt)
+{
+ SQLElementData data;
+ data.type = SQL_ELEMENT_PREPARED;
+ data.element.stmt = stmt;
+ m_queries.push_back(data);
+}
+
+void Transaction::Cleanup()
+{
+ // This might be called by explicit calls to Cleanup or by the auto-destructor
+ if (_cleanedUp)
+ return;
+
+ while (!m_queries.empty())
+ {
+ SQLElementData const &data = m_queries.front();
+ switch (data.type)
+ {
+ case SQL_ELEMENT_PREPARED:
+ delete data.element.stmt;
+ break;
+ case SQL_ELEMENT_RAW:
+ free((void*)(data.element.query));
+ break;
+ }
+
+ m_queries.pop_front();
+ }
+
+ _cleanedUp = true;
+}
+
+bool TransactionTask::Execute()
+{
+ int errorCode = m_conn->ExecuteTransaction(m_trans);
+ if (!errorCode)
+ return true;
+
+ if (errorCode == ER_LOCK_DEADLOCK)
+ {
+ // Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other
+ std::lock_guard<std::mutex> lock(_deadlockLock);
+ uint8 loopBreaker = 5; // Handle MySQL Errno 1213 without extending deadlock to the core itself
+ for (uint8 i = 0; i < loopBreaker; ++i)
+ if (!m_conn->ExecuteTransaction(m_trans))
+ return true;
+ }
+
+ // Clean up now.
+ m_trans->Cleanup();
+
+ return false;
+}
diff --git a/src/server/database/Database/Transaction.h b/src/server/database/Database/Transaction.h
new file mode 100644
index 00000000000..4fbbe1ed45b
--- /dev/null
+++ b/src/server/database/Database/Transaction.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008-2015 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 _TRANSACTION_H
+#define _TRANSACTION_H
+
+#include "SQLOperation.h"
+#include "StringFormat.h"
+
+//- Forward declare (don't include header to prevent circular includes)
+class PreparedStatement;
+
+/*! Transactions, high level class. */
+class Transaction
+{
+ friend class TransactionTask;
+ friend class MySQLConnection;
+
+ template <typename T>
+ friend class DatabaseWorkerPool;
+
+ public:
+ Transaction() : _cleanedUp(false) { }
+ ~Transaction() { Cleanup(); }
+
+ void Append(PreparedStatement* statement);
+ void Append(const char* sql);
+ template<typename Format, typename... Args>
+ void PAppend(Format&& sql, Args&&... args)
+ {
+ Append(Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str());
+ }
+
+ size_t GetSize() const { return m_queries.size(); }
+
+ protected:
+ void Cleanup();
+ std::list<SQLElementData> m_queries;
+
+ private:
+ bool _cleanedUp;
+
+};
+typedef std::shared_ptr<Transaction> SQLTransaction;
+
+/*! Low level class*/
+class TransactionTask : public SQLOperation
+{
+ template <class T> friend class DatabaseWorkerPool;
+ friend class DatabaseWorker;
+
+ public:
+ TransactionTask(SQLTransaction trans) : m_trans(trans) { }
+ ~TransactionTask() { }
+
+ protected:
+ bool Execute() override;
+
+ SQLTransaction m_trans;
+ static std::mutex _deadlockLock;
+};
+
+#endif
diff --git a/src/server/database/Logging/AppenderDB.cpp b/src/server/database/Logging/AppenderDB.cpp
new file mode 100644
index 00000000000..9e6ab1a057c
--- /dev/null
+++ b/src/server/database/Logging/AppenderDB.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008-2015 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 "AppenderDB.h"
+#include "Database/DatabaseEnv.h"
+
+AppenderDB::AppenderDB(uint8 id, std::string const& name, LogLevel level, AppenderFlags /*flags*/, ExtraAppenderArgs /*extraArgs*/)
+ : Appender(id, name, level), realmId(0), enabled(false) { }
+
+AppenderDB::~AppenderDB() { }
+
+void AppenderDB::_write(LogMessage const* message)
+{
+ // Avoid infinite loop, PExecute triggers Logging with "sql.sql" type
+ if (!enabled || (message->type.find("sql") != std::string::npos))
+ return;
+
+ PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_LOG);
+ stmt->setUInt64(0, message->mtime);
+ stmt->setUInt32(1, realmId);
+ stmt->setString(2, message->type);
+ stmt->setUInt8(3, uint8(message->level));
+ stmt->setString(4, message->text);
+ LoginDatabase.Execute(stmt);
+}
+
+void AppenderDB::setRealmId(uint32 _realmId)
+{
+ enabled = true;
+ realmId = _realmId;
+}
diff --git a/src/server/database/Logging/AppenderDB.h b/src/server/database/Logging/AppenderDB.h
new file mode 100644
index 00000000000..50607fd8136
--- /dev/null
+++ b/src/server/database/Logging/AppenderDB.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008-2015 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 APPENDERDB_H
+#define APPENDERDB_H
+
+#include "Appender.h"
+
+class AppenderDB: public Appender
+{
+ public:
+ typedef std::integral_constant<AppenderType, APPENDER_DB>::type TypeIndex;
+
+ AppenderDB(uint8 id, std::string const& name, LogLevel level, AppenderFlags flags, ExtraAppenderArgs extraArgs);
+ ~AppenderDB();
+
+ void setRealmId(uint32 realmId) override;
+ AppenderType getType() const override { return TypeIndex::value; }
+
+ private:
+ uint32 realmId;
+ bool enabled;
+ void _write(LogMessage const* message) override;
+};
+
+#endif
diff --git a/src/server/database/PrecompiledHeaders/databasePCH.cpp b/src/server/database/PrecompiledHeaders/databasePCH.cpp
new file mode 100644
index 00000000000..f84a52be82a
--- /dev/null
+++ b/src/server/database/PrecompiledHeaders/databasePCH.cpp
@@ -0,0 +1 @@
+#include "PrecompiledHeaders/databasePCH.h"
diff --git a/src/server/database/PrecompiledHeaders/databasePCH.h b/src/server/database/PrecompiledHeaders/databasePCH.h
new file mode 100644
index 00000000000..d524d52ade0
--- /dev/null
+++ b/src/server/database/PrecompiledHeaders/databasePCH.h
@@ -0,0 +1,23 @@
+#include "Config.h"
+#include "Database/AdhocStatement.h"
+#include "Database/DatabaseEnv.h"
+#include "Database/DatabaseLoader.h"
+#include "Database/DatabaseWorker.h"
+#include "Database/DatabaseWorkerPool.h"
+#include "Database/Field.h"
+#include "Database/MySQLConnection.h"
+#include "Database/MySQLThreading.h"
+#include "Database/PreparedStatement.h"
+#include "Database/QueryHolder.h"
+#include "Database/QueryResult.h"
+#include "Database/SQLOperation.h"
+#include "Database/Transaction.h"
+#include "Logging/Appender.h"
+#include "Logging/AppenderConsole.h"
+#include "Logging/AppenderDB.h"
+#include "Logging/AppenderFile.h"
+#include "Logging/Log.h"
+#include "Logging/LogOperation.h"
+#include "Logging/Logger.h"
+#include "Updater/DBUpdater.h"
+#include "Updater/UpdateFetcher.h"
diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp
new file mode 100644
index 00000000000..c69d19b11d6
--- /dev/null
+++ b/src/server/database/Updater/DBUpdater.cpp
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2008-2015 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 "DBUpdater.h"
+#include "Log.h"
+#include "Revision.h"
+#include "UpdateFetcher.h"
+#include "DatabaseLoader.h"
+#include "Config.h"
+
+#include <fstream>
+#include <iostream>
+#include <unordered_map>
+#include <boost/process.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;
+
+template<class T>
+std::string DBUpdater<T>::GetSourceDirectory()
+{
+ std::string const entry = sConfigMgr->GetStringDefault("Updates.SourcePath", "");
+ if (!entry.empty())
+ return entry;
+ else
+ return Revision::GetSourceDirectory();
+}
+
+template<class T>
+std::string DBUpdater<T>::GetMySqlCli()
+{
+ std::string const entry = sConfigMgr->GetStringDefault("Updates.MySqlCLIPath", "");
+ if (!entry.empty())
+ return entry;
+ else
+ return Revision::GetMySQLExecutable();
+}
+
+// Auth Database
+template<>
+std::string DBUpdater<LoginDatabaseConnection>::GetConfigEntry()
+{
+ return "Updates.Auth";
+}
+
+template<>
+std::string DBUpdater<LoginDatabaseConnection>::GetTableName()
+{
+ return "Auth";
+}
+
+template<>
+std::string DBUpdater<LoginDatabaseConnection>::GetBaseFile()
+{
+ return DBUpdater<LoginDatabaseConnection>::GetSourceDirectory() + "/sql/base/auth_database.sql";
+}
+
+template<>
+bool DBUpdater<LoginDatabaseConnection>::IsEnabled(uint32 const updateMask)
+{
+ // This way silences warnings under msvc
+ return (updateMask & DatabaseLoader::DATABASE_LOGIN) ? true : false;
+}
+
+// World Database
+template<>
+std::string DBUpdater<WorldDatabaseConnection>::GetConfigEntry()
+{
+ return "Updates.World";
+}
+
+template<>
+std::string DBUpdater<WorldDatabaseConnection>::GetTableName()
+{
+ return "World";
+}
+
+template<>
+std::string DBUpdater<WorldDatabaseConnection>::GetBaseFile()
+{
+ return Revision::GetFullDatabase();
+}
+
+template<>
+bool DBUpdater<WorldDatabaseConnection>::IsEnabled(uint32 const updateMask)
+{
+ // This way silences warnings under msvc
+ return (updateMask & DatabaseLoader::DATABASE_WORLD) ? true : false;
+}
+
+template<>
+BaseLocation DBUpdater<WorldDatabaseConnection>::GetBaseLocationType()
+{
+ return LOCATION_DOWNLOAD;
+}
+
+// Character Database
+template<>
+std::string DBUpdater<CharacterDatabaseConnection>::GetConfigEntry()
+{
+ return "Updates.Character";
+}
+
+template<>
+std::string DBUpdater<CharacterDatabaseConnection>::GetTableName()
+{
+ return "Character";
+}
+
+template<>
+std::string DBUpdater<CharacterDatabaseConnection>::GetBaseFile()
+{
+ return DBUpdater<CharacterDatabaseConnection>::GetSourceDirectory() + "/sql/base/characters_database.sql";
+}
+
+template<>
+bool DBUpdater<CharacterDatabaseConnection>::IsEnabled(uint32 const updateMask)
+{
+ // This way silences warnings under msvc
+ return (updateMask & DatabaseLoader::DATABASE_CHARACTER) ? true : false;
+}
+
+// All
+template<class T>
+BaseLocation DBUpdater<T>::GetBaseLocationType()
+{
+ return LOCATION_REPOSITORY;
+}
+
+template<class T>
+bool DBUpdater<T>::CheckExecutable()
+{
+ DBUpdater<T>::Path const exe(DBUpdater<T>::GetMySqlCli());
+ if (!exists(exe))
+ {
+ // Check for mysql in path
+ std::vector<std::string> args = {"--version"};
+ uint32 ret;
+ try
+ {
+ child c = execute(run_exe("mysql"), set_args(args), throw_on_error(), close_stdout());
+ ret = wait_for_exit(c);
+ }
+ catch (boost::system::system_error&)
+ {
+ ret = EXIT_FAILURE;
+ }
+
+ if (ret == EXIT_FAILURE)
+ {
+ TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\', correct the path in the *.conf (\"Updates.MySqlCLIPath\").",
+ absolute(exe).generic_string().c_str());
+
+ return false;
+ }
+ }
+ return true;
+}
+
+template<class T>
+bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
+{
+ TC_LOG_INFO("sql.updates", "Database \"%s\" does not exist, do you want to create it? [yes (default) / no]: ",
+ pool.GetConnectionInfo()->database.c_str());
+
+ std::string answer;
+ std::getline(std::cin, answer);
+ if (!answer.empty() && !(answer.substr(0, 1) == "y"))
+ return false;
+
+ TC_LOG_INFO("sql.updates", "Creating database \"%s\"...", pool.GetConnectionInfo()->database.c_str());
+
+ // Path of temp file
+ static Path const temp("create_table.sql");
+
+ // Create temporary query to use external mysql cli
+ std::ofstream file(temp.generic_string());
+ if (!file.is_open())
+ {
+ TC_LOG_FATAL("sql.updates", "Failed to create temporary query file \"%s\"!", temp.generic_string().c_str());
+ return false;
+ }
+
+ file << "CREATE DATABASE `" << pool.GetConnectionInfo()->database << "` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci\n\n";
+
+ file.close();
+
+ try
+ {
+ DBUpdater<T>::ApplyFile(pool, pool.GetConnectionInfo()->host, pool.GetConnectionInfo()->user, pool.GetConnectionInfo()->password,
+ pool.GetConnectionInfo()->port_or_socket, "", temp);
+ }
+ catch (UpdateException&)
+ {
+ TC_LOG_FATAL("sql.updates", "Failed to create database %s! Has the user `CREATE` priviliges?", pool.GetConnectionInfo()->database.c_str());
+ boost::filesystem::remove(temp);
+ return false;
+ }
+
+ TC_LOG_INFO("sql.updates", "Done.");
+ boost::filesystem::remove(temp);
+ return true;
+}
+
+template<class T>
+bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool)
+{
+ if (!DBUpdater<T>::CheckExecutable())
+ return false;
+
+ TC_LOG_INFO("sql.updates", "Updating %s database...", DBUpdater<T>::GetTableName().c_str());
+
+ Path const sourceDirectory(GetSourceDirectory());
+
+ if (!is_directory(sourceDirectory))
+ {
+ TC_LOG_ERROR("sql.updates", "DBUpdater: Given source directory %s does not exist, skipped!", sourceDirectory.generic_string().c_str());
+ return false;
+ }
+
+ UpdateFetcher updateFetcher(sourceDirectory, [&](std::string const& query) { DBUpdater<T>::Apply(pool, query); },
+ [&](Path const& file) { DBUpdater<T>::ApplyFile(pool, file); },
+ [&](std::string const& query) -> QueryResult { return DBUpdater<T>::Retrieve(pool, query); });
+
+ UpdateResult result;
+ try
+ {
+ result = updateFetcher.Update(
+ sConfigMgr->GetBoolDefault("Updates.Redundancy", true),
+ sConfigMgr->GetBoolDefault("Updates.AllowRehash", true),
+ sConfigMgr->GetBoolDefault("Updates.ArchivedRedundancy", false),
+ sConfigMgr->GetIntDefault("Updates.CleanDeadRefMaxCount", 3));
+ }
+ catch (UpdateException&)
+ {
+ return false;
+ }
+
+ std::string const info = Trinity::StringFormat("Containing " SZFMTD " new and " SZFMTD " archived updates.",
+ result.recent, result.archived);
+
+ if (!result.updated)
+ TC_LOG_INFO("sql.updates", ">> %s database is up-to-date! %s", DBUpdater<T>::GetTableName().c_str(), info.c_str());
+ else
+ TC_LOG_INFO("sql.updates", ">> Applied " SZFMTD " %s. %s", result.updated, result.updated == 1 ? "query" : "queries", info.c_str());
+
+ return true;
+}
+
+template<class T>
+bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
+{
+ {
+ QueryResult const result = Retrieve(pool, "SHOW TABLES");
+ if (result && (result->GetRowCount() > 0))
+ return true;
+ }
+
+ if (!DBUpdater<T>::CheckExecutable())
+ return false;
+
+ TC_LOG_INFO("sql.updates", "Database %s is empty, auto populating it...", DBUpdater<T>::GetTableName().c_str());
+
+ std::string const p = DBUpdater<T>::GetBaseFile();
+ if (p.empty())
+ {
+ TC_LOG_INFO("sql.updates", ">> No base file provided, skipped!");
+ return true;
+ }
+
+ Path const base(p);
+ if (!exists(base))
+ {
+ switch (DBUpdater<T>::GetBaseLocationType())
+ {
+ case LOCATION_REPOSITORY:
+ {
+ TC_LOG_ERROR("sql.updates", ">> Base file \"%s\" is missing, try to clone the source again.",
+ base.generic_string().c_str());
+
+ break;
+ }
+ case LOCATION_DOWNLOAD:
+ {
+ TC_LOG_ERROR("sql.updates", ">> File \"%s\" is missing, download it from \"http://www.trinitycore.org/f/files/category/1-database/\"" \
+ " and place it in your server directory.", base.filename().generic_string().c_str());
+ break;
+ }
+ }
+ return false;
+ }
+
+ // Update database
+ TC_LOG_INFO("sql.updates", ">> Applying \'%s\'...", base.generic_string().c_str());
+ try
+ {
+ ApplyFile(pool, base);
+ }
+ catch (UpdateException&)
+ {
+ return false;
+ }
+
+ TC_LOG_INFO("sql.updates", ">> Done!");
+ return true;
+}
+
+template<class T>
+QueryResult DBUpdater<T>::Retrieve(DatabaseWorkerPool<T>& pool, std::string const& query)
+{
+ return pool.PQuery(query.c_str());
+}
+
+template<class T>
+void DBUpdater<T>::Apply(DatabaseWorkerPool<T>& pool, std::string const& query)
+{
+ pool.DirectExecute(query.c_str());
+}
+
+template<class T>
+void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, Path const& path)
+{
+ DBUpdater<T>::ApplyFile(pool, pool.GetConnectionInfo()->host, pool.GetConnectionInfo()->user, pool.GetConnectionInfo()->password,
+ pool.GetConnectionInfo()->port_or_socket, pool.GetConnectionInfo()->database, path);
+}
+
+template<class T>
+void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& host, std::string const& user,
+ std::string const& password, std::string const& port_or_socket, std::string const& database, Path const& path)
+{
+ std::vector<std::string> args;
+ args.reserve(7);
+
+ // CLI Client connection info
+ args.push_back("-h" + host);
+ args.push_back("-u" + user);
+
+ if (!password.empty())
+ args.push_back("-p" + password);
+
+ // Check if we want to connect through ip or socket (Unix only)
+#ifdef _WIN32
+
+ args.push_back("-P" + port_or_socket);
+
+#else
+
+ if (!std::isdigit(port_or_socket[0]))
+ {
+ // We can't check here if host == "." because is named localhost if socket option is enabled
+ args.push_back("-P0");
+ args.push_back("--protocol=SOCKET");
+ args.push_back("-S" + port_or_socket);
+ }
+ else
+ // generic case
+ args.push_back("-P" + port_or_socket);
+
+#endif
+
+ // Set the default charset to utf8
+ args.push_back("--default-character-set=utf8");
+
+ // Set max allowed packet to 1 GB
+ args.push_back("--max-allowed-packet=1GB");
+
+ // Database
+ 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
+ {
+ child c = execute(run_exe(DBUpdater<T>::GetMySqlCli().empty() ? "mysql" :
+ boost::filesystem::absolute(DBUpdater<T>::GetMySqlCli()).generic_string()),
+ set_args(args), bind_stdin(source), throw_on_error());
+
+ ret = wait_for_exit(c);
+ }
+ catch (boost::system::system_error&)
+ {
+ ret = EXIT_FAILURE;
+ }
+
+ source.close();
+
+ 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.",
+ 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>;
diff --git a/src/server/database/Updater/DBUpdater.h b/src/server/database/Updater/DBUpdater.h
new file mode 100644
index 00000000000..a2b12bed235
--- /dev/null
+++ b/src/server/database/Updater/DBUpdater.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008-2015 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 DBUpdater_h__
+#define DBUpdater_h__
+
+#include "DatabaseEnv.h"
+
+#include <string>
+#include <boost/filesystem.hpp>
+
+class UpdateException : public std::exception
+{
+public:
+ UpdateException(std::string const& msg) : _msg(msg) { }
+ ~UpdateException() throw() { }
+
+ char const* what() const throw() override { return _msg.c_str(); }
+
+private:
+ std::string const _msg;
+};
+
+enum BaseLocation
+{
+ LOCATION_REPOSITORY,
+ LOCATION_DOWNLOAD
+};
+
+struct UpdateResult
+{
+ UpdateResult()
+ : updated(0), recent(0), archived(0) { }
+
+ UpdateResult(size_t const updated_, size_t const recent_, size_t const archived_)
+ : updated(updated_), recent(recent_), archived(archived_) { }
+
+ size_t updated;
+ size_t recent;
+ size_t archived;
+};
+
+template <class T>
+class DBUpdater
+{
+public:
+ using Path = boost::filesystem::path;
+
+ static std::string GetSourceDirectory();
+
+ static inline std::string GetConfigEntry();
+
+ static inline std::string GetTableName();
+
+ static std::string GetBaseFile();
+
+ static bool IsEnabled(uint32 const updateMask);
+
+ static BaseLocation GetBaseLocationType();
+
+ static bool Create(DatabaseWorkerPool<T>& pool);
+
+ static bool Update(DatabaseWorkerPool<T>& pool);
+
+ static bool Populate(DatabaseWorkerPool<T>& pool);
+
+private:
+ static std::string GetMySqlCli();
+ static bool CheckExecutable();
+
+ static QueryResult Retrieve(DatabaseWorkerPool<T>& pool, std::string const& query);
+ static void Apply(DatabaseWorkerPool<T>& pool, std::string const& query);
+ static void ApplyFile(DatabaseWorkerPool<T>& pool, Path const& path);
+ static void ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& host, std::string const& user,
+ std::string const& password, std::string const& port_or_socket, std::string const& database, Path const& path);
+};
+
+#endif // DBUpdater_h__
diff --git a/src/server/database/Updater/UpdateFetcher.cpp b/src/server/database/Updater/UpdateFetcher.cpp
new file mode 100644
index 00000000000..ec023928b99
--- /dev/null
+++ b/src/server/database/Updater/UpdateFetcher.cpp
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2008-2015 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 "UpdateFetcher.h"
+#include "Log.h"
+#include "Util.h"
+
+#include <fstream>
+#include <chrono>
+#include <vector>
+#include <sstream>
+#include <exception>
+#include <unordered_map>
+#include <openssl/sha.h>
+
+using namespace boost::filesystem;
+
+UpdateFetcher::UpdateFetcher(Path const& sourceDirectory,
+ std::function<void(std::string const&)> const& apply,
+ std::function<void(Path const& path)> const& applyFile,
+ std::function<QueryResult(std::string const&)> const& retrieve) :
+ _sourceDirectory(sourceDirectory), _apply(apply), _applyFile(applyFile),
+ _retrieve(retrieve)
+{
+}
+
+UpdateFetcher::LocaleFileStorage UpdateFetcher::GetFileList() const
+{
+ LocaleFileStorage files;
+ DirectoryStorage directories = ReceiveIncludedDirectories();
+ for (auto const& entry : directories)
+ FillFileListRecursively(entry.path, files, entry.state, 1);
+
+ return files;
+}
+
+void UpdateFetcher::FillFileListRecursively(Path const& path, LocaleFileStorage& storage, State const state, uint32 const depth) const
+{
+ static uint32 const MAX_DEPTH = 10;
+ static directory_iterator const end;
+
+ for (directory_iterator itr(path); itr != end; ++itr)
+ {
+ if (is_directory(itr->path()))
+ {
+ if (depth < MAX_DEPTH)
+ FillFileListRecursively(itr->path(), storage, state, depth + 1);
+ }
+ else if (itr->path().extension() == ".sql")
+ {
+ TC_LOG_TRACE("sql.updates", "Added locale file \"%s\".", itr->path().filename().generic_string().c_str());
+
+ LocaleFileEntry const entry = { itr->path(), state };
+
+ // Check for doubled filenames
+ // Since elements are only compared through their filenames this is ok
+ if (storage.find(entry) != storage.end())
+ {
+ TC_LOG_FATAL("sql.updates", "Duplicated filename occurred \"%s\", since updates are ordered " \
+ "through its filename every name needs to be unique!", itr->path().generic_string().c_str());
+
+ throw UpdateException("Updating failed, see the log for details.");
+ }
+
+ storage.insert(entry);
+ }
+ }
+}
+
+UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories() const
+{
+ DirectoryStorage directories;
+
+ QueryResult const result = _retrieve("SELECT `path`, `state` FROM `updates_include`");
+ if (!result)
+ return directories;
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ std::string path = fields[0].GetString();
+ if (path.substr(0, 1) == "$")
+ path = _sourceDirectory.generic_string() + path.substr(1);
+
+ Path const p(path);
+
+ if (!is_directory(p))
+ {
+ TC_LOG_WARN("sql.updates", "DBUpdater: Given update include directory \"%s\" isn't existing, skipped!", p.generic_string().c_str());
+ continue;
+ }
+
+ DirectoryEntry const entry = { p, AppliedFileEntry::StateConvert(fields[1].GetString()) };
+ directories.push_back(entry);
+
+ TC_LOG_TRACE("sql.updates", "Added applied file \"%s\" from remote.", p.filename().generic_string().c_str());
+
+ } while (result->NextRow());
+
+ return directories;
+}
+
+UpdateFetcher::AppliedFileStorage UpdateFetcher::ReceiveAppliedFiles() const
+{
+ AppliedFileStorage map;
+
+ QueryResult result = _retrieve("SELECT `name`, `hash`, `state`, UNIX_TIMESTAMP(`timestamp`) FROM `updates` ORDER BY `name` ASC");
+ if (!result)
+ return map;
+
+ do
+ {
+ Field* fields = result->Fetch();
+
+ AppliedFileEntry const entry = { fields[0].GetString(), fields[1].GetString(),
+ AppliedFileEntry::StateConvert(fields[2].GetString()), fields[3].GetUInt64() };
+
+ map.insert(std::make_pair(entry.name, entry));
+ }
+ while (result->NextRow());
+
+ return map;
+}
+
+UpdateFetcher::SQLUpdate UpdateFetcher::ReadSQLUpdate(boost::filesystem::path const& file) const
+{
+ std::ifstream in(file.c_str());
+ WPFatal(in.is_open(), "Could not read an update file.");
+
+ auto const start_pos = in.tellg();
+ in.ignore(std::numeric_limits<std::streamsize>::max());
+ auto const char_count = in.gcount();
+ in.seekg(start_pos);
+
+ SQLUpdate const update(new std::string(char_count, char{}));
+
+ 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
+{
+ LocaleFileStorage const available = GetFileList();
+ AppliedFileStorage applied = ReceiveAppliedFiles();
+
+ size_t countRecentUpdates = 0;
+ size_t countArchivedUpdates = 0;
+
+ // Count updates
+ for (auto const& entry : applied)
+ if (entry.second.state == RELEASED)
+ ++countRecentUpdates;
+ else
+ ++countArchivedUpdates;
+
+ // Fill hash to name cache
+ HashToFileNameStorage hashToName;
+ for (auto entry : applied)
+ hashToName.insert(std::make_pair(entry.second.hash, entry.first));
+
+ size_t importedUpdates = 0;
+
+ for (auto const& availableQuery : available)
+ {
+ TC_LOG_DEBUG("sql.updates", "Checking update \"%s\"...", availableQuery.first.filename().generic_string().c_str());
+
+ AppliedFileStorage::const_iterator iter = applied.find(availableQuery.first.filename().string());
+ if (iter != applied.end())
+ {
+ // If redundancy is disabled skip it since the update is already applied.
+ if (!redundancyChecks)
+ {
+ TC_LOG_DEBUG("sql.updates", ">> Update is already applied, skipping redundancy checks.");
+ applied.erase(iter);
+ continue;
+ }
+
+ // If the update is in an archived directory and is marked as archived in our database skip redundancy checks (archived updates never change).
+ if (!archivedRedundancy && (iter->second.state == ARCHIVED) && (availableQuery.second == ARCHIVED))
+ {
+ TC_LOG_DEBUG("sql.updates", ">> Update is archived and marked as archived in database, skipping redundancy checks.");
+ applied.erase(iter);
+ continue;
+ }
+ }
+
+ // Read update from file
+ SQLUpdate const update = ReadSQLUpdate(availableQuery.first);
+
+ // Calculate hash
+ std::string const hash = CalculateHash(update);
+
+ UpdateMode mode = MODE_APPLY;
+
+ // Update is not in our applied list
+ if (iter == applied.end())
+ {
+ // Catch renames (different filename but same hash)
+ HashToFileNameStorage::const_iterator const hashIter = hashToName.find(hash);
+ if (hashIter != hashToName.end())
+ {
+ // Check if the original file was removed if not we've got a problem.
+ LocaleFileStorage::const_iterator localeIter;
+ // Push localeIter forward
+ for (localeIter = available.begin(); (localeIter != available.end()) &&
+ (localeIter->first.filename().string() != hashIter->second); ++localeIter);
+
+ // Conflict!
+ if (localeIter != available.end())
+ {
+ TC_LOG_WARN("sql.updates", ">> Seems like update \"%s\" \'%s\' was renamed, but the old file is still there! " \
+ "Trade it as a new file! (Probably its an unmodified copy of file \"%s\")",
+ availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str(),
+ localeIter->first.filename().string().c_str());
+ }
+ // Its save to trade the file as renamed here
+ else
+ {
+ TC_LOG_INFO("sql.updates", ">> Renaming update \"%s\" to \"%s\" \'%s\'.",
+ hashIter->second.c_str(), availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str());
+
+ RenameEntry(hashIter->second, availableQuery.first.filename().string());
+ applied.erase(hashIter->second);
+ continue;
+ }
+ }
+ // Apply the update if it was never seen before.
+ else
+ {
+ TC_LOG_INFO("sql.updates", ">> Applying update \"%s\" \'%s\'...",
+ availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str());
+ }
+ }
+ // Rehash the update entry if it is contained in our database but with an empty hash.
+ else if (allowRehash && iter->second.hash.empty())
+ {
+ mode = MODE_REHASH;
+
+ TC_LOG_INFO("sql.updates", ">> Re-hashing update \"%s\" \'%s\'...", availableQuery.first.filename().string().c_str(),
+ hash.substr(0, 7).c_str());
+ }
+ else
+ {
+ // If the hash of the files differs from the one stored in our database reapply the update (because it was changed).
+ if (iter->second.hash != hash)
+ {
+ TC_LOG_INFO("sql.updates", ">> Reapplying update \"%s\" \'%s\' -> \'%s\' (it changed)...", availableQuery.first.filename().string().c_str(),
+ iter->second.hash.substr(0, 7).c_str(), hash.substr(0, 7).c_str());
+ }
+ else
+ {
+ // If the file wasn't changed and just moved update its state if necessary.
+ if (iter->second.state != availableQuery.second)
+ {
+ TC_LOG_DEBUG("sql.updates", ">> Updating state of \"%s\" to \'%s\'...",
+ availableQuery.first.filename().string().c_str(), AppliedFileEntry::StateConvert(availableQuery.second).c_str());
+
+ UpdateState(availableQuery.first.filename().string(), availableQuery.second);
+ }
+
+ TC_LOG_DEBUG("sql.updates", ">> Update is already applied and is matching hash \'%s\'.", hash.substr(0, 7).c_str());
+
+ applied.erase(iter);
+ continue;
+ }
+ }
+
+ uint32 speed = 0;
+ AppliedFileEntry const file = { availableQuery.first.filename().string(), hash, availableQuery.second, 0 };
+
+ switch (mode)
+ {
+ case MODE_APPLY:
+ speed = Apply(availableQuery.first);
+ /*no break*/
+ case MODE_REHASH:
+ UpdateEntry(file, speed);
+ break;
+ }
+
+ if (iter != applied.end())
+ applied.erase(iter);
+
+ if (mode == MODE_APPLY)
+ ++importedUpdates;
+ }
+
+ // Cleanup up orphaned entries if enabled
+ if (!applied.empty())
+ {
+ bool const doCleanup = (cleanDeadReferencesMaxCount < 0) || (applied.size() <= static_cast<size_t>(cleanDeadReferencesMaxCount));
+
+ for (auto const& entry : applied)
+ {
+ TC_LOG_WARN("sql.updates", ">> File \'%s\' was applied to the database but is missing in" \
+ " your update directory now!", entry.first.c_str());
+
+ if (doCleanup)
+ TC_LOG_INFO("sql.updates", "Deleting orphaned entry \'%s\'...", entry.first.c_str());
+ }
+
+ if (doCleanup)
+ CleanUp(applied);
+ else
+ {
+ TC_LOG_ERROR("sql.updates", "Cleanup is disabled! There are " SZFMTD " dirty files that were applied to your database " \
+ "but are now missing in your source directory!", applied.size());
+ }
+ }
+
+ 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;
+
+ // Benchmark query speed
+ auto const begin = Time::now();
+
+ // Update database
+ _applyFile(path);
+
+ // Return time the query took to apply
+ return std::chrono::duration_cast<std::chrono::milliseconds>(Time::now() - begin).count();
+}
+
+void UpdateFetcher::UpdateEntry(AppliedFileEntry const& entry, uint32 const speed) const
+{
+ std::string const update = "REPLACE INTO `updates` (`name`, `hash`, `state`, `speed`) VALUES (\"" +
+ entry.name + "\", \"" + entry.hash + "\", \'" + entry.GetStateAsString() + "\', " + std::to_string(speed) + ")";
+
+ // Update database
+ _apply(update);
+}
+
+void UpdateFetcher::RenameEntry(std::string const& from, std::string const& to) const
+{
+ // Delete target if it exists
+ {
+ std::string const update = "DELETE FROM `updates` WHERE `name`=\"" + to + "\"";
+
+ // Update database
+ _apply(update);
+ }
+
+ // Rename
+ {
+ std::string const update = "UPDATE `updates` SET `name`=\"" + to + "\" WHERE `name`=\"" + from + "\"";
+
+ // Update database
+ _apply(update);
+ }
+}
+
+void UpdateFetcher::CleanUp(AppliedFileStorage const& storage) const
+{
+ if (storage.empty())
+ return;
+
+ std::stringstream update;
+ size_t remaining = storage.size();
+
+ update << "DELETE FROM `updates` WHERE `name` IN(";
+
+ for (auto const& entry : storage)
+ {
+ update << "\"" << entry.first << "\"";
+ if ((--remaining) > 0)
+ update << ", ";
+ }
+
+ update << ")";
+
+ // Update database
+ _apply(update.str());
+}
+
+void UpdateFetcher::UpdateState(std::string const& name, State const state) const
+{
+ std::string const update = "UPDATE `updates` SET `state`=\'" + AppliedFileEntry::StateConvert(state) + "\' WHERE `name`=\"" + name + "\"";
+
+ // Update database
+ _apply(update);
+}
diff --git a/src/server/database/Updater/UpdateFetcher.h b/src/server/database/Updater/UpdateFetcher.h
new file mode 100644
index 00000000000..4ff8c93bc76
--- /dev/null
+++ b/src/server/database/Updater/UpdateFetcher.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2008-2015 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 UpdateFetcher_h__
+#define UpdateFetcher_h__
+
+#include <DBUpdater.h>
+
+#include <functional>
+#include <string>
+#include <memory>
+#include <vector>
+
+class UpdateFetcher
+{
+ typedef boost::filesystem::path Path;
+
+public:
+ UpdateFetcher(Path const& updateDirectory,
+ std::function<void(std::string const&)> const& apply,
+ std::function<void(Path const& path)> const& applyFile,
+ std::function<QueryResult(std::string const&)> const& retrieve);
+
+ UpdateResult Update(bool const redundancyChecks, bool const allowRehash,
+ bool const archivedRedundancy, int32 const cleanDeadReferencesMaxCount) const;
+
+private:
+ enum UpdateMode
+ {
+ MODE_APPLY,
+ MODE_REHASH
+ };
+
+ enum State
+ {
+ RELEASED,
+ ARCHIVED
+ };
+
+ struct AppliedFileEntry
+ {
+ AppliedFileEntry(std::string const& name_, std::string const& hash_, State state_, uint64 timestamp_)
+ : name(name_), hash(hash_), state(state_), timestamp(timestamp_) { }
+
+ std::string const name;
+
+ std::string const hash;
+
+ State const state;
+
+ uint64 const timestamp;
+
+ static inline State StateConvert(std::string const& state)
+ {
+ return (state == "RELEASED") ? RELEASED : ARCHIVED;
+ }
+
+ static inline std::string StateConvert(State const state)
+ {
+ return (state == RELEASED) ? "RELEASED" : "ARCHIVED";
+ }
+
+ std::string GetStateAsString() const
+ {
+ return StateConvert(state);
+ }
+ };
+
+ struct DirectoryEntry
+ {
+ DirectoryEntry(Path const& path_, State state_) : path(path_), state(state_) { }
+
+ Path const path;
+
+ State const state;
+ };
+
+ typedef std::pair<Path, State> LocaleFileEntry;
+
+ struct PathCompare
+ {
+ inline bool operator() (LocaleFileEntry const& left, LocaleFileEntry const& right) const
+ {
+ return left.first.filename().string() < right.first.filename().string();
+ }
+ };
+
+ typedef std::set<LocaleFileEntry, PathCompare> LocaleFileStorage;
+ typedef std::unordered_map<std::string, std::string> HashToFileNameStorage;
+ typedef std::unordered_map<std::string, AppliedFileEntry> AppliedFileStorage;
+ typedef std::vector<UpdateFetcher::DirectoryEntry> DirectoryStorage;
+ typedef std::shared_ptr<std::string> SQLUpdate;
+
+ LocaleFileStorage GetFileList() 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;
+
+ uint32 Apply(Path const& path) const;
+
+ void UpdateEntry(AppliedFileEntry const& entry, uint32 const speed = 0) const;
+ void RenameEntry(std::string const& from, std::string const& to) const;
+ void CleanUp(AppliedFileStorage const& storage) const;
+
+ void UpdateState(std::string const& name, State const state) const;
+
+ Path const _sourceDirectory;
+
+ std::function<void(std::string const&)> const _apply;
+ std::function<void(Path const& path)> const _applyFile;
+ std::function<QueryResult(std::string const&)> const _retrieve;
+};
+
+#endif // UpdateFetcher_h__