Initial commit
This commit is contained in:
56
src/server/database/Database/AdhocStatement.cpp
Normal file
56
src/server/database/Database/AdhocStatement.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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"
|
||||
#include "QueryResult.h"
|
||||
|
||||
/*! Basic, ad-hoc queries. */
|
||||
BasicStatementTask::BasicStatementTask(std::string_view sql, bool async) : m_result(nullptr)
|
||||
{
|
||||
m_sql = std::string(sql);
|
||||
m_has_result = async; // If the operation is async, then there's a result
|
||||
|
||||
if (async)
|
||||
m_result = new QueryResultPromise();
|
||||
}
|
||||
|
||||
BasicStatementTask::~BasicStatementTask()
|
||||
{
|
||||
m_sql.clear();
|
||||
if (m_has_result && m_result)
|
||||
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(nullptr));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_result->set_value(QueryResult(result));
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_conn->Execute(m_sql);
|
||||
}
|
||||
41
src/server/database/Database/AdhocStatement.h
Normal file
41
src/server/database/Database/AdhocStatement.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "DatabaseEnvFwd.h"
|
||||
#include "Define.h"
|
||||
#include "SQLOperation.h"
|
||||
|
||||
/*! Raw, ad-hoc query. */
|
||||
class AC_DATABASE_API BasicStatementTask : public SQLOperation
|
||||
{
|
||||
public:
|
||||
BasicStatementTask(std::string_view sql, bool async = false);
|
||||
~BasicStatementTask();
|
||||
|
||||
bool Execute() override;
|
||||
QueryResultFuture GetFuture() const { return m_result->get_future(); }
|
||||
|
||||
private:
|
||||
std::string m_sql; //- Raw query to be executed
|
||||
bool m_has_result;
|
||||
QueryResultPromise* m_result;
|
||||
};
|
||||
|
||||
#endif
|
||||
22
src/server/database/Database/DatabaseEnv.cpp
Normal file
22
src/server/database/Database/DatabaseEnv.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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"
|
||||
|
||||
DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabase;
|
||||
DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabase;
|
||||
DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabase;
|
||||
39
src/server/database/Database/DatabaseEnv.h
Normal file
39
src/server/database/Database/DatabaseEnv.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "DatabaseWorkerPool.h"
|
||||
#include "Define.h"
|
||||
|
||||
#include "Implementation/CharacterDatabase.h"
|
||||
#include "Implementation/LoginDatabase.h"
|
||||
#include "Implementation/WorldDatabase.h"
|
||||
|
||||
#include "PreparedStatement.h"
|
||||
#include "QueryCallback.h"
|
||||
#include "Transaction.h"
|
||||
|
||||
/// Accessor to the world database
|
||||
AC_DATABASE_API extern DatabaseWorkerPool<WorldDatabaseConnection> WorldDatabase;
|
||||
/// Accessor to the character database
|
||||
AC_DATABASE_API extern DatabaseWorkerPool<CharacterDatabaseConnection> CharacterDatabase;
|
||||
/// Accessor to the realm/login database
|
||||
AC_DATABASE_API extern DatabaseWorkerPool<LoginDatabaseConnection> LoginDatabase;
|
||||
|
||||
#endif
|
||||
93
src/server/database/Database/DatabaseEnvFwd.h
Normal file
93
src/server/database/Database/DatabaseEnvFwd.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 DatabaseEnvFwd_h__
|
||||
#define DatabaseEnvFwd_h__
|
||||
|
||||
#include <future>
|
||||
|
||||
struct QueryResultFieldMetadata;
|
||||
class Field;
|
||||
|
||||
class ResultSet;
|
||||
using QueryResult = std::shared_ptr<ResultSet>;
|
||||
using QueryResultFuture = std::future<QueryResult>;
|
||||
using QueryResultPromise = std::promise<QueryResult>;
|
||||
|
||||
class CharacterDatabaseConnection;
|
||||
class LoginDatabaseConnection;
|
||||
class WorldDatabaseConnection;
|
||||
|
||||
class PreparedStatementBase;
|
||||
|
||||
template<typename T>
|
||||
class PreparedStatement;
|
||||
|
||||
using CharacterDatabasePreparedStatement = PreparedStatement<CharacterDatabaseConnection>;
|
||||
using LoginDatabasePreparedStatement = PreparedStatement<LoginDatabaseConnection>;
|
||||
using WorldDatabasePreparedStatement = PreparedStatement<WorldDatabaseConnection>;
|
||||
|
||||
class PreparedResultSet;
|
||||
using PreparedQueryResult = std::shared_ptr<PreparedResultSet>;
|
||||
using PreparedQueryResultFuture = std::future<PreparedQueryResult>;
|
||||
using PreparedQueryResultPromise = std::promise<PreparedQueryResult>;
|
||||
|
||||
class QueryCallback;
|
||||
|
||||
template<typename T>
|
||||
class AsyncCallbackProcessor;
|
||||
|
||||
using QueryCallbackProcessor = AsyncCallbackProcessor<QueryCallback>;
|
||||
|
||||
class TransactionBase;
|
||||
|
||||
using TransactionFuture = std::future<bool>;
|
||||
using TransactionPromise = std::promise<bool>;
|
||||
|
||||
template<typename T>
|
||||
class Transaction;
|
||||
|
||||
class TransactionCallback;
|
||||
|
||||
template<typename T>
|
||||
using SQLTransaction = std::shared_ptr<Transaction<T>>;
|
||||
|
||||
using CharacterDatabaseTransaction = SQLTransaction<CharacterDatabaseConnection>;
|
||||
using LoginDatabaseTransaction = SQLTransaction<LoginDatabaseConnection>;
|
||||
using WorldDatabaseTransaction = SQLTransaction<WorldDatabaseConnection>;
|
||||
|
||||
class SQLQueryHolderBase;
|
||||
using QueryResultHolderFuture = std::future<void>;
|
||||
using QueryResultHolderPromise = std::promise<void>;
|
||||
|
||||
template<typename T>
|
||||
class SQLQueryHolder;
|
||||
|
||||
using CharacterDatabaseQueryHolder = SQLQueryHolder<CharacterDatabaseConnection>;
|
||||
using LoginDatabaseQueryHolder = SQLQueryHolder<LoginDatabaseConnection>;
|
||||
using WorldDatabaseQueryHolder = SQLQueryHolder<WorldDatabaseConnection>;
|
||||
|
||||
class SQLQueryHolderCallback;
|
||||
|
||||
// mysql
|
||||
struct MySQLHandle;
|
||||
struct MySQLResult;
|
||||
struct MySQLField;
|
||||
struct MySQLBind;
|
||||
struct MySQLStmt;
|
||||
|
||||
#endif // DatabaseEnvFwd_h__
|
||||
240
src/server/database/Database/DatabaseLoader.cpp
Normal file
240
src/server/database/Database/DatabaseLoader.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "Config.h"
|
||||
#include "DBUpdater.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "Duration.h"
|
||||
#include "Log.h"
|
||||
#include <errmsg.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <thread>
|
||||
#include <string_view>
|
||||
namespace
|
||||
{
|
||||
std::string const EMPTY_DATABASE_INFO;
|
||||
std::string const LOGIN_DATABASE_INFO_DEFAULT = "127.0.0.1;3306;acore;acore;acore_auth";
|
||||
std::string const WORLD_DATABASE_INFO_DEFAULT = "127.0.0.1;3306;acore;acore;acore_world";
|
||||
std::string const CHARACTER_DATABASE_INFO_DEFAULT = "127.0.0.1;3306;acore;acore;acore_characters";
|
||||
std::string const& GetDefaultDatabaseInfo(std::string_view name)
|
||||
{
|
||||
if (name == "Login")
|
||||
return LOGIN_DATABASE_INFO_DEFAULT;
|
||||
if (name == "World")
|
||||
return WORLD_DATABASE_INFO_DEFAULT;
|
||||
if (name == "Character")
|
||||
return CHARACTER_DATABASE_INFO_DEFAULT;
|
||||
return EMPTY_DATABASE_INFO;
|
||||
}
|
||||
}
|
||||
|
||||
DatabaseLoader::DatabaseLoader(std::string const& logger, uint32 const defaultUpdateMask, std::string_view modulesList)
|
||||
: _logger(logger),
|
||||
_modulesList(modulesList),
|
||||
_autoSetup(sConfigMgr->GetOption<bool>("Updates.AutoSetup", true)),
|
||||
_updateFlags(sConfigMgr->GetOption<uint32>("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([this, name, updatesEnabledForThis, &pool]() -> bool
|
||||
{
|
||||
std::string const& defaultDatabaseInfo = GetDefaultDatabaseInfo(name);
|
||||
std::string const dbString = sConfigMgr->GetOption<std::string>(name + "DatabaseInfo", defaultDatabaseInfo);
|
||||
if (dbString.empty())
|
||||
{
|
||||
LOG_ERROR(_logger, "Database {} not specified in configuration file!", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 const asyncThreads = sConfigMgr->GetOption<uint8>(name + "Database.WorkerThreads", 1);
|
||||
if (asyncThreads < 1 || asyncThreads > 32)
|
||||
{
|
||||
LOG_ERROR(_logger, "{} database: invalid number of worker threads specified. "
|
||||
"Please pick a value between 1 and 32.", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 const synchThreads = sConfigMgr->GetOption<uint8>(name + "Database.SynchThreads", 1);
|
||||
|
||||
pool.SetConnectionInfo(dbString, asyncThreads, synchThreads);
|
||||
|
||||
if (uint32 error = pool.Open())
|
||||
{
|
||||
// Try reconnect
|
||||
if (error == CR_CONNECTION_ERROR)
|
||||
{
|
||||
uint8 const attempts = sConfigMgr->GetOption<uint8>("Database.Reconnect.Attempts", 20);
|
||||
Seconds reconnectSeconds = Seconds(sConfigMgr->GetOption<uint8>("Database.Reconnect.Seconds", 15));
|
||||
uint8 reconnectCount = 0;
|
||||
|
||||
while (reconnectCount < attempts)
|
||||
{
|
||||
LOG_WARN(_logger, "> Retrying after {} seconds", static_cast<uint32>(reconnectSeconds.count()));
|
||||
std::this_thread::sleep_for(reconnectSeconds);
|
||||
error = pool.Open();
|
||||
|
||||
if (error == CR_CONNECTION_ERROR)
|
||||
{
|
||||
reconnectCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
LOG_ERROR(_logger, "DatabasePool {} NOT opened. There were errors opening the MySQL connections. "
|
||||
"Check your log file for specific errors", name);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Add the close operation
|
||||
_close.push([&pool]
|
||||
{
|
||||
pool.Close();
|
||||
});
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// Populate and update only if updates are enabled for this pool
|
||||
if (updatesEnabledForThis)
|
||||
{
|
||||
_populate.push([this, name, &pool]() -> bool
|
||||
{
|
||||
if (!DBUpdater<T>::Populate(pool))
|
||||
{
|
||||
LOG_ERROR(_logger, "Could not populate the {} database, see log for details.", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
_update.push([this, name, &pool]() -> bool
|
||||
{
|
||||
if (!DBUpdater<T>::Update(pool, _modulesList))
|
||||
{
|
||||
LOG_ERROR(_logger, "Could not update the {} database, see log for details.", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
_prepare.push([this, name, &pool]() -> bool
|
||||
{
|
||||
if (!pool.PrepareStatements())
|
||||
{
|
||||
LOG_ERROR(_logger, "Could not prepare statements of the {} database, see log for details.", name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DatabaseLoader::Load()
|
||||
{
|
||||
if (!_updateFlags)
|
||||
LOG_WARN("sql.updates", "> AUTOUPDATER: Automatic database updates are disabled for all databases in the config! This is not recommended!");
|
||||
|
||||
if (!OpenDatabases())
|
||||
return false;
|
||||
|
||||
if (!PopulateDatabases())
|
||||
return false;
|
||||
|
||||
if (!UpdateDatabases())
|
||||
return false;
|
||||
|
||||
if (!PrepareStatements())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DatabaseLoader::OpenDatabases()
|
||||
{
|
||||
return Process(_open);
|
||||
}
|
||||
|
||||
bool DatabaseLoader::PopulateDatabases()
|
||||
{
|
||||
return Process(_populate);
|
||||
}
|
||||
|
||||
bool DatabaseLoader::UpdateDatabases()
|
||||
{
|
||||
return Process(_update);
|
||||
}
|
||||
|
||||
bool DatabaseLoader::PrepareStatements()
|
||||
{
|
||||
return Process(_prepare);
|
||||
}
|
||||
|
||||
bool DatabaseLoader::Process(std::queue<Predicate>& queue)
|
||||
{
|
||||
while (!queue.empty())
|
||||
{
|
||||
if (!queue.front()())
|
||||
{
|
||||
// Close all open databases which have a registered close operation
|
||||
while (!_close.empty())
|
||||
{
|
||||
_close.top()();
|
||||
_close.pop();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
queue.pop();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template AC_DATABASE_API
|
||||
DatabaseLoader& DatabaseLoader::AddDatabase<LoginDatabaseConnection>(DatabaseWorkerPool<LoginDatabaseConnection>&, std::string const&);
|
||||
template AC_DATABASE_API
|
||||
DatabaseLoader& DatabaseLoader::AddDatabase<CharacterDatabaseConnection>(DatabaseWorkerPool<CharacterDatabaseConnection>&, std::string const&);
|
||||
template AC_DATABASE_API
|
||||
DatabaseLoader& DatabaseLoader::AddDatabase<WorldDatabaseConnection>(DatabaseWorkerPool<WorldDatabaseConnection>&, std::string const&);
|
||||
82
src/server/database/Database/DatabaseLoader.h
Normal file
82
src/server/database/Database/DatabaseLoader.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "Define.h"
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
|
||||
template <class T>
|
||||
class DatabaseWorkerPool;
|
||||
|
||||
// A helper class to initiate all database worker pools,
|
||||
// handles updating, delays preparing of statements and cleans up on failure.
|
||||
class AC_DATABASE_API DatabaseLoader
|
||||
{
|
||||
public:
|
||||
DatabaseLoader(std::string const& logger, uint32 const defaultUpdateMask = 7, std::string_view modulesList = {});
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
[[nodiscard]] uint32 GetUpdateFlags() const
|
||||
{
|
||||
return _updateFlags;
|
||||
}
|
||||
|
||||
private:
|
||||
bool OpenDatabases();
|
||||
bool PopulateDatabases();
|
||||
bool UpdateDatabases();
|
||||
bool PrepareStatements();
|
||||
|
||||
using Predicate = std::function<bool()>;
|
||||
using Closer = std::function<void()>;
|
||||
|
||||
// Invokes all functions in the given queue and closes the databases on errors.
|
||||
// Returns false when there was an error.
|
||||
bool Process(std::queue<Predicate>& queue);
|
||||
|
||||
std::string const _logger;
|
||||
std::string_view _modulesList;
|
||||
bool const _autoSetup;
|
||||
uint32 const _updateFlags;
|
||||
|
||||
std::queue<Predicate> _open, _populate, _update, _prepare;
|
||||
std::stack<Closer> _close;
|
||||
};
|
||||
|
||||
#endif // DatabaseLoader_h__
|
||||
53
src/server/database/Database/DatabaseWorker.cpp
Normal file
53
src/server/database/Database/DatabaseWorker.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "DatabaseWorker.h"
|
||||
#include "PCQueue.h"
|
||||
#include "SQLOperation.h"
|
||||
|
||||
DatabaseWorker::DatabaseWorker(ProducerConsumerQueue<SQLOperation*>* newQueue, MySQLConnection* connection)
|
||||
{
|
||||
_connection = connection;
|
||||
_queue = newQueue;
|
||||
_workerThread = std::thread(&DatabaseWorker::WorkerThread, this);
|
||||
}
|
||||
|
||||
DatabaseWorker::~DatabaseWorker()
|
||||
{
|
||||
_workerThread.join();
|
||||
}
|
||||
|
||||
void DatabaseWorker::WorkerThread()
|
||||
{
|
||||
if (!_queue)
|
||||
return;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
SQLOperation* operation = nullptr;
|
||||
|
||||
_queue->WaitAndPop(operation);
|
||||
|
||||
if (!operation)
|
||||
return;
|
||||
|
||||
operation->SetConnection(_connection);
|
||||
operation->call();
|
||||
|
||||
delete operation;
|
||||
}
|
||||
}
|
||||
48
src/server/database/Database/DatabaseWorker.h
Normal file
48
src/server/database/Database/DatabaseWorker.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "Define.h"
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
template <typename T>
|
||||
class ProducerConsumerQueue;
|
||||
|
||||
class MySQLConnection;
|
||||
class SQLOperation;
|
||||
|
||||
class AC_DATABASE_API DatabaseWorker
|
||||
{
|
||||
public:
|
||||
DatabaseWorker(ProducerConsumerQueue<SQLOperation*>* newQueue, MySQLConnection* connection);
|
||||
~DatabaseWorker();
|
||||
|
||||
private:
|
||||
ProducerConsumerQueue<SQLOperation*>* _queue;
|
||||
MySQLConnection* _connection;
|
||||
|
||||
void WorkerThread();
|
||||
std::thread _workerThread;
|
||||
|
||||
DatabaseWorker(DatabaseWorker const& right) = delete;
|
||||
DatabaseWorker& operator=(DatabaseWorker const& right) = delete;
|
||||
};
|
||||
|
||||
#endif
|
||||
573
src/server/database/Database/DatabaseWorkerPool.cpp
Normal file
573
src/server/database/Database/DatabaseWorkerPool.cpp
Normal file
@@ -0,0 +1,573 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "AdhocStatement.h"
|
||||
#include "CharacterDatabase.h"
|
||||
#include "Errors.h"
|
||||
#include "Log.h"
|
||||
#include "LoginDatabase.h"
|
||||
#include "MySQLPreparedStatement.h"
|
||||
#include "MySQLWorkaround.h"
|
||||
#include "PCQueue.h"
|
||||
#include "PreparedStatement.h"
|
||||
#include "QueryCallback.h"
|
||||
#include "QueryHolder.h"
|
||||
#include "QueryResult.h"
|
||||
#include "SQLOperation.h"
|
||||
#include "Transaction.h"
|
||||
#include "WorldDatabase.h"
|
||||
#include <limits>
|
||||
#include <mysqld_error.h>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#ifdef ACORE_DEBUG
|
||||
#include <boost/stacktrace.hpp>
|
||||
#include <sstream>
|
||||
#endif
|
||||
|
||||
class PingOperation : public SQLOperation
|
||||
{
|
||||
//! Operation for idle delaythreads
|
||||
bool Execute() override
|
||||
{
|
||||
m_conn->Ping();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
DatabaseWorkerPool<T>::DatabaseWorkerPool() :
|
||||
_queue(new ProducerConsumerQueue<SQLOperation*>()),
|
||||
_async_threads(0),
|
||||
_synch_threads(0)
|
||||
{
|
||||
WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
|
||||
|
||||
bool isSupportClientDB = mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION;
|
||||
bool isSameClientDB = mysql_get_client_version() == MYSQL_VERSION_ID;
|
||||
|
||||
WPFatal(isSupportClientDB, "AzerothCore does not support MySQL versions below 8.0\n\nFound version: {} / {}. Server compiled with: {}.\nSearch the wiki for ACE00043 in Common Errors (https://www.azerothcore.org/wiki/common-errors#ace00043).",
|
||||
mysql_get_client_info(), mysql_get_client_version(), MYSQL_VERSION_ID);
|
||||
WPFatal(isSameClientDB, "Used MySQL library version ({} id {}) does not match the version id used to compile AzerothCore (id {}).\nSearch the wiki for ACE00046 in Common Errors (https://www.azerothcore.org/wiki/common-errors#ace00046).",
|
||||
mysql_get_client_info(), mysql_get_client_version(), MYSQL_VERSION_ID);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
DatabaseWorkerPool<T>::~DatabaseWorkerPool()
|
||||
{
|
||||
_queue->Cancel();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::SetConnectionInfo(std::string_view infoString, uint8 const asyncThreads, uint8 const synchThreads)
|
||||
{
|
||||
_connectionInfo = std::make_unique<MySQLConnectionInfo>(infoString);
|
||||
|
||||
_async_threads = asyncThreads;
|
||||
_synch_threads = synchThreads;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint32 DatabaseWorkerPool<T>::Open()
|
||||
{
|
||||
WPFatal(_connectionInfo.get(), "Connection info was not set!");
|
||||
|
||||
LOG_INFO("sql.driver", "Opening DatabasePool '{}'. Asynchronous connections: {}, synchronous connections: {}.",
|
||||
GetDatabaseName(), _async_threads, _synch_threads);
|
||||
|
||||
uint32 error = OpenConnections(IDX_ASYNC, _async_threads);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = OpenConnections(IDX_SYNCH, _synch_threads);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
LOG_INFO("sql.driver", "DatabasePool '{}' opened successfully. {} total connections running.",
|
||||
GetDatabaseName(), (_connections[IDX_SYNCH].size() + _connections[IDX_ASYNC].size()));
|
||||
}
|
||||
|
||||
LOG_INFO("sql.driver", " ");
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::Close()
|
||||
{
|
||||
LOG_INFO("sql.driver", "Closing down DatabasePool '{}'. Waiting for {} queries to finish...", GetDatabaseName(), _queue->Size());
|
||||
|
||||
// Gracefully close async query queue, worker threads will block when the destructor
|
||||
// is called from the .clear() functions below until the queue is empty
|
||||
_queue->Shutdown();
|
||||
|
||||
//! Closes the actualy MySQL connection.
|
||||
_connections[IDX_ASYNC].clear();
|
||||
|
||||
LOG_INFO("sql.driver", "Asynchronous connections on DatabasePool '{}' terminated. Proceeding with synchronous connections.",
|
||||
GetDatabaseName());
|
||||
|
||||
//! Shut down the synchronous connections
|
||||
//! There's no need for locking the connection, because DatabaseWorkerPool<>::Close
|
||||
//! should only be called after any other thread tasks in the core have exited,
|
||||
//! meaning there can be no concurrent access at this point.
|
||||
_connections[IDX_SYNCH].clear();
|
||||
|
||||
LOG_INFO("sql.driver", "All connections on DatabasePool '{}' closed.", GetDatabaseName());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
bool DatabaseWorkerPool<T>::PrepareStatements()
|
||||
{
|
||||
for (auto const& connections : _connections)
|
||||
{
|
||||
for (auto const& connection : connections)
|
||||
{
|
||||
connection->LockIfReady();
|
||||
if (!connection->PrepareStatements())
|
||||
{
|
||||
connection->Unlock();
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
connection->Unlock();
|
||||
|
||||
std::size_t const preparedSize = connection->m_stmts.size();
|
||||
if (_preparedStatementSize.size() < preparedSize)
|
||||
_preparedStatementSize.resize(preparedSize);
|
||||
|
||||
for (std::size_t i = 0; i < preparedSize; ++i)
|
||||
{
|
||||
// already set by another connection
|
||||
// (each connection only has prepared statements of it's own type sync/async)
|
||||
if (_preparedStatementSize[i] > 0)
|
||||
continue;
|
||||
|
||||
if (MySQLPreparedStatement* stmt = connection->m_stmts[i].get())
|
||||
{
|
||||
uint32 const paramCount = stmt->GetParameterCount();
|
||||
|
||||
// WH only supports uint8 indices.
|
||||
ASSERT(paramCount < std::numeric_limits<uint8>::max());
|
||||
|
||||
_preparedStatementSize[i] = static_cast<uint8>(paramCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
QueryResult DatabaseWorkerPool<T>::Query(std::string_view sql)
|
||||
{
|
||||
auto connection = GetFreeConnection();
|
||||
|
||||
ResultSet* result = connection->Query(sql);
|
||||
connection->Unlock();
|
||||
|
||||
if (!result || !result->GetRowCount() || !result->NextRow())
|
||||
{
|
||||
delete result;
|
||||
return QueryResult(nullptr);
|
||||
}
|
||||
|
||||
return QueryResult(result);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PreparedQueryResult DatabaseWorkerPool<T>::Query(PreparedStatement<T>* stmt)
|
||||
{
|
||||
auto connection = GetFreeConnection();
|
||||
PreparedResultSet* ret = connection->Query(stmt);
|
||||
connection->Unlock();
|
||||
|
||||
//! Delete proxy-class. Not needed anymore
|
||||
delete stmt;
|
||||
|
||||
if (!ret || !ret->GetRowCount())
|
||||
{
|
||||
delete ret;
|
||||
return PreparedQueryResult(nullptr);
|
||||
}
|
||||
|
||||
return PreparedQueryResult(ret);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
QueryCallback DatabaseWorkerPool<T>::AsyncQuery(std::string_view 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 QueryCallback(std::move(result));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
QueryCallback DatabaseWorkerPool<T>::AsyncQuery(PreparedStatement<T>* 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 QueryCallback(std::move(result));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
SQLQueryHolderCallback DatabaseWorkerPool<T>::DelayQueryHolder(std::shared_ptr<SQLQueryHolder<T>> 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 { std::move(holder), std::move(result) };
|
||||
}
|
||||
|
||||
template <class T>
|
||||
SQLTransaction<T> DatabaseWorkerPool<T>::BeginTransaction()
|
||||
{
|
||||
return std::make_shared<Transaction<T>>();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::CommitTransaction(SQLTransaction<T> transaction)
|
||||
{
|
||||
#ifdef ACORE_DEBUG
|
||||
//! Only analyze transaction weaknesses in Debug mode.
|
||||
//! Ideally we catch the faults in Debug mode and then correct them,
|
||||
//! so there's no need to waste these CPU cycles in Release mode.
|
||||
switch (transaction->GetSize())
|
||||
{
|
||||
case 0:
|
||||
LOG_DEBUG("sql.driver", "Transaction contains 0 queries. Not executing.");
|
||||
return;
|
||||
case 1:
|
||||
LOG_DEBUG("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif // ACORE_DEBUG
|
||||
|
||||
Enqueue(new TransactionTask(transaction));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
TransactionCallback DatabaseWorkerPool<T>::AsyncCommitTransaction(SQLTransaction<T> transaction)
|
||||
{
|
||||
#ifdef ACORE_DEBUG
|
||||
//! Only analyze transaction weaknesses in Debug mode.
|
||||
//! Ideally we catch the faults in Debug mode and then correct them,
|
||||
//! so there's no need to waste these CPU cycles in Release mode.
|
||||
switch (transaction->GetSize())
|
||||
{
|
||||
case 0:
|
||||
LOG_DEBUG("sql.driver", "Transaction contains 0 queries. Not executing.");
|
||||
break;
|
||||
case 1:
|
||||
LOG_DEBUG("sql.driver", "Warning: Transaction only holds 1 query, consider removing Transaction context in code.");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif // ACORE_DEBUG
|
||||
|
||||
TransactionWithResultTask* task = new TransactionWithResultTask(transaction);
|
||||
TransactionFuture result = task->GetFuture();
|
||||
Enqueue(task);
|
||||
return TransactionCallback(std::move(result));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::DirectCommitTransaction(SQLTransaction<T>& transaction)
|
||||
{
|
||||
T* connection = GetFreeConnection();
|
||||
int errorCode = connection->ExecuteTransaction(transaction);
|
||||
|
||||
if (!errorCode)
|
||||
{
|
||||
connection->Unlock(); // OK, operation succesful
|
||||
return;
|
||||
}
|
||||
|
||||
//! Handle MySQL Errno 1213 without extending deadlock to the core itself
|
||||
/// @todo More elegant way
|
||||
if (errorCode == ER_LOCK_DEADLOCK)
|
||||
{
|
||||
//todo: handle multiple sync threads deadlocking in a similar way as async threads
|
||||
uint8 loopBreaker = 5;
|
||||
|
||||
for (uint8 i = 0; i < loopBreaker; ++i)
|
||||
{
|
||||
if (!connection->ExecuteTransaction(transaction))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//! Clean up now.
|
||||
transaction->Cleanup();
|
||||
|
||||
connection->Unlock();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
PreparedStatement<T>* DatabaseWorkerPool<T>::GetPreparedStatement(PreparedStatementIndex index)
|
||||
{
|
||||
return new PreparedStatement<T>(index, _preparedStatementSize[index]);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::EscapeString(std::string& str)
|
||||
{
|
||||
if (str.empty())
|
||||
return;
|
||||
|
||||
char* buf = new char[str.size() * 2 + 1];
|
||||
EscapeString(buf, str.c_str(), uint32(str.size()));
|
||||
str = buf;
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::KeepAlive()
|
||||
{
|
||||
//! Ping synchronous connections
|
||||
for (auto& connection : _connections[IDX_SYNCH])
|
||||
{
|
||||
if (connection->LockIfReady())
|
||||
{
|
||||
connection->Ping();
|
||||
connection->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
//! Assuming all worker threads are free, every worker thread will receive 1 ping operation request
|
||||
//! If one or more worker threads are busy, the ping operations will not be split evenly, but this doesn't matter
|
||||
//! as the sole purpose is to prevent connections from idling.
|
||||
auto const count = _connections[IDX_ASYNC].size();
|
||||
|
||||
for (uint8 i = 0; i < count; ++i)
|
||||
Enqueue(new PingOperation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if the version string given is incompatible
|
||||
*
|
||||
* Intended to be used with mysql_get_server_info()'s output as the source
|
||||
*
|
||||
* DatabaseIncompatibleVersion("8.0.35") => false
|
||||
* DatabaseIncompatibleVersion("5.6.6") => true
|
||||
*
|
||||
* Adapted from stackoverflow response
|
||||
* https://stackoverflow.com/a/2941508
|
||||
*
|
||||
* @param mysqlVersion The output from GetServerInfo()/mysql_get_server_info()
|
||||
* @return Returns true if the Server version is incompatible
|
||||
*/
|
||||
bool DatabaseIncompatibleVersion(std::string const mysqlVersion)
|
||||
{
|
||||
// anon func to turn a version string into an array of uint8
|
||||
// "1.2.3" => [1, 2, 3]
|
||||
auto parse = [](std::string const& input)
|
||||
{
|
||||
std::vector<uint8> result;
|
||||
std::istringstream parser(input);
|
||||
result.push_back(parser.get());
|
||||
for (int i = 1; i < 3; i++)
|
||||
{
|
||||
// Skip period
|
||||
parser.get();
|
||||
// Append int from parser to output
|
||||
result.push_back(parser.get());
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
// default to values for MySQL
|
||||
uint8 offset = 0;
|
||||
std::string minVersion = MIN_MYSQL_SERVER_VERSION;
|
||||
|
||||
auto parsedMySQLVersion = parse(mysqlVersion.substr(offset));
|
||||
auto parsedMinVersion = parse(minVersion);
|
||||
|
||||
return std::lexicographical_compare(parsedMySQLVersion.begin(), parsedMySQLVersion.end(),
|
||||
parsedMinVersion.begin(), parsedMinVersion.end());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
uint32 DatabaseWorkerPool<T>::OpenConnections(InternalIndex type, uint8 numConnections)
|
||||
{
|
||||
for (uint8 i = 0; i < numConnections; ++i)
|
||||
{
|
||||
// Create the connection
|
||||
auto connection = [&]
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case IDX_ASYNC:
|
||||
return std::make_unique<T>(_queue.get(), *_connectionInfo);
|
||||
case IDX_SYNCH:
|
||||
return std::make_unique<T>(*_connectionInfo);
|
||||
default:
|
||||
ABORT();
|
||||
}
|
||||
}();
|
||||
|
||||
if (uint32 error = connection->Open())
|
||||
{
|
||||
// Failed to open a connection or invalid version, abort and cleanup
|
||||
_queue->Cancel();
|
||||
_connections[type].clear();
|
||||
return error;
|
||||
}
|
||||
else if (DatabaseIncompatibleVersion(connection->GetServerInfo()))
|
||||
{
|
||||
LOG_ERROR("sql.driver", "AzerothCore does not support MySQL versions below 8.0\n\nFound server version: {}. Server compiled with: {}.",
|
||||
connection->GetServerInfo(), MYSQL_VERSION_ID);
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
_connections[type].push_back(std::move(connection));
|
||||
}
|
||||
}
|
||||
|
||||
// Everything is fine
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
unsigned long DatabaseWorkerPool<T>::EscapeString(char* to, char const* from, unsigned long length)
|
||||
{
|
||||
if (!to || !from || !length)
|
||||
return 0;
|
||||
|
||||
return _connections[IDX_SYNCH].front()->EscapeString(to, from, length);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::Enqueue(SQLOperation* op)
|
||||
{
|
||||
_queue->Push(op);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::size_t DatabaseWorkerPool<T>::QueueSize() const
|
||||
{
|
||||
return _queue->Size();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T* DatabaseWorkerPool<T>::GetFreeConnection()
|
||||
{
|
||||
#ifdef ACORE_DEBUG
|
||||
if (_warnSyncQueries)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << boost::stacktrace::stacktrace();
|
||||
LOG_WARN("sql.performances", "Sync query at:\n{}", ss.str());
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8 i = 0;
|
||||
auto const num_cons = _connections[IDX_SYNCH].size();
|
||||
T* connection = nullptr;
|
||||
|
||||
//! Block forever until a connection is free
|
||||
for (;;)
|
||||
{
|
||||
connection = _connections[IDX_SYNCH][++i % num_cons].get();
|
||||
//! Must be matched with t->Unlock() or you will get deadlocks
|
||||
if (connection->LockIfReady())
|
||||
break;
|
||||
}
|
||||
|
||||
return connection;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
std::string_view DatabaseWorkerPool<T>::GetDatabaseName() const
|
||||
{
|
||||
return std::string_view{ _connectionInfo->database };
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::Execute(std::string_view sql)
|
||||
{
|
||||
if (sql.empty())
|
||||
return;
|
||||
|
||||
BasicStatementTask* task = new BasicStatementTask(sql);
|
||||
Enqueue(task);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::Execute(PreparedStatement<T>* stmt)
|
||||
{
|
||||
PreparedStatementTask* task = new PreparedStatementTask(stmt);
|
||||
Enqueue(task);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::DirectExecute(std::string_view sql)
|
||||
{
|
||||
if (sql.empty())
|
||||
return;
|
||||
|
||||
T* connection = GetFreeConnection();
|
||||
connection->Execute(sql);
|
||||
connection->Unlock();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::DirectExecute(PreparedStatement<T>* stmt)
|
||||
{
|
||||
T* connection = GetFreeConnection();
|
||||
connection->Execute(stmt);
|
||||
connection->Unlock();
|
||||
|
||||
//! Delete proxy-class. Not needed anymore
|
||||
delete stmt;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::ExecuteOrAppend(SQLTransaction<T>& trans, std::string_view sql)
|
||||
{
|
||||
if (!trans)
|
||||
Execute(sql);
|
||||
else
|
||||
trans->Append(sql);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void DatabaseWorkerPool<T>::ExecuteOrAppend(SQLTransaction<T>& trans, PreparedStatement<T>* stmt)
|
||||
{
|
||||
if (!trans)
|
||||
Execute(stmt);
|
||||
else
|
||||
trans->Append(stmt);
|
||||
}
|
||||
|
||||
template class AC_DATABASE_API DatabaseWorkerPool<LoginDatabaseConnection>;
|
||||
template class AC_DATABASE_API DatabaseWorkerPool<WorldDatabaseConnection>;
|
||||
template class AC_DATABASE_API DatabaseWorkerPool<CharacterDatabaseConnection>;
|
||||
242
src/server/database/Database/DatabaseWorkerPool.h
Normal file
242
src/server/database/Database/DatabaseWorkerPool.h
Normal file
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "DatabaseEnvFwd.h"
|
||||
#include "Define.h"
|
||||
#include "StringFormat.h"
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
/** @file DatabaseWorkerPool.h */
|
||||
|
||||
/**
|
||||
* @def MIN_MYSQL_CLIENT_VERSION
|
||||
* The minimum MySQL Client Version
|
||||
*/
|
||||
#define MIN_MYSQL_CLIENT_VERSION 80000u
|
||||
|
||||
/**
|
||||
* @def MIN_MYSQL_SERVER_VERSION
|
||||
* The minimum MySQL Server Version
|
||||
*/
|
||||
#define MIN_MYSQL_SERVER_VERSION "8.0.0"
|
||||
|
||||
template <typename T>
|
||||
class ProducerConsumerQueue;
|
||||
|
||||
class SQLOperation;
|
||||
struct MySQLConnectionInfo;
|
||||
|
||||
template <class T>
|
||||
class DatabaseWorkerPool
|
||||
{
|
||||
private:
|
||||
enum InternalIndex
|
||||
{
|
||||
IDX_ASYNC,
|
||||
IDX_SYNCH,
|
||||
IDX_SIZE
|
||||
};
|
||||
|
||||
public:
|
||||
/* Activity state */
|
||||
DatabaseWorkerPool();
|
||||
~DatabaseWorkerPool();
|
||||
|
||||
void SetConnectionInfo(std::string_view infoString, uint8 const asyncThreads, uint8 const synchThreads);
|
||||
|
||||
uint32 Open();
|
||||
void Close();
|
||||
|
||||
//! Prepares all prepared statements
|
||||
bool PrepareStatements();
|
||||
|
||||
[[nodiscard]] 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(std::string_view sql);
|
||||
|
||||
//! Enqueues a one-way SQL operation in string format -with variable args- that will be executed asynchronously.
|
||||
//! This method should only be used for queries that are only executed once, e.g during startup.
|
||||
template<typename... Args>
|
||||
void Execute(std::string_view sql, Args&&... args)
|
||||
{
|
||||
if (sql.empty())
|
||||
return;
|
||||
|
||||
Execute(Acore::StringFormat(sql, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
//! 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<T>* stmt);
|
||||
|
||||
/**
|
||||
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(std::string_view sql);
|
||||
|
||||
//! Directly executes a one-way SQL operation in string format -with variable args-, that will block the calling thread until finished.
|
||||
//! This method should only be used for queries that are only executed once, e.g during startup.
|
||||
template<typename... Args>
|
||||
void DirectExecute(std::string_view sql, Args&&... args)
|
||||
{
|
||||
if (sql.empty())
|
||||
return;
|
||||
|
||||
DirectExecute(Acore::StringFormat(sql, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
//! 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<T>* 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(std::string_view sql);
|
||||
|
||||
//! 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... Args>
|
||||
QueryResult Query(std::string_view sql, Args&&... args)
|
||||
{
|
||||
if (sql.empty())
|
||||
return QueryResult(nullptr);
|
||||
|
||||
return Query(Acore::StringFormat(sql, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
//! 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<T>* stmt);
|
||||
|
||||
/**
|
||||
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.
|
||||
QueryCallback AsyncQuery(std::string_view sql);
|
||||
|
||||
//! Enqueues a query in prepared format that will set the value of the PreparedQueryResultFuture return object as soon as the query is executed.
|
||||
//! The return value is then processed in ProcessQueryCallback methods.
|
||||
//! Statement must be prepared with CONNECTION_ASYNC flag.
|
||||
QueryCallback AsyncQuery(PreparedStatement<T>* stmt);
|
||||
|
||||
//! Enqueues a vector of SQL operations (can be both adhoc and prepared) that will set the value of the QueryResultHolderFuture
|
||||
//! return object as soon as the query is executed.
|
||||
//! The return value is then processed in ProcessQueryCallback methods.
|
||||
//! Any prepared statements added to this holder need to be prepared with the CONNECTION_ASYNC flag.
|
||||
SQLQueryHolderCallback DelayQueryHolder(std::shared_ptr<SQLQueryHolder<T>> holder);
|
||||
|
||||
/**
|
||||
Transaction context methods.
|
||||
*/
|
||||
|
||||
//! Begins an automanaged transaction pointer that will automatically rollback if not commited. (Autocommit=0)
|
||||
SQLTransaction<T> BeginTransaction();
|
||||
|
||||
//! Enqueues a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations
|
||||
//! were appended to the transaction will be respected during execution.
|
||||
void CommitTransaction(SQLTransaction<T> transaction);
|
||||
|
||||
//! Enqueues a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations
|
||||
//! were appended to the transaction will be respected during execution.
|
||||
TransactionCallback AsyncCommitTransaction(SQLTransaction<T> transaction);
|
||||
|
||||
//! Directly executes a collection of one-way SQL operations (can be both adhoc and prepared). The order in which these operations
|
||||
//! were appended to the transaction will be respected during execution.
|
||||
void DirectCommitTransaction(SQLTransaction<T>& transaction);
|
||||
|
||||
//! Method used to execute ad-hoc statements in a diverse context.
|
||||
//! Will be wrapped in a transaction if valid object is present, otherwise executed standalone.
|
||||
void ExecuteOrAppend(SQLTransaction<T>& trans, std::string_view sql);
|
||||
|
||||
//! Method used to execute prepared statements in a diverse context.
|
||||
//! Will be wrapped in a transaction if valid object is present, otherwise executed standalone.
|
||||
void ExecuteOrAppend(SQLTransaction<T>& trans, PreparedStatement<T>* stmt);
|
||||
|
||||
/**
|
||||
Other
|
||||
*/
|
||||
|
||||
typedef typename T::Statements PreparedStatementIndex;
|
||||
|
||||
//! Automanaged (internally) pointer to a prepared statement object for usage in upper level code.
|
||||
//! Pointer is deleted in this->DirectExecute(PreparedStatement*), this->Query(PreparedStatement*) or PreparedStatementTask::~PreparedStatementTask.
|
||||
//! This object is not tied to the prepared statement on the MySQL context yet until execution.
|
||||
PreparedStatement<T>* GetPreparedStatement(PreparedStatementIndex index);
|
||||
|
||||
//! Apply escape string'ing for current collation. (utf8)
|
||||
void EscapeString(std::string& str);
|
||||
|
||||
//! Keeps all our MySQL connections alive, prevent the server from disconnecting us.
|
||||
void KeepAlive();
|
||||
|
||||
void WarnAboutSyncQueries([[maybe_unused]] bool warn)
|
||||
{
|
||||
#ifdef ACORE_DEBUG
|
||||
_warnSyncQueries = warn;
|
||||
#endif
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t QueueSize() const;
|
||||
|
||||
private:
|
||||
uint32 OpenConnections(InternalIndex type, uint8 numConnections);
|
||||
|
||||
unsigned long EscapeString(char* to, char const* from, unsigned long length);
|
||||
|
||||
void Enqueue(SQLOperation* op);
|
||||
|
||||
//! Gets a free connection in the synchronous connection pool.
|
||||
//! Caller MUST call t->Unlock() after touching the MySQL context to prevent deadlocks.
|
||||
T* GetFreeConnection();
|
||||
|
||||
[[nodiscard]] std::string_view GetDatabaseName() const;
|
||||
|
||||
//! Queue shared by async worker threads.
|
||||
std::unique_ptr<ProducerConsumerQueue<SQLOperation*>> _queue;
|
||||
std::array<std::vector<std::unique_ptr<T>>, IDX_SIZE> _connections;
|
||||
std::unique_ptr<MySQLConnectionInfo> _connectionInfo;
|
||||
std::vector<uint8> _preparedStatementSize;
|
||||
uint8 _async_threads, _synch_threads;
|
||||
#ifdef ACORE_DEBUG
|
||||
static inline thread_local bool _warnSyncQueries = false;
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif // _DATABASEWORKERPOOL_H
|
||||
338
src/server/database/Database/Field.cpp
Normal file
338
src/server/database/Database/Field.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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"
|
||||
#include "Errors.h"
|
||||
#include "Log.h"
|
||||
#include "MySQLHacks.h"
|
||||
#include "StringConvert.h"
|
||||
|
||||
Field::Field()
|
||||
{
|
||||
data.value = nullptr;
|
||||
data.length = 0;
|
||||
data.raw = false;
|
||||
meta = nullptr;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
template<typename T>
|
||||
constexpr T GetDefaultValue()
|
||||
{
|
||||
if constexpr (std::is_same_v<T, bool>)
|
||||
return false;
|
||||
else if constexpr (std::is_integral_v<T>)
|
||||
return 0;
|
||||
else if constexpr (std::is_floating_point_v<T>)
|
||||
return 1.0f;
|
||||
else if constexpr (std::is_same_v<T, std::vector<uint8>> || std::is_same_v<std::string_view, T>)
|
||||
return {};
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool IsCorrectFieldType(DatabaseFieldTypes type)
|
||||
{
|
||||
// Int8
|
||||
if constexpr (std::is_same_v<T, bool> || std::is_same_v<T, int8> || std::is_same_v<T, uint8>)
|
||||
{
|
||||
if (type == DatabaseFieldTypes::Int8)
|
||||
return true;
|
||||
}
|
||||
|
||||
// In16
|
||||
if constexpr (std::is_same_v<T, uint16> || std::is_same_v<T, int16>)
|
||||
{
|
||||
if (type == DatabaseFieldTypes::Int16)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Int32
|
||||
if constexpr (std::is_same_v<T, uint32> || std::is_same_v<T, int32>)
|
||||
{
|
||||
if (type == DatabaseFieldTypes::Int32)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Int64
|
||||
if constexpr (std::is_same_v<T, uint64> || std::is_same_v<T, int64>)
|
||||
{
|
||||
if (type == DatabaseFieldTypes::Int64)
|
||||
return true;
|
||||
}
|
||||
|
||||
// float
|
||||
if constexpr (std::is_same_v<T, float>)
|
||||
{
|
||||
if (type == DatabaseFieldTypes::Float)
|
||||
return true;
|
||||
}
|
||||
|
||||
// dobule
|
||||
if constexpr (std::is_same_v<T, double>)
|
||||
{
|
||||
if (type == DatabaseFieldTypes::Double || type == DatabaseFieldTypes::Decimal)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Binary
|
||||
if constexpr (std::is_same_v<T, Binary>)
|
||||
{
|
||||
if (type == DatabaseFieldTypes::Binary)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline Optional<std::string_view> GetCleanAliasName(std::string_view alias)
|
||||
{
|
||||
if (alias.empty())
|
||||
return {};
|
||||
|
||||
auto pos = alias.find_first_of('(');
|
||||
if (pos == std::string_view::npos)
|
||||
return {};
|
||||
|
||||
alias.remove_suffix(alias.length() - pos);
|
||||
|
||||
return { alias };
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool IsCorrectAlias(DatabaseFieldTypes type, std::string_view alias)
|
||||
{
|
||||
if constexpr (std::is_same_v<T, double>)
|
||||
{
|
||||
if ((StringEqualI(alias, "sum") || StringEqualI(alias, "avg")) && type == DatabaseFieldTypes::Decimal)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<T, uint64>)
|
||||
{
|
||||
if (StringEqualI(alias, "count") && type == DatabaseFieldTypes::Int64)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((StringEqualI(alias, "min") || StringEqualI(alias, "max")) && IsCorrectFieldType<T>(type))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Field::GetBinarySizeChecked(uint8* buf, std::size_t length) const
|
||||
{
|
||||
ASSERT(data.value && (data.length == length), "Expected {}-byte binary blob, got {}data ({} bytes) instead", length, data.value ? "" : "no ", data.length);
|
||||
memcpy(buf, data.value, length);
|
||||
}
|
||||
|
||||
void Field::SetByteValue(char const* newValue, uint32 length)
|
||||
{
|
||||
// This value stores raw bytes that have to be explicitly cast later
|
||||
data.value = newValue;
|
||||
data.length = length;
|
||||
data.raw = true;
|
||||
}
|
||||
|
||||
void Field::SetStructuredValue(char const* newValue, uint32 length)
|
||||
{
|
||||
// This value stores somewhat structured data that needs function style casting
|
||||
data.value = newValue;
|
||||
data.length = length;
|
||||
data.raw = false;
|
||||
}
|
||||
|
||||
bool Field::IsType(DatabaseFieldTypes type) const
|
||||
{
|
||||
return meta->Type == type;
|
||||
}
|
||||
|
||||
bool Field::IsNumeric() const
|
||||
{
|
||||
return (meta->Type == DatabaseFieldTypes::Int8 ||
|
||||
meta->Type == DatabaseFieldTypes::Int16 ||
|
||||
meta->Type == DatabaseFieldTypes::Int32 ||
|
||||
meta->Type == DatabaseFieldTypes::Int64 ||
|
||||
meta->Type == DatabaseFieldTypes::Float ||
|
||||
meta->Type == DatabaseFieldTypes::Double);
|
||||
}
|
||||
|
||||
void Field::LogWrongType(std::string_view getter, std::string_view typeName) const
|
||||
{
|
||||
LOG_WARN("sql.sql", "Warning: {}<{}> on {} field {}.{} ({}.{}) at index {}.",
|
||||
getter, typeName, meta->TypeName, meta->TableAlias, meta->Alias, meta->TableName, meta->Name, meta->Index);
|
||||
}
|
||||
|
||||
void Field::SetMetadata(QueryResultFieldMetadata const* fieldMeta)
|
||||
{
|
||||
meta = fieldMeta;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T Field::GetData() const
|
||||
{
|
||||
static_assert(std::is_arithmetic_v<T>, "Unsurropt type for Field::GetData()");
|
||||
|
||||
if (!data.value)
|
||||
return GetDefaultValue<T>();
|
||||
|
||||
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
|
||||
if (!IsCorrectFieldType<T>(meta->Type))
|
||||
{
|
||||
LogWrongType(__FUNCTION__, typeid(T).name());
|
||||
//return GetDefaultValue<T>();
|
||||
}
|
||||
#endif
|
||||
|
||||
Optional<T> result = {};
|
||||
|
||||
if (data.raw)
|
||||
result = *reinterpret_cast<T const*>(data.value);
|
||||
else
|
||||
result = Acore::StringTo<T>(std::string_view(data.value, data.length));
|
||||
|
||||
// Correct double fields... this undefined behavior :/
|
||||
if constexpr (std::is_same_v<T, double>)
|
||||
{
|
||||
if (data.raw && !IsType(DatabaseFieldTypes::Decimal))
|
||||
result = *reinterpret_cast<double const*>(data.value);
|
||||
else
|
||||
result = Acore::StringTo<float>(std::string_view(data.value, data.length));
|
||||
}
|
||||
|
||||
// Check -1 for *_dbc db tables
|
||||
if constexpr (std::is_same_v<T, uint32>)
|
||||
{
|
||||
std::string_view tableName{ meta->TableName };
|
||||
|
||||
if (!tableName.empty() && tableName.size() > 4)
|
||||
{
|
||||
auto signedResult = Acore::StringTo<int32>(std::string_view(data.value, data.length));
|
||||
|
||||
if (signedResult && !result && tableName.substr(tableName.length() - 4) == "_dbc")
|
||||
{
|
||||
LOG_DEBUG("sql.sql", "> Found incorrect value '{}' for type '{}' in _dbc table.", data.value, typeid(T).name());
|
||||
LOG_DEBUG("sql.sql", "> Table name '{}'. Field name '{}'. Try return int32 value", meta->TableName, meta->Name);
|
||||
return GetData<int32>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto alias = GetCleanAliasName(meta->Alias))
|
||||
{
|
||||
if ((StringEqualI(*alias, "min") || StringEqualI(*alias, "max")) && !IsCorrectAlias<T>(meta->Type, *alias))
|
||||
{
|
||||
LogWrongType(__FUNCTION__, typeid(T).name());
|
||||
}
|
||||
|
||||
if ((StringEqualI(*alias, "sum") || StringEqualI(*alias, "avg")) && !IsCorrectAlias<T>(meta->Type, *alias))
|
||||
{
|
||||
LogWrongType(__FUNCTION__, typeid(T).name());
|
||||
LOG_WARN("sql.sql", "> Please use GetData<double>()");
|
||||
return GetData<double>();
|
||||
}
|
||||
|
||||
if (StringEqualI(*alias, "count") && !IsCorrectAlias<T>(meta->Type, *alias))
|
||||
{
|
||||
LogWrongType(__FUNCTION__, typeid(T).name());
|
||||
LOG_WARN("sql.sql", "> Please use GetData<uint64>()");
|
||||
return GetData<uint64>();
|
||||
}
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
LOG_FATAL("sql.sql", "> Incorrect value '{}' for type '{}'. Value is raw ? '{}'", data.value, typeid(T).name(), data.raw);
|
||||
LOG_FATAL("sql.sql", "> Table name '{}'. Field name '{}'", meta->TableName, meta->Name);
|
||||
//ABORT();
|
||||
return GetDefaultValue<T>();
|
||||
}
|
||||
|
||||
return *result;
|
||||
}
|
||||
|
||||
template bool Field::GetData() const;
|
||||
template uint8 Field::GetData() const;
|
||||
template uint16 Field::GetData() const;
|
||||
template uint32 Field::GetData() const;
|
||||
template uint64 Field::GetData() const;
|
||||
template int8 Field::GetData() const;
|
||||
template int16 Field::GetData() const;
|
||||
template int32 Field::GetData() const;
|
||||
template int64 Field::GetData() const;
|
||||
template float Field::GetData() const;
|
||||
template double Field::GetData() const;
|
||||
|
||||
std::string Field::GetDataString() const
|
||||
{
|
||||
if (!data.value)
|
||||
return "";
|
||||
|
||||
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
|
||||
if (IsNumeric() && data.raw)
|
||||
{
|
||||
LogWrongType(__FUNCTION__, "std::string");
|
||||
return "";
|
||||
}
|
||||
#endif
|
||||
|
||||
return { data.value, data.length };
|
||||
}
|
||||
|
||||
std::string_view Field::GetDataStringView() const
|
||||
{
|
||||
if (!data.value)
|
||||
return {};
|
||||
|
||||
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
|
||||
if (IsNumeric() && data.raw)
|
||||
{
|
||||
LogWrongType(__FUNCTION__, "std::string_view");
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
return { data.value, data.length };
|
||||
}
|
||||
|
||||
Binary Field::GetDataBinary() const
|
||||
{
|
||||
Binary result = {};
|
||||
if (!data.value || !data.length)
|
||||
return result;
|
||||
|
||||
#ifdef ACORE_STRICT_DATABASE_TYPE_CHECKS
|
||||
if (!IsCorrectFieldType<Binary>(meta->Type))
|
||||
{
|
||||
LogWrongType(__FUNCTION__, "Binary");
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
result.resize(data.length);
|
||||
memcpy(result.data(), data.value, data.length);
|
||||
return result;
|
||||
}
|
||||
178
src/server/database/Database/Field.h
Normal file
178
src/server/database/Database/Field.h
Normal file
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "Define.h"
|
||||
#include "Duration.h"
|
||||
#include <array>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace Acore::Types
|
||||
{
|
||||
template <typename T>
|
||||
using is_chrono_v = std::enable_if_t<std::is_same_v<Milliseconds, T>
|
||||
|| std::is_same_v<Seconds, T>
|
||||
|| std::is_same_v<Minutes, T>
|
||||
|| std::is_same_v<Hours, T>
|
||||
|| std::is_same_v<Days, T>
|
||||
|| std::is_same_v<Weeks, T>
|
||||
|| std::is_same_v<Years, T>
|
||||
|| std::is_same_v<Months, T>, T>;
|
||||
}
|
||||
|
||||
using Binary = std::vector<uint8>;
|
||||
|
||||
enum class DatabaseFieldTypes : uint8
|
||||
{
|
||||
Null,
|
||||
Int8,
|
||||
Int16,
|
||||
Int32,
|
||||
Int64,
|
||||
Float,
|
||||
Double,
|
||||
Decimal,
|
||||
Date,
|
||||
Binary
|
||||
};
|
||||
|
||||
struct QueryResultFieldMetadata
|
||||
{
|
||||
std::string TableName{};
|
||||
std::string TableAlias{};
|
||||
std::string Name{};
|
||||
std::string Alias{};
|
||||
std::string TypeName{};
|
||||
uint32 Index = 0;
|
||||
DatabaseFieldTypes Type = DatabaseFieldTypes::Null;
|
||||
};
|
||||
|
||||
/**
|
||||
@class Field
|
||||
|
||||
@brief Class used to access individual fields of database query result
|
||||
|
||||
Guideline on field type matching:
|
||||
|
||||
| MySQL type | method to use |
|
||||
|------------------------|-----------------------------------------|
|
||||
| TINYINT | Get<bool>, Get<int8>, Get<uint8> |
|
||||
| SMALLINT | Get<int16>, Get<uint16> |
|
||||
| MEDIUMINT, INT | Get<int32>, Get<uint32> |
|
||||
| BIGINT | Get<int64>, Get<uint64> |
|
||||
| FLOAT | Get<float> |
|
||||
| DOUBLE, DECIMAL | Get<double> |
|
||||
| CHAR, VARCHAR, | Get<std::string>, Get<std::string_view> |
|
||||
| TINYTEXT, MEDIUMTEXT, | Get<std::string>, Get<std::string_view> |
|
||||
| TEXT, LONGTEXT | Get<std::string>, Get<std::string_view> |
|
||||
| TINYBLOB, MEDIUMBLOB, | Get<Binary>, Get<std::string> |
|
||||
| BLOB, LONGBLOB | Get<Binary>, Get<std::string> |
|
||||
| BINARY, VARBINARY | Get<Binary> |
|
||||
|
||||
Return types of aggregate functions:
|
||||
|
||||
| Function | Type |
|
||||
|----------|-------------------|
|
||||
| MIN, MAX | Same as the field |
|
||||
| SUM, AVG | DECIMAL |
|
||||
| COUNT | BIGINT |
|
||||
*/
|
||||
class AC_DATABASE_API Field
|
||||
{
|
||||
friend class ResultSet;
|
||||
friend class PreparedResultSet;
|
||||
|
||||
public:
|
||||
Field();
|
||||
~Field() = default;
|
||||
|
||||
[[nodiscard]] inline bool IsNull() const
|
||||
{
|
||||
return data.value == nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_arithmetic_v<T>, T> Get() const
|
||||
{
|
||||
return GetData<T>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_same_v<std::string, T>, T> Get() const
|
||||
{
|
||||
return GetDataString();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_same_v<std::string_view, T>, T> Get() const
|
||||
{
|
||||
return GetDataStringView();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline std::enable_if_t<std::is_same_v<Binary, T>, T> Get() const
|
||||
{
|
||||
return GetDataBinary();
|
||||
}
|
||||
|
||||
template <typename T, std::size_t S>
|
||||
inline std::enable_if_t<std::is_same_v<Binary, T>, std::array<uint8, S>> Get() const
|
||||
{
|
||||
std::array<uint8, S> buf = {};
|
||||
GetBinarySizeChecked(buf.data(), S);
|
||||
return buf;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline Acore::Types::is_chrono_v<T> Get(bool convertToUin32 = true) const
|
||||
{
|
||||
return convertToUin32 ? T(GetData<uint32>()) : T(GetData<uint64>());
|
||||
}
|
||||
|
||||
DatabaseFieldTypes GetType() { return meta->Type; }
|
||||
|
||||
protected:
|
||||
struct
|
||||
{
|
||||
char const* value; // Actual data in memory
|
||||
uint32 length; // Length
|
||||
bool raw; // Raw bytes? (Prepared statement or ad hoc)
|
||||
} data;
|
||||
|
||||
void SetByteValue(char const* newValue, uint32 length);
|
||||
void SetStructuredValue(char const* newValue, uint32 length);
|
||||
[[nodiscard]] bool IsType(DatabaseFieldTypes type) const;
|
||||
[[nodiscard]] bool IsNumeric() const;
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
T GetData() const;
|
||||
|
||||
std::string GetDataString() const;
|
||||
std::string_view GetDataStringView() const;
|
||||
Binary GetDataBinary() const;
|
||||
|
||||
QueryResultFieldMetadata const* meta;
|
||||
void LogWrongType(std::string_view getter, std::string_view typeName) const;
|
||||
void SetMetadata(QueryResultFieldMetadata const* fieldMeta);
|
||||
void GetBinarySizeChecked(uint8* buf, std::size_t size) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,635 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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"
|
||||
#include "MySQLPreparedStatement.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_DATA_BY_NAME, "SELECT guid, account, name, gender, race, class, level FROM characters WHERE deleteDate IS NULL AND name = ?", CONNECTION_BOTH);
|
||||
PrepareStatement(CHAR_SEL_DATA_BY_GUID, "SELECT guid, account, name, gender, race, class, level FROM characters WHERE deleteDate IS NULL AND guid = ?", 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 FROM_UNIXTIME(bandate, '%Y-%m-%d %H:%i:%s'), 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_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, "
|
||||
"gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.extra_flags "
|
||||
"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 COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, "
|
||||
"c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, "
|
||||
"cb.guid, c.extra_flags, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? "
|
||||
"LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
|
||||
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT guid, name, at_login FROM characters WHERE guid = ? AND account = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_POSITION_XYZ, "SELECT map, position_x, position_y, position_z FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
||||
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_QUEST_STATUS_DAILY, "DELETE FROM character_queststatus_daily", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_QUEST_STATUS_WEEKLY, "DELETE FROM character_queststatus_weekly", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_QUEST_STATUS_MONTHLY, "DELETE FROM character_queststatus_monthly", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_QUEST_STATUS_SEASONAL, "DELETE FROM character_queststatus_seasonal WHERE event = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_QUEST_STATUS_DAILY_CHAR, "DELETE FROM character_queststatus_daily WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR, "DELETE FROM character_queststatus_weekly WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_QUEST_STATUS_MONTHLY_CHAR, "DELETE FROM character_queststatus_monthly WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR, "DELETE FROM character_queststatus_seasonal WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_BATTLEGROUND_RANDOM, "DELETE FROM character_battleground_random", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_BATTLEGROUND_RANDOM, "INSERT INTO character_battleground_random (guid) VALUES (?)", CONNECTION_ASYNC);
|
||||
|
||||
// Start LoginQueryHolder content
|
||||
PrepareStatement(CHAR_SEL_CHARACTER, "SELECT guid, account, name, race, class, gender, level, xp, money, skin, face, hairStyle, hairColor, facialStyle, bankSlots, restState, playerFlags, "
|
||||
"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, "
|
||||
"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, "
|
||||
"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, "
|
||||
"health, power1, power2, power3, power4, power5, power6, power7, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, "
|
||||
"knownTitles, actionBars, grantableLevels, innTriggerId, extraBonusTalentCount, UNIX_TIMESTAMP(creation_date) FROM characters WHERE guid = ?", CONNECTION_ASYNC);
|
||||
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_AURAS, "SELECT casterGuid, itemGuid, 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, specMask 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, itemcount5, itemcount6, playercount FROM character_queststatus WHERE guid = ? AND status <> 0", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_DAILYQUESTSTATUS, "SELECT quest, time FROM character_queststatus_daily WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_WEEKLYQUESTSTATUS, "SELECT quest FROM character_queststatus_weekly WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_MONTHLYQUESTSTATUS, "SELECT quest FROM character_queststatus_monthly WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_SEASONALQUESTSTATUS, "SELECT quest, event FROM character_queststatus_seasonal WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_CHARACTER_DAILYQUESTSTATUS, "INSERT INTO character_queststatus_daily (guid, quest, time) VALUES (?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_CHARACTER_WEEKLYQUESTSTATUS, "INSERT INTO character_queststatus_weekly (guid, quest) VALUES (?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_CHARACTER_MONTHLYQUESTSTATUS, "INSERT INTO character_queststatus_monthly (guid, quest) VALUES (?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_CHARACTER_SEASONALQUESTSTATUS, "INSERT IGNORE INTO character_queststatus_seasonal (guid, quest, event) VALUES (?, ?, ?)", 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_UNREAD, "SELECT COUNT(id) FROM mail WHERE receiver = ? AND (checked & 1) = 0 AND deliver_time <= ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD_SYNCH, "SELECT COUNT(id) FROM mail WHERE receiver = ? AND (checked & 1) = 0 AND deliver_time <= ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_MAIL_SERVER_CHARACTER, "SELECT mailId from mail_server_character WHERE guid = ? and mailId = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_REP_MAIL_SERVER_CHARACTER, "REPLACE INTO mail_server_character (guid, mailId) values (?, ?)", CONNECTION_ASYNC);
|
||||
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_BOTH);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, category, item, time, needSend FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_DECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC);
|
||||
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_ENTRY_POINT, "SELECT joinX, joinY, joinZ, joinO, joinMapId, taxiPath0, taxiPath1, mountSpell FROM character_entry_point 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, specMask 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_BREW_OF_THE_MONTH, "SELECT lastEventId FROM character_brew_of_the_month WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_REP_BREW_OF_THE_MONTH, "REPLACE INTO character_brew_of_the_month (guid, lastEventId) VALUES (?, ?)", CONNECTION_ASYNC);
|
||||
// End LoginQueryHolder content
|
||||
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC, "SELECT button, action, type FROM character_action WHERE guid = ? AND spec = ? ORDER BY button", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_MAILITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, item_guid, itemEntry, ii.owner_guid, m.id FROM mail_items mi INNER JOIN mail m ON mi.mail_id = m.id LEFT JOIN item_instance ii ON mi.item_guid = ii.guid WHERE m.receiver = ?", CONNECTION_BOTH);
|
||||
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, houseid, 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, houseid, 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_SEL_EXPIRED_MAIL, "SELECT id, messageType, sender, receiver, has_items, expire_time, stationery, 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_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_ACCOUNT_BY_NAME, "SELECT account FROM characters WHERE name = ?", 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_MATCH_MAKER_RATING, "SELECT matchMakerRating, maxMMR 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_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
|
||||
PrepareStatement(CHAR_SEL_GUILD_MEMBER_EXTENDED, "SELECT g.guildid, g.name, gr.rname, 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);
|
||||
// 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, gender, zone, account FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
||||
|
||||
// Chat channel handling
|
||||
PrepareStatement(CHAR_INS_CHANNEL, "INSERT INTO channels(channelId, name, team, announce, lastUsed) VALUES (?, ?, ?, ?, UNIX_TIMESTAMP())", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_CHANNEL, "UPDATE channels SET announce = ?, password = ?, lastUsed = UNIX_TIMESTAMP() WHERE channelId = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_CHANNEL, "DELETE FROM channels WHERE name = ? AND team = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_CHANNEL_USAGE, "UPDATE channels SET lastUsed = UNIX_TIMESTAMP() WHERE channelId = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_OLD_CHANNELS, "DELETE FROM channels WHERE lastUsed + ? < UNIX_TIMESTAMP()", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_OLD_CHANNELS_BANS, "DELETE cb.* FROM channels_bans cb LEFT JOIN channels cn ON cb.channelId=cn.channelId WHERE cn.channelId IS NULL OR cb.banTime <= UNIX_TIMESTAMP()", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_CHANNEL_BAN, "REPLACE INTO channels_bans VALUES (?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_CHANNEL_BAN, "DELETE FROM channels_bans WHERE channelId = ? AND playerGUID = ?", 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_ASYNC);
|
||||
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_ASYNC);
|
||||
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_SAVE_DATA, "UPDATE instance SET data=? WHERE id=?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_INSTANCE_SAVE_ENCOUNTERMASK, "UPDATE instance SET completedEncounters=? 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);
|
||||
PrepareStatement(CHAR_SEL_GAME_EVENT_CONDITION_SAVE_DATA, "SELECT eventEntry, condition_id, done FROM game_event_condition_save", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_GAME_EVENT_SAVE_DATA, "SELECT eventEntry, state, next_start FROM game_event_save", CONNECTION_SYNCH);
|
||||
|
||||
// Petitions
|
||||
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);
|
||||
|
||||
// Arena teams
|
||||
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_REP_CHARACTER_ARENA_STATS, "REPLACE INTO character_arena_stats (guid, slot, matchMakerRating, maxMMR) 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_ENTRY_POINT, "INSERT INTO character_entry_point (guid, joinX, joinY, joinZ, joinO, joinMapId, taxiPath0, taxiPath1, mountSpell) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_PLAYER_ENTRY_POINT, "DELETE FROM character_entry_point 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, guid FROM corpse WHERE mapId = ? AND instanceId = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_INS_CORPSE, "INSERT INTO corpse (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 guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_CORPSES_FROM_MAP, "DELETE FROM corpse WHERE mapId = ? AND instanceId = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CORPSE_LOCATION, "SELECT mapId, posX, posY, posZ, orientation FROM corpse WHERE guid = ?", 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);
|
||||
|
||||
// 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, type, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp, resolvedBy FROM gm_ticket", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_REP_GM_TICKET, "REPLACE INTO gm_ticket (id, type, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp, resolvedBy) 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);
|
||||
PrepareStatement(CHAR_UPD_PLAYER_GM_TICKETS_ON_CHAR_DELETION, "UPDATE gm_ticket SET type = 2 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_REP_LFG_DATA, "REPLACE 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, skin, face, hairStyle, hairColor, facialStyle, bankSlots, restState, playerFlags, "
|
||||
"map, instance_id, instance_mode_mask, position_x, position_y, position_z, orientation, trans_x, trans_y, trans_z, trans_o, transguid, "
|
||||
"taximask, cinematic, "
|
||||
"totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, "
|
||||
"extra_flags, stable_slots, at_login, zone, "
|
||||
"death_expire_time, taxi_path, arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, "
|
||||
"todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, health, power1, power2, power3, "
|
||||
"power4, power5, power6, power7, latency, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, "
|
||||
"ammoId, knownTitles, actionBars, grantableLevels, innTriggerId, extraBonusTalentCount) VALUES "
|
||||
"(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_CHARACTER, "UPDATE characters SET name=?,race=?,class=?,gender=?,level=?,xp=?,money=?,skin=?,face=?,hairStyle=?,hairColor=?,facialStyle=?,bankSlots=?,restState=?,playerFlags=?,"
|
||||
"map=?,instance_id=?,instance_mode_mask=?,position_x=?,position_y=?,position_z=?,orientation=?,trans_x=?,trans_y=?,trans_z=?,trans_o=?,transguid=?,taximask=?,cinematic=?,totaltime=?,leveltime=?,rest_bonus=?,"
|
||||
"logout_time=?,is_logout_resting=?,resettalents_cost=?,resettalents_time=?,extra_flags=?,stable_slots=?,at_login=?,zone=?,death_expire_time=?,taxi_path=?,"
|
||||
"arenaPoints=?,totalHonorPoints=?,todayHonorPoints=?,yesterdayHonorPoints=?,totalKills=?,todayKills=?,yesterdayKills=?,chosenTitle=?,knownCurrencies=?,"
|
||||
"watchedFaction=?,drunk=?,health=?,power1=?,power2=?,power3=?,power4=?,power5=?,power6=?,power7=?,latency=?,talentGroupsCount=?,activeTalentGroup=?,exploredZones=?,"
|
||||
"equipmentCache=?,ammoId=?,knownTitles=?,actionBars=?,grantableLevels=?,innTriggerId=?,extraBonusTalentCount=?,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 petition_id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_PETITION_SIGNATURE, "INSERT INTO petition_sign (ownerguid, petition_id, 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_REP_GROUP_MEMBER, "REPLACE INTO group_member (guid, memberGuid, memberFlags, subgroup, roles) VALUES(?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_GROUP_MEMBER, "DELETE FROM group_member WHERE memberGuid = ? AND guid = ?", 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_UDP_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_UPD_XP_ACCUMULATIVE, "UPDATE characters SET xp = xp + ? 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_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 = ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_WORLDSTATE, "UPDATE worldstates SET value = ? WHERE entry = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_WORLDSTATE, "INSERT INTO worldstates (entry, value) VALUES (?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE, "DELETE FROM character_instance WHERE instance = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_NOT_EXTENDED, "DELETE FROM character_instance WHERE instance = ? AND extended = 0", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_CHAR_INSTANCE_SET_NOT_EXTENDED, "UPDATE character_instance SET extended = 0 WHERE instance = ?", 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 = ?, extended = 0 WHERE guid = ? AND instance = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_CHAR_INSTANCE_EXTENDED, "UPDATE character_instance SET extended = ? WHERE guid = ? AND instance = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_CHAR_INSTANCE, "INSERT INTO character_instance (guid, instance, permanent, extended) VALUES (?, ?, ?, 0)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_ARENA_LOG_FIGHT, "INSERT INTO log_arena_fights VALUES (?, NOW(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_ARENA_LOG_MEMBERSTATS, "INSERT INTO log_arena_memberstats VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_GENDER_AND_APPEARANCE, "UPDATE characters SET gender = ?, skin = ?, face = ?, hairStyle = ?, hairColor = ?, facialStyle = ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_CHARACTER_SKILL, "DELETE FROM character_skills WHERE guid = ? AND skill = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_ADD_CHARACTER_SOCIAL_FLAGS, "UPDATE character_social SET flags = flags | ? WHERE guid = ? AND friend = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_REM_CHARACTER_SOCIAL_FLAGS, "UPDATE character_social SET flags = flags & ~ ? WHERE guid = ? AND friend = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_CHARACTER_SOCIAL, "REPLACE 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 = '', cinematic = 1 WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_AURA_FROZEN, "SELECT characters.name FROM characters LEFT JOIN character_aura ON (characters.guid = character_aura.guid) WHERE character_aura.spell = 9454", CONNECTION_ASYNC);
|
||||
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);
|
||||
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);
|
||||
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_CUSTOMIZE_INFO, "SELECT name, race, class, gender, at_login FROM characters WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS, "SELECT at_login, knownTitles, money FROM characters WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHAR_AT_LOGIN_TITLES_MONEY, "SELECT at_login, knownTitles, money FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL, "SELECT id, messageType, mailTemplateId, sender, subject, body, money, has_items FROM mail WHERE receiver = ? AND has_items <> 0 AND cod <> 0", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_CHAR_SOCIAL, "SELECT DISTINCT guid FROM character_social WHERE friend = ?", CONNECTION_SYNCH);
|
||||
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, expire_time, deliver_time, money, cod, checked, stationery, mailTemplateId FROM mail WHERE receiver = ? AND deliver_time <= ? ORDER BY id DESC", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_NEXT_MAIL_DELIVERYTIME, "SELECT MIN(deliver_time) FROM mail WHERE receiver = ? AND deliver_time > ? AND (checked & 1) = 0 LIMIT 1", 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_CHAR_INVENTORY_ITEM_BY_ENTRY_AND_OWNER, "SELECT ci.item FROM character_inventory ci INNER JOIN item_instance ii ON ii.guid = ci.item WHERE ii.itemEntry = ? AND ii.owner_guid = ?", 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_INS_CHAR_ACHIEVEMENT_OFFLINE_UPDATES, "INSERT INTO character_achievement_offline_updates (guid, update_type, arg1, arg2, arg3) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHAR_ACHIEVEMENT_OFFLINE_UPDATES, "SELECT update_type, arg1, arg2, arg3 FROM character_achievement_offline_updates WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_CHAR_ACHIEVEMENT_OFFLINE_UPDATES, "DELETE FROM character_achievement_offline_updates WHERE guid = ?", 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_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_MAIL_ITEM_BY_ID, "DELETE FROM mail_items WHERE mail_id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_PETITION, "INSERT INTO petition (petition_id, ownerguid, petitionguid, name, type) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_PETITION_BY_ID, "DELETE FROM petition WHERE petition_id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_ID, "DELETE FROM petition_sign WHERE petition_id = ?", 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_CHAR_RACE, "UPDATE characters SET race = ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_CHAR_SKILL_LANGUAGES, "DELETE FROM character_skills WHERE skill IN (98, 113, 759, 111, 313, 109, 115, 315, 673, 137) AND guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_CHAR_SKILL_LANGUAGE, "INSERT INTO `character_skills` (guid, skill, value, max) VALUES (?, ?, 300, 300)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_CHAR_TAXI_PATH, "UPDATE characters SET taxi_path = '' WHERE guid = ?", CONNECTION_ASYNC);
|
||||
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 guid = ? AND spell = ?", 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_COOLDOWN, "DELETE FROM character_spell_cooldown WHERE guid = ?", 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_UDP_CHAR_HONOR_POINTS, "UPDATE characters SET totalHonorPoints = ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UDP_CHAR_HONOR_POINTS_ACCUMULATIVE, "UPDATE characters SET totalHonorPoints = totalHonorPoints + ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UDP_CHAR_ARENA_POINTS, "UPDATE characters SET arenaPoints = ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UDP_CHAR_ARENA_POINTS_ACCUMULATIVE, "UPDATE characters SET arenaPoints = arenaPoints + ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UDP_CHAR_MONEY, "UPDATE characters SET money = ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UDP_CHAR_MONEY_ACCUMULATIVE, "UPDATE characters SET money = money + ? WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_UPD_CHAR_REMOVE_GHOST, "UPDATE characters SET playerFlags = (playerFlags & (~16)) 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, itemcount5, itemcount6, 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_UDP_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, specMask) 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_SEL_CHAR_STATS, "SELECT maxhealth, strength, agility, stamina, intellect, spirit, armor, attackPower, spellPower, resilience FROM character_stats WHERE guid = ?", CONNECTION_SYNCH);
|
||||
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, "DELETE FROM character_talent WHERE guid = ? AND spell = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_CHAR_TALENT, "INSERT INTO character_talent (guid, spell, specMask) 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 containerGUID, itemid, item_index, count, randomPropertyId, randomSuffix, follow_loot_rules, freeforall, is_blocked, is_counted, is_underthreshold, needs_quest, conditionLootId FROM item_loot_storage", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_DEL_ITEMCONTAINER_SINGLE_ITEM, "DELETE FROM item_loot_storage WHERE containerGUID = ? AND itemid = ? AND count = ? AND item_index = ? LIMIT 1", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_ITEMCONTAINER_SINGLE_ITEM, "INSERT INTO item_loot_storage (containerGUID, itemid, item_index, count, randomPropertyId, randomSuffix, follow_loot_rules, freeforall, is_blocked, is_counted, is_underthreshold, needs_quest, conditionLootId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_ITEMCONTAINER_CONTAINER, "DELETE FROM item_loot_storage WHERE containerGUID = ?", 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_CHAR_PET_IDS, "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_ADD_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = ? AND id = ?", 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_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, category, time FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_PET_AURAS, "DELETE FROM pet_aura WHERE guid = ?", CONNECTION_ASYNC);
|
||||
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_ASYNC);
|
||||
PrepareStatement(CHAR_INS_PET_SPELL_COOLDOWN, "INSERT INTO pet_spell_cooldown (guid, spell, category, time) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
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_ASYNC);
|
||||
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_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHAR_PETS, "SELECT id, entry, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ?", CONNECTION_ASYNC);
|
||||
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_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);
|
||||
PrepareStatement(CHAR_REP_CHAR_PET, "REPLACE INTO character_pet (id, entry, owner, modelid, CreatedBySpell, PetType, level, exp, Reactstate, name, renamed, slot, curhealth, curmana, curhappiness, savetime, abdata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", 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, winner, 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);
|
||||
PrepareStatement(CHAR_SEL_PVPSTATS_BRACKET_MONTH, "SELECT character_guid, COUNT(character_guid) AS count, characters.name as character_name FROM pvpstats_players INNER JOIN pvpstats_battlegrounds ON pvpstats_players.battleground_id = pvpstats_battlegrounds.id AND bracket_id = ? AND MONTH(date) = MONTH(NOW()) AND YEAR(date) = YEAR(NOW()) INNER JOIN characters ON pvpstats_players.character_guid = characters.guid AND characters.deleteDate IS NULL WHERE pvpstats_players.winner = 1 GROUP BY character_guid ORDER BY count(character_guid) DESC LIMIT 0, ?", CONNECTION_SYNCH);
|
||||
|
||||
// Deserter tracker
|
||||
PrepareStatement(CHAR_INS_DESERTER_TRACK, "INSERT INTO battleground_deserters (guid, type, datetime) VALUES (?, ?, NOW())", CONNECTION_ASYNC);
|
||||
|
||||
// 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);
|
||||
|
||||
// Recovery Item
|
||||
PrepareStatement(CHAR_INS_RECOVERY_ITEM, "INSERT INTO recovery_item (Guid, ItemEntry, Count, DeleteDate) VALUES (?, ?, ?, UNIX_TIMESTAMP())", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_RECOVERY_ITEM, "SELECT id, itemEntry, Count, Guid FROM recovery_item WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_RECOVERY_ITEM_LIST, "SELECT id, itemEntry, Count FROM recovery_item WHERE Guid = ? ORDER BY id DESC", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_DEL_RECOVERY_ITEM, "DELETE FROM recovery_item WHERE Guid = ? AND ItemEntry = ? AND Count = ? ORDER BY Id DESC LIMIT 1", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_RECOVERY_ITEM_BY_RECOVERY_ID, "DELETE FROM recovery_item WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_RECOVERY_ITEM_OLD_ITEMS, "SELECT Guid, ItemEntry FROM recovery_item WHERE DeleteDate IS NOT NULL AND DeleteDate < ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_DEL_RECOVERY_ITEM_BY_GUID, "DELETE FROM recovery_item WHERE Guid = ?", CONNECTION_ASYNC);
|
||||
|
||||
PrepareStatement(CHAR_SEL_HONORPOINTS, "SELECT totalHonorPoints FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_SEL_ARENAPOINTS, "SELECT arenaPoints FROM characters WHERE guid = ?", CONNECTION_SYNCH);
|
||||
|
||||
// Character names
|
||||
PrepareStatement(CHAR_INS_RESERVED_PLAYER_NAME, "INSERT IGNORE INTO reserved_name (name) VALUES (?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_PROFANITY_PLAYER_NAME, "INSERT IGNORE INTO profanity_name (name) VALUES (?)", CONNECTION_ASYNC);
|
||||
|
||||
// Character settings
|
||||
PrepareStatement(CHAR_SEL_CHAR_SETTINGS, "SELECT source, data FROM character_settings WHERE guid = ?", CONNECTION_BOTH);
|
||||
PrepareStatement(CHAR_REP_CHAR_SETTINGS, "REPLACE INTO character_settings (guid, source, data) VALUES (?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_CHAR_SETTINGS, "DELETE FROM character_settings WHERE guid = ?", CONNECTION_ASYNC);
|
||||
|
||||
// Instance saved data. Stores the states of gameobjects in instances to be loaded on server start
|
||||
PrepareStatement(CHAR_SELECT_INSTANCE_SAVED_DATA, "SELECT guid, state FROM instance_saved_go_state_data WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_INSERT_INSTANCE_SAVED_DATA, "INSERT INTO instance_saved_go_state_data (id, guid, state) VALUES (?, ?, ?)"
|
||||
"ON DUPLICATE KEY UPDATE state = VALUES(state)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DELETE_INSTANCE_SAVED_DATA, "DELETE FROM instance_saved_go_state_data WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SANITIZE_INSTANCE_SAVED_DATA, "DELETE FROM instance_saved_go_state_data WHERE id NOT IN (SELECT instance.id FROM instance)", CONNECTION_ASYNC);
|
||||
|
||||
// world_state
|
||||
PrepareStatement(CHAR_SEL_WORLD_STATE, "SELECT Id, Data FROM world_state", CONNECTION_SYNCH);
|
||||
PrepareStatement(CHAR_REP_WORLD_STATE, "REPLACE INTO world_state (Id, Data) VALUES(?, ?)", CONNECTION_ASYNC);
|
||||
}
|
||||
|
||||
CharacterDatabaseConnection::CharacterDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
|
||||
{
|
||||
}
|
||||
|
||||
CharacterDatabaseConnection::CharacterDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo)
|
||||
{
|
||||
}
|
||||
|
||||
CharacterDatabaseConnection::~CharacterDatabaseConnection()
|
||||
{
|
||||
}
|
||||
551
src/server/database/Database/Implementation/CharacterDatabase.h
Normal file
551
src/server/database/Database/Implementation/CharacterDatabase.h
Normal file
@@ -0,0 +1,551 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "MySQLConnection.h"
|
||||
|
||||
enum CharacterDatabaseStatements : uint32
|
||||
{
|
||||
/* 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_DATA_BY_NAME,
|
||||
CHAR_SEL_DATA_BY_GUID,
|
||||
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_ENUM,
|
||||
CHAR_SEL_ENUM_DECLINED_NAME,
|
||||
CHAR_SEL_FREE_NAME,
|
||||
CHAR_SEL_CHAR_ZONE,
|
||||
CHAR_SEL_CHARACTER_NAME_DATA,
|
||||
CHAR_SEL_CHAR_POSITION_XYZ,
|
||||
CHAR_SEL_CHAR_POSITION,
|
||||
CHAR_DEL_QUEST_STATUS_DAILY,
|
||||
CHAR_DEL_QUEST_STATUS_WEEKLY,
|
||||
CHAR_DEL_QUEST_STATUS_MONTHLY,
|
||||
CHAR_DEL_QUEST_STATUS_SEASONAL,
|
||||
CHAR_DEL_QUEST_STATUS_DAILY_CHAR,
|
||||
CHAR_DEL_QUEST_STATUS_WEEKLY_CHAR,
|
||||
CHAR_DEL_QUEST_STATUS_MONTHLY_CHAR,
|
||||
CHAR_DEL_QUEST_STATUS_SEASONAL_CHAR,
|
||||
CHAR_DEL_BATTLEGROUND_RANDOM,
|
||||
CHAR_INS_BATTLEGROUND_RANDOM,
|
||||
|
||||
CHAR_SEL_CHARACTER,
|
||||
CHAR_SEL_CHARACTER_AURAS,
|
||||
CHAR_SEL_CHARACTER_SPELL,
|
||||
CHAR_SEL_CHARACTER_QUESTSTATUS,
|
||||
CHAR_SEL_CHARACTER_DAILYQUESTSTATUS,
|
||||
CHAR_SEL_CHARACTER_WEEKLYQUESTSTATUS,
|
||||
CHAR_SEL_CHARACTER_MONTHLYQUESTSTATUS,
|
||||
CHAR_SEL_CHARACTER_SEASONALQUESTSTATUS,
|
||||
CHAR_INS_CHARACTER_DAILYQUESTSTATUS,
|
||||
CHAR_INS_CHARACTER_WEEKLYQUESTSTATUS,
|
||||
CHAR_INS_CHARACTER_MONTHLYQUESTSTATUS,
|
||||
CHAR_INS_CHARACTER_SEASONALQUESTSTATUS,
|
||||
CHAR_SEL_CHARACTER_REPUTATION,
|
||||
CHAR_SEL_CHARACTER_INVENTORY,
|
||||
CHAR_SEL_CHARACTER_ACTIONS,
|
||||
CHAR_SEL_CHARACTER_ACTIONS_SPEC,
|
||||
CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD,
|
||||
CHAR_SEL_CHARACTER_MAILCOUNT_UNREAD_SYNCH,
|
||||
CHAR_SEL_MAIL_SERVER_CHARACTER,
|
||||
CHAR_REP_MAIL_SERVER_CHARACTER,
|
||||
CHAR_SEL_CHARACTER_SOCIALLIST,
|
||||
CHAR_SEL_CHARACTER_HOMEBIND,
|
||||
CHAR_SEL_CHARACTER_SPELLCOOLDOWNS,
|
||||
CHAR_SEL_CHARACTER_DECLINEDNAMES,
|
||||
CHAR_SEL_CHARACTER_ACHIEVEMENTS,
|
||||
CHAR_SEL_CHARACTER_CRITERIAPROGRESS,
|
||||
CHAR_SEL_CHARACTER_EQUIPMENTSETS,
|
||||
CHAR_SEL_CHARACTER_ENTRY_POINT,
|
||||
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_BREW_OF_THE_MONTH,
|
||||
CHAR_REP_BREW_OF_THE_MONTH,
|
||||
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_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_DEL_ACCOUNT_INSTANCE_LOCK_TIMES,
|
||||
CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES,
|
||||
CHAR_SEL_MATCH_MAKER_RATING,
|
||||
CHAR_SEL_CHARACTER_COUNT,
|
||||
CHAR_UPD_NAME_BY_GUID,
|
||||
CHAR_DEL_DECLINED_NAME,
|
||||
|
||||
CHAR_INS_GUILD,
|
||||
CHAR_DEL_GUILD,
|
||||
CHAR_UPD_GUILD_NAME,
|
||||
CHAR_INS_GUILD_MEMBER,
|
||||
CHAR_DEL_GUILD_MEMBER,
|
||||
CHAR_DEL_GUILD_MEMBERS,
|
||||
CHAR_SEL_GUILD_MEMBER_EXTENDED,
|
||||
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_INS_CHANNEL,
|
||||
CHAR_UPD_CHANNEL,
|
||||
CHAR_DEL_CHANNEL,
|
||||
CHAR_UPD_CHANNEL_USAGE,
|
||||
CHAR_DEL_OLD_CHANNELS,
|
||||
CHAR_DEL_OLD_CHANNELS_BANS,
|
||||
CHAR_INS_CHANNEL_BAN,
|
||||
CHAR_DEL_CHANNEL_BAN,
|
||||
|
||||
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_SAVE_DATA,
|
||||
CHAR_UPD_INSTANCE_SAVE_ENCOUNTERMASK,
|
||||
|
||||
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_SEL_GAME_EVENT_CONDITION_SAVE_DATA,
|
||||
CHAR_SEL_GAME_EVENT_SAVE_DATA,
|
||||
|
||||
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_REP_CHARACTER_ARENA_STATS,
|
||||
CHAR_SEL_PLAYER_ARENA_TEAMS,
|
||||
CHAR_UPD_ARENA_TEAM_NAME,
|
||||
|
||||
CHAR_DEL_ALL_PETITION_SIGNATURES,
|
||||
CHAR_DEL_PETITION_SIGNATURE,
|
||||
|
||||
CHAR_INS_PLAYER_ENTRY_POINT,
|
||||
CHAR_DEL_PLAYER_ENTRY_POINT,
|
||||
|
||||
CHAR_INS_PLAYER_HOMEBIND,
|
||||
CHAR_UPD_PLAYER_HOMEBIND,
|
||||
CHAR_DEL_PLAYER_HOMEBIND,
|
||||
|
||||
CHAR_SEL_CORPSES,
|
||||
CHAR_INS_CORPSE,
|
||||
CHAR_DEL_CORPSE,
|
||||
CHAR_DEL_CORPSES_FROM_MAP,
|
||||
CHAR_SEL_CORPSE_LOCATION,
|
||||
|
||||
CHAR_SEL_CREATURE_RESPAWNS,
|
||||
CHAR_REP_CREATURE_RESPAWN,
|
||||
CHAR_DEL_CREATURE_RESPAWN,
|
||||
CHAR_DEL_CREATURE_RESPAWN_BY_INSTANCE,
|
||||
|
||||
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_UPD_PLAYER_GM_TICKETS_ON_CHAR_DELETION,
|
||||
|
||||
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_REP_GROUP_MEMBER,
|
||||
CHAR_DEL_GROUP_MEMBER,
|
||||
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_UDP_RESTORE_DELETE_INFO,
|
||||
CHAR_UPD_ZONE,
|
||||
CHAR_UPD_LEVEL,
|
||||
CHAR_UPD_XP_ACCUMULATIVE,
|
||||
CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA,
|
||||
CHAR_DEL_INVALID_ACHIEVMENT,
|
||||
CHAR_INS_ADDON,
|
||||
CHAR_DEL_INVALID_PET_SPELL,
|
||||
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,
|
||||
CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_NOT_EXTENDED,
|
||||
CHAR_UPD_CHAR_INSTANCE_SET_NOT_EXTENDED,
|
||||
CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID,
|
||||
CHAR_UPD_CHAR_INSTANCE,
|
||||
CHAR_UPD_CHAR_INSTANCE_EXTENDED,
|
||||
CHAR_INS_CHAR_INSTANCE,
|
||||
CHAR_INS_ARENA_LOG_FIGHT,
|
||||
CHAR_INS_ARENA_LOG_MEMBERSTATS,
|
||||
CHAR_UPD_GENDER_AND_APPEARANCE,
|
||||
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_REP_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_CUSTOMIZE_INFO,
|
||||
CHAR_SEL_CHAR_RACE_OR_FACTION_CHANGE_INFOS,
|
||||
CHAR_SEL_CHAR_AT_LOGIN_TITLES_MONEY,
|
||||
CHAR_SEL_CHAR_COD_ITEM_MAIL,
|
||||
CHAR_SEL_CHAR_SOCIAL,
|
||||
CHAR_SEL_CHAR_OLD_CHARS,
|
||||
CHAR_SEL_ARENA_TEAM_ID_BY_PLAYER_GUID,
|
||||
CHAR_SEL_MAIL,
|
||||
CHAR_SEL_NEXT_MAIL_DELIVERYTIME,
|
||||
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_CHAR_INVENTORY_ITEM_BY_ENTRY_AND_OWNER,
|
||||
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_INS_CHAR_ACHIEVEMENT_OFFLINE_UPDATES,
|
||||
CHAR_SEL_CHAR_ACHIEVEMENT_OFFLINE_UPDATES,
|
||||
CHAR_DEL_CHAR_ACHIEVEMENT_OFFLINE_UPDATES,
|
||||
CHAR_DEL_CHAR_REPUTATION_BY_FACTION,
|
||||
CHAR_INS_CHAR_REPUTATION_BY_FACTION,
|
||||
CHAR_UPD_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_MAIL_ITEM_BY_ID,
|
||||
CHAR_INS_PETITION,
|
||||
CHAR_DEL_PETITION_BY_ID,
|
||||
CHAR_DEL_PETITION_SIGNATURE_BY_ID,
|
||||
CHAR_DEL_CHAR_DECLINED_NAME,
|
||||
CHAR_INS_CHAR_DECLINED_NAME,
|
||||
CHAR_UPD_CHAR_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_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_UDP_CHAR_HONOR_POINTS,
|
||||
CHAR_UDP_CHAR_HONOR_POINTS_ACCUMULATIVE,
|
||||
CHAR_UDP_CHAR_ARENA_POINTS,
|
||||
CHAR_UDP_CHAR_ARENA_POINTS_ACCUMULATIVE,
|
||||
CHAR_UDP_CHAR_MONEY,
|
||||
CHAR_UDP_CHAR_MONEY_ACCUMULATIVE,
|
||||
CHAR_UPD_CHAR_REMOVE_GHOST, // pussywizard
|
||||
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_UDP_CHAR_SKILLS,
|
||||
CHAR_INS_CHAR_SPELL,
|
||||
CHAR_DEL_CHAR_STATS,
|
||||
CHAR_INS_CHAR_STATS,
|
||||
CHAR_SEL_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,
|
||||
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_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_PETS,
|
||||
CHAR_SEL_CHAR_PET_IDS,
|
||||
CHAR_DEL_CHAR_PET_DECLINEDNAME,
|
||||
CHAR_ADD_CHAR_PET_DECLINEDNAME,
|
||||
CHAR_SEL_PET_DECLINED_NAME,
|
||||
CHAR_UPD_CHAR_PET_NAME,
|
||||
CHAR_UPD_CHAR_PET_SLOT_BY_ID,
|
||||
CHAR_DEL_CHAR_PET_BY_ID,
|
||||
CHAR_DEL_CHAR_PET_BY_SLOT,
|
||||
CHAR_REP_CHAR_PET,
|
||||
|
||||
CHAR_SEL_ITEMCONTAINER_ITEMS,
|
||||
CHAR_DEL_ITEMCONTAINER_SINGLE_ITEM,
|
||||
CHAR_INS_ITEMCONTAINER_SINGLE_ITEM,
|
||||
CHAR_DEL_ITEMCONTAINER_CONTAINER,
|
||||
|
||||
CHAR_SEL_PVPSTATS_MAXID,
|
||||
CHAR_INS_PVPSTATS_BATTLEGROUND,
|
||||
CHAR_INS_PVPSTATS_PLAYER,
|
||||
CHAR_SEL_PVPSTATS_FACTIONS_OVERALL,
|
||||
CHAR_SEL_PVPSTATS_BRACKET_MONTH,
|
||||
|
||||
CHAR_INS_DESERTER_TRACK,
|
||||
|
||||
CHAR_INS_QUEST_TRACK,
|
||||
CHAR_UPD_QUEST_TRACK_GM_COMPLETE,
|
||||
CHAR_UPD_QUEST_TRACK_COMPLETE_TIME,
|
||||
CHAR_UPD_QUEST_TRACK_ABANDON_TIME,
|
||||
|
||||
CHAR_INS_RECOVERY_ITEM,
|
||||
CHAR_SEL_RECOVERY_ITEM,
|
||||
CHAR_SEL_RECOVERY_ITEM_LIST,
|
||||
CHAR_DEL_RECOVERY_ITEM,
|
||||
CHAR_DEL_RECOVERY_ITEM_BY_RECOVERY_ID,
|
||||
CHAR_SEL_RECOVERY_ITEM_OLD_ITEMS,
|
||||
CHAR_DEL_RECOVERY_ITEM_BY_GUID,
|
||||
|
||||
CHAR_SEL_HONORPOINTS,
|
||||
CHAR_SEL_ARENAPOINTS,
|
||||
|
||||
CHAR_INS_RESERVED_PLAYER_NAME,
|
||||
CHAR_INS_PROFANITY_PLAYER_NAME,
|
||||
|
||||
CHAR_SEL_CHAR_SETTINGS,
|
||||
CHAR_REP_CHAR_SETTINGS,
|
||||
CHAR_DEL_CHAR_SETTINGS,
|
||||
|
||||
CHAR_SELECT_INSTANCE_SAVED_DATA,
|
||||
CHAR_INSERT_INSTANCE_SAVED_DATA,
|
||||
CHAR_DELETE_INSTANCE_SAVED_DATA,
|
||||
CHAR_SANITIZE_INSTANCE_SAVED_DATA,
|
||||
|
||||
CHAR_SEL_WORLD_STATE,
|
||||
CHAR_REP_WORLD_STATE,
|
||||
|
||||
MAX_CHARACTERDATABASE_STATEMENTS
|
||||
};
|
||||
|
||||
class AC_DATABASE_API CharacterDatabaseConnection : public MySQLConnection
|
||||
{
|
||||
public:
|
||||
typedef CharacterDatabaseStatements Statements;
|
||||
|
||||
//- Constructors for sync and async connections
|
||||
CharacterDatabaseConnection(MySQLConnectionInfo& connInfo);
|
||||
CharacterDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo);
|
||||
~CharacterDatabaseConnection() override;
|
||||
|
||||
//- Loads database type specific prepared statements
|
||||
void DoPrepareStatements() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
161
src/server/database/Database/Implementation/LoginDatabase.cpp
Normal file
161
src/server/database/Database/Implementation/LoginDatabase.cpp
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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"
|
||||
#include "MySQLPreparedStatement.h"
|
||||
|
||||
void LoginDatabaseConnection::DoPrepareStatements()
|
||||
{
|
||||
if (!m_reconnecting)
|
||||
m_stmts.resize(MAX_LOGINDATABASE_STATEMENTS);
|
||||
|
||||
PrepareStatement(LOGIN_SEL_LOGONCHALLENGE,
|
||||
"SELECT a.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins, "
|
||||
"ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, ab.unbandate = ab.bandate, "
|
||||
"ipb.unbandate > UNIX_TIMESTAMP() OR ipb.unbandate = ipb.bandate, ipb.unbandate = ipb.bandate, "
|
||||
"aa.gmlevel, a.totp_secret, a.salt, a.verifier "
|
||||
"FROM account a "
|
||||
"LEFT JOIN account_access aa ON a.id = aa.id "
|
||||
"LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 "
|
||||
"LEFT JOIN ip_banned ipb ON ipb.ip = ? "
|
||||
"WHERE a.username = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_RECONNECTCHALLENGE,
|
||||
"SELECT a.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins, "
|
||||
"ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, ab.unbandate = ab.bandate, "
|
||||
"ipb.unbandate > UNIX_TIMESTAMP() OR ipb.unbandate = ipb.bandate, ipb.unbandate = ipb.bandate, "
|
||||
"aa.gmlevel, a.session_key "
|
||||
"FROM account a "
|
||||
"LEFT JOIN account_access aa ON a.id = aa.id "
|
||||
"LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 "
|
||||
"LEFT JOIN ip_banned ipb ON ipb.ip = ? "
|
||||
"WHERE a.username = ? AND a.session_key IS NOT NULL", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.session_key, a.last_ip, a.locked, a.lock_country, a.expansion, a.Flags, a.mutetime, a.locale, a.recruiter, a.os, a.totaltime, "
|
||||
"aa.gmlevel, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, r.id FROM account a LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID IN (-1, ?) "
|
||||
"LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account r ON a.id = r.recruiter WHERE a.username = ? "
|
||||
"AND a.session_key IS NOT NULL ORDER BY aa.RealmID DESC LIMIT 1", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_IP_INFO, "SELECT unbandate > UNIX_TIMESTAMP() OR unbandate = bandate AS banned, NULL as country FROM ip_banned WHERE ip = ?", CONNECTION_ASYNC);
|
||||
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_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_IP_BANNED, "SELECT * FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_INS_IP_AUTO_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'realmd', '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()+?, 'realmd', 'Failed login autoban', 1)", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_DEL_ACCOUNT_BANNED, "DELETE FROM account_banned WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_LOGON, "UPDATE account SET salt = ?, verifier = ? WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_LOGONPROOF, "UPDATE account SET session_key = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_ASYNC);
|
||||
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_LIST_BY_EMAIL, "SELECT id, username FROM account WHERE email = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_NUM_CHARS_ON_REALM, "SELECT numchars FROM realmcharacters WHERE realmid = ? AND acctid= ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_REALM_CHARACTER_COUNTS, "SELECT realmid, numchars FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_IP, "SELECT id, username FROM account WHERE last_ip = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_BY_ID, "SELECT 1 FROM account WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_INS_IP_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?)", CONNECTION_ASYNC);
|
||||
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, "DELETE FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_REP_REALM_CHARACTERS, "REPLACE 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, salt, verifier, expansion, reg_mail, email, joindate) VALUES(?, ?, ?, ?, ?, ?, NOW())", CONNECTION_ASYNC);
|
||||
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_COUNTRY, "UPDATE account SET lock_country = ? WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_EMAIL, "UPDATE account SET email = ? WHERE id = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_UPD_USERNAME, "UPDATE account SET username = ? 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 = ? 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 salt, verifier FROM account WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_CHECK_PASSWORD_BY_NAME, "SELECT salt, verifier FROM account WHERE username = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_FLAG, "SELECT Flags FROM account WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_UPD_SET_ACCOUNT_FLAG, "UPDATE account SET Flags = ? WHERE id = ?", CONNECTION_ASYNC);
|
||||
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 = ? ORDER BY a.last_ip", CONNECTION_SYNCH); // Only used in ".account onlinelist" command
|
||||
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_AUTOBROADCAST, "SELECT id, weight, text FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_AUTOBROADCAST_LOCALIZED, "SELECT id, locale, text FROM autobroadcast_locale WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_MOTD, "SELECT text FROM motd WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_SEL_MOTD_LOCALE, "SELECT locale, text FROM motd_localized WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_REP_MOTD, "REPLACE INTO motd (realmid, text) VALUES (?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_REP_MOTD_LOCALE, "REPLACE INTO motd_localized (realmid, locale, text) VALUES (?, ?, ?)", 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);
|
||||
PrepareStatement(LOGIN_DEL_ACCOUNT_MUTED, "DELETE FROM account_muted WHERE guid = ?", CONNECTION_ASYNC);
|
||||
// 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 (?, 0, 1, ?, ?, unix_timestamp(NOW()), NOW())", CONNECTION_ASYNC);
|
||||
|
||||
// DB logging
|
||||
PrepareStatement(LOGIN_INS_LOG, "INSERT INTO logs (time, realm, type, level, string) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
|
||||
// TOTP
|
||||
PrepareStatement(LOGIN_SEL_SECRET_DIGEST, "SELECT digest FROM secret_digest WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_INS_SECRET_DIGEST, "INSERT INTO secret_digest (id, digest) VALUES (?,?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(LOGIN_DEL_SECRET_DIGEST, "DELETE FROM secret_digest WHERE id = ?", CONNECTION_ASYNC);
|
||||
|
||||
PrepareStatement(LOGIN_SEL_ACCOUNT_TOTP_SECRET, "SELECT totp_secret FROM account WHERE id = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(LOGIN_UPD_ACCOUNT_TOTP_SECRET, "UPDATE account SET totp_secret = ? WHERE id = ?", CONNECTION_ASYNC);
|
||||
|
||||
PrepareStatement(LOGIN_INS_UPTIME, "INSERT INTO uptime (realmid, starttime, uptime, revision) VALUES (?, ?, 0, ?)", CONNECTION_ASYNC);
|
||||
}
|
||||
|
||||
LoginDatabaseConnection::LoginDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
|
||||
{
|
||||
}
|
||||
|
||||
LoginDatabaseConnection::LoginDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo)
|
||||
{
|
||||
}
|
||||
|
||||
LoginDatabaseConnection::~LoginDatabaseConnection()
|
||||
{
|
||||
}
|
||||
146
src/server/database/Database/Implementation/LoginDatabase.h
Normal file
146
src/server/database/Database/Implementation/LoginDatabase.h
Normal file
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "MySQLConnection.h"
|
||||
|
||||
enum LoginDatabaseStatements : uint32
|
||||
{
|
||||
/* 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_INFO,
|
||||
LOGIN_SEL_IP_BANNED,
|
||||
LOGIN_INS_IP_AUTO_BANNED,
|
||||
LOGIN_SEL_ACCOUNT_BANNED,
|
||||
LOGIN_SEL_ACCOUNT_BANNED_ALL,
|
||||
LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME,
|
||||
LOGIN_INS_ACCOUNT_AUTO_BANNED,
|
||||
LOGIN_DEL_ACCOUNT_BANNED,
|
||||
LOGIN_UPD_LOGON,
|
||||
LOGIN_UPD_LOGONPROOF,
|
||||
LOGIN_SEL_LOGONCHALLENGE,
|
||||
LOGIN_SEL_RECONNECTCHALLENGE,
|
||||
LOGIN_UPD_FAILEDLOGINS,
|
||||
LOGIN_SEL_FAILEDLOGINS,
|
||||
LOGIN_SEL_ACCOUNT_ID_BY_NAME,
|
||||
LOGIN_SEL_ACCOUNT_LIST_BY_NAME,
|
||||
LOGIN_SEL_ACCOUNT_INFO_BY_NAME,
|
||||
LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL,
|
||||
LOGIN_SEL_NUM_CHARS_ON_REALM,
|
||||
LOGIN_SEL_REALM_CHARACTER_COUNTS,
|
||||
LOGIN_SEL_ACCOUNT_BY_IP,
|
||||
LOGIN_INS_IP_BANNED,
|
||||
LOGIN_DEL_IP_NOT_BANNED,
|
||||
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,
|
||||
LOGIN_REP_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_COUNTRY,
|
||||
LOGIN_UPD_EMAIL,
|
||||
LOGIN_UPD_USERNAME,
|
||||
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_ACCOUNT_FLAG,
|
||||
LOGIN_UPD_SET_ACCOUNT_FLAG,
|
||||
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_AUTOBROADCAST,
|
||||
LOGIN_SEL_AUTOBROADCAST_LOCALIZED,
|
||||
LOGIN_SEL_MOTD,
|
||||
LOGIN_SEL_MOTD_LOCALE,
|
||||
LOGIN_REP_MOTD,
|
||||
LOGIN_REP_MOTD_LOCALE,
|
||||
LOGIN_SEL_LAST_ATTEMPT_IP,
|
||||
LOGIN_SEL_LAST_IP,
|
||||
LOGIN_INS_ALDL_IP_LOGGING,
|
||||
LOGIN_INS_FACL_IP_LOGGING,
|
||||
LOGIN_INS_CHAR_IP_LOGGING,
|
||||
LOGIN_INS_FALP_IP_LOGGING,
|
||||
|
||||
LOGIN_INS_ACCOUNT_MUTE,
|
||||
LOGIN_SEL_ACCOUNT_MUTE_INFO,
|
||||
LOGIN_DEL_ACCOUNT_MUTED,
|
||||
|
||||
LOGIN_INS_LOG,
|
||||
|
||||
LOGIN_SEL_SECRET_DIGEST,
|
||||
LOGIN_INS_SECRET_DIGEST,
|
||||
LOGIN_DEL_SECRET_DIGEST,
|
||||
|
||||
LOGIN_SEL_ACCOUNT_TOTP_SECRET,
|
||||
LOGIN_UPD_ACCOUNT_TOTP_SECRET,
|
||||
|
||||
LOGIN_INS_UPTIME,
|
||||
|
||||
MAX_LOGINDATABASE_STATEMENTS
|
||||
};
|
||||
|
||||
class AC_DATABASE_API LoginDatabaseConnection : public MySQLConnection
|
||||
{
|
||||
public:
|
||||
typedef LoginDatabaseStatements Statements;
|
||||
|
||||
//- Constructors for sync and async connections
|
||||
LoginDatabaseConnection(MySQLConnectionInfo& connInfo);
|
||||
LoginDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo);
|
||||
~LoginDatabaseConnection() override;
|
||||
|
||||
//- Loads database type specific prepared statements
|
||||
void DoPrepareStatements() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
126
src/server/database/Database/Implementation/WorldDatabase.cpp
Normal file
126
src/server/database/Database/Implementation/WorldDatabase.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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"
|
||||
#include "MySQLPreparedStatement.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 CreatureID, 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, event_param5, event_param6, action_type, action_param1, action_param2, action_param3, action_param4, action_param5, action_param6, target_type, target_param1, target_param2, target_param3, target_param4, 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, orientation, delay 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 graveyard_zone (ID, GhostZone, Faction) VALUES (?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_DEL_GRAVEYARD_ZONE, "DELETE FROM graveyard_zone WHERE ID = ? AND GhostZone = ? AND Faction = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_INS_GAME_TELE, "INSERT INTO game_tele (id, position_x, position_y, position_z, orientation, map, name) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_DEL_GAME_TELE, "DELETE FROM game_tele WHERE name = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_INS_NPC_VENDOR, "INSERT INTO npc_vendor (entry, item, maxcount, incrtime, extendedcost) VALUES(?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
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_WANDER_DISTANCE, "UPDATE creature SET wander_distance = ?, 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, security, help FROM command", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, scale, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId 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 id1 = ? OR id2 = ? OR id3 = ?", 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)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_CREATURE_NEAREST, "SELECT guid, id1, id2, id3, 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)) <= ? AND (phaseMask & ?) <> 0 ORDER BY order_", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_INS_CREATURE, "INSERT INTO creature (guid, id1, id2, id3, map, spawnMask, phaseMask, equipment_id, position_x, position_y, position_z, orientation, spawntimesecs, wander_distance, currentwaypoint, curhealth, curmana, MovementType, npcflag, unit_flags, dynamicflags) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENTS, "SELECT eventEntry, UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), occurence, length, holiday, holidayStage, description, world_event, announce FROM game_event", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_PREREQUISITE_DATA, "SELECT eventEntry, prerequisite_event FROM game_event_prerequisite", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_CREATURE_DATA, "SELECT guid, eventEntry FROM game_event_creature", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_GAMEOBJECT_DATA, "SELECT guid, eventEntry FROM game_event_gameobject", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_MODEL_EQUIPMENT_DATA, "SELECT creature.guid, creature.id1, creature.id2, creature.id3, game_event_model_equip.eventEntry, game_event_model_equip.modelid, game_event_model_equip.equipment_id FROM creature JOIN game_event_model_equip ON creature.guid=game_event_model_equip.guid", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_QUEST_DATA, "SELECT id, quest, eventEntry FROM game_event_creature_quest", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_GAMEOBJECT_QUEST_DATA, "SELECT id, quest, eventEntry FROM game_event_gameobject_quest", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_QUEST_CONDITION_DATA, "SELECT quest, eventEntry, condition_id, num FROM game_event_quest_condition", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_CONDITION_DATA, "SELECT eventEntry, condition_id, req_num, max_world_state_field, done_world_state_field FROM game_event_condition", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_NPC_FLAGS, "SELECT guid, eventEntry, npcflag FROM game_event_npcflag", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_QUEST_SEASONAL_RELATIONS, "SELECT questId, eventEntry FROM game_event_seasonal_questrelation", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_BATTLEGROUND_DATA, "SELECT eventEntry, bgflag FROM game_event_battleground_holiday", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_POOL_DATA, "SELECT pool_template.entry, game_event_pool.eventEntry FROM pool_template JOIN game_event_pool ON pool_template.entry = game_event_pool.pool_entry", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_SEL_GAME_EVENT_ARENA_SEASON, "SELECT eventEntry FROM game_event_arena_seasons WHERE season = ?", CONNECTION_SYNCH);
|
||||
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_SEL_GAME_EVENT_NPC_VENDOR, "SELECT eventEntry, guid, item, maxcount, incrtime, ExtendedCost FROM game_event_npc_vendor ORDER BY guid, slot ASC", CONNECTION_SYNCH);
|
||||
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);
|
||||
PrepareStatement(WORLD_INS_GAMEOBJECT_ADDON, "INSERT INTO gameobject_addon (guid, invisibilityType, invisibilityValue) VALUES (?, 0, 0)", CONNECTION_ASYNC);
|
||||
// 0: uint8
|
||||
PrepareStatement(WORLD_SEL_REQ_XP, "SELECT Experience FROM player_xp_for_level WHERE Level = ?", CONNECTION_SYNCH);
|
||||
PrepareStatement(WORLD_UPD_VERSION, "UPDATE version SET core_version = ?, core_revision = ?", CONNECTION_ASYNC);
|
||||
}
|
||||
|
||||
WorldDatabaseConnection::WorldDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo)
|
||||
{
|
||||
}
|
||||
|
||||
WorldDatabaseConnection::WorldDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo) : MySQLConnection(q, connInfo)
|
||||
{
|
||||
}
|
||||
|
||||
WorldDatabaseConnection::~WorldDatabaseConnection()
|
||||
{
|
||||
}
|
||||
137
src/server/database/Database/Implementation/WorldDatabase.h
Normal file
137
src/server/database/Database/Implementation/WorldDatabase.h
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "MySQLConnection.h"
|
||||
|
||||
enum WorldDatabaseStatements : uint32
|
||||
{
|
||||
/* 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_WANDER_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_SEL_GAME_EVENTS,
|
||||
WORLD_SEL_GAME_EVENT_PREREQUISITE_DATA,
|
||||
WORLD_SEL_GAME_EVENT_CREATURE_DATA,
|
||||
WORLD_SEL_GAME_EVENT_GAMEOBJECT_DATA,
|
||||
WORLD_SEL_GAME_EVENT_MODEL_EQUIPMENT_DATA,
|
||||
WORLD_SEL_GAME_EVENT_QUEST_DATA,
|
||||
WORLD_SEL_GAME_EVENT_GAMEOBJECT_QUEST_DATA,
|
||||
WORLD_SEL_GAME_EVENT_QUEST_CONDITION_DATA,
|
||||
WORLD_SEL_GAME_EVENT_CONDITION_DATA,
|
||||
WORLD_SEL_GAME_EVENT_NPC_FLAGS,
|
||||
WORLD_SEL_GAME_EVENT_QUEST_SEASONAL_RELATIONS,
|
||||
WORLD_SEL_GAME_EVENT_BATTLEGROUND_DATA,
|
||||
WORLD_SEL_GAME_EVENT_POOL_DATA,
|
||||
WORLD_SEL_GAME_EVENT_ARENA_SEASON,
|
||||
WORLD_DEL_GAME_EVENT_CREATURE,
|
||||
WORLD_DEL_GAME_EVENT_MODEL_EQUIP,
|
||||
WORLD_SEL_GAME_EVENT_NPC_VENDOR,
|
||||
WORLD_INS_GAMEOBJECT,
|
||||
WORLD_SEL_DISABLES,
|
||||
WORLD_INS_DISABLES,
|
||||
WORLD_DEL_DISABLES,
|
||||
WORLD_UPD_CREATURE_ZONE_AREA_DATA,
|
||||
WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA,
|
||||
WORLD_SEL_REQ_XP,
|
||||
WORLD_INS_GAMEOBJECT_ADDON,
|
||||
WORLD_UPD_VERSION,
|
||||
|
||||
MAX_WORLDDATABASE_STATEMENTS
|
||||
};
|
||||
|
||||
class AC_DATABASE_API WorldDatabaseConnection : public MySQLConnection
|
||||
{
|
||||
public:
|
||||
typedef WorldDatabaseStatements Statements;
|
||||
|
||||
//- Constructors for sync and async connections
|
||||
WorldDatabaseConnection(MySQLConnectionInfo& connInfo);
|
||||
WorldDatabaseConnection(ProducerConsumerQueue<SQLOperation*>* q, MySQLConnectionInfo& connInfo);
|
||||
~WorldDatabaseConnection() override;
|
||||
|
||||
//- Loads database type specific prepared statements
|
||||
void DoPrepareStatements() override;
|
||||
};
|
||||
|
||||
#endif
|
||||
646
src/server/database/Database/MySQLConnection.cpp
Normal file
646
src/server/database/Database/MySQLConnection.cpp
Normal file
@@ -0,0 +1,646 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "DatabaseWorker.h"
|
||||
#include "Log.h"
|
||||
#include "MySQLHacks.h"
|
||||
#include "MySQLPreparedStatement.h"
|
||||
#include "PreparedStatement.h"
|
||||
#include "QueryResult.h"
|
||||
#include "StringConvert.h"
|
||||
#include "Timer.h"
|
||||
#include "Tokenize.h"
|
||||
#include "Transaction.h"
|
||||
#include <errmsg.h>
|
||||
#include <mysql.h>
|
||||
#include <mysqld_error.h>
|
||||
|
||||
MySQLConnectionInfo::MySQLConnectionInfo(std::string_view infoString)
|
||||
{
|
||||
std::vector<std::string_view> tokens = Acore::Tokenize(infoString, ';', true);
|
||||
|
||||
if (tokens.size() != 5 && tokens.size() != 6)
|
||||
return;
|
||||
|
||||
host.assign(tokens.at(0));
|
||||
port_or_socket.assign(tokens.at(1));
|
||||
user.assign(tokens.at(2));
|
||||
password.assign(tokens.at(3));
|
||||
database.assign(tokens.at(4));
|
||||
|
||||
if (tokens.size() == 6)
|
||||
ssl.assign(tokens.at(5));
|
||||
}
|
||||
|
||||
MySQLConnection::MySQLConnection(MySQLConnectionInfo& connInfo) :
|
||||
m_reconnecting(false),
|
||||
m_prepareError(false),
|
||||
m_Mysql(nullptr),
|
||||
m_queue(nullptr),
|
||||
m_connectionInfo(connInfo),
|
||||
m_connectionFlags(CONNECTION_SYNCH) { }
|
||||
|
||||
MySQLConnection::MySQLConnection(ProducerConsumerQueue<SQLOperation*>* queue, MySQLConnectionInfo& connInfo) :
|
||||
m_reconnecting(false),
|
||||
m_prepareError(false),
|
||||
m_Mysql(nullptr),
|
||||
m_queue(queue),
|
||||
m_connectionInfo(connInfo),
|
||||
m_connectionFlags(CONNECTION_ASYNC)
|
||||
{
|
||||
m_worker = std::make_unique<DatabaseWorker>(m_queue, this);
|
||||
}
|
||||
|
||||
MySQLConnection::~MySQLConnection()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
void MySQLConnection::Close()
|
||||
{
|
||||
// Stop the worker thread before the statements are cleared
|
||||
m_worker.reset();
|
||||
m_stmts.clear();
|
||||
|
||||
if (m_Mysql)
|
||||
{
|
||||
mysql_close(m_Mysql);
|
||||
m_Mysql = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 MySQLConnection::Open()
|
||||
{
|
||||
MYSQL* mysqlInit = mysql_init(nullptr);
|
||||
if (!mysqlInit)
|
||||
{
|
||||
LOG_ERROR("sql.driver", "Could not initialize Mysql connection to database `{}`", m_connectionInfo.database);
|
||||
return CR_UNKNOWN_ERROR;
|
||||
}
|
||||
|
||||
uint32 port;
|
||||
char const* unix_socket;
|
||||
|
||||
mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8");
|
||||
|
||||
#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 = *Acore::StringTo<uint32>(m_connectionInfo.port_or_socket);
|
||||
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 = *Acore::StringTo<uint32>(m_connectionInfo.port_or_socket);
|
||||
unix_socket = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_connectionInfo.ssl != "")
|
||||
{
|
||||
mysql_ssl_mode opt_use_ssl = SSL_MODE_DISABLED;
|
||||
if (m_connectionInfo.ssl == "ssl")
|
||||
{
|
||||
opt_use_ssl = SSL_MODE_REQUIRED;
|
||||
}
|
||||
|
||||
mysql_options(mysqlInit, MYSQL_OPT_SSL_MODE, (char const*)&opt_use_ssl);
|
||||
}
|
||||
|
||||
m_Mysql = reinterpret_cast<MySQLHandle*>(mysql_real_connect(mysqlInit, m_connectionInfo.host.c_str(), m_connectionInfo.user.c_str(),
|
||||
m_connectionInfo.password.c_str(), m_connectionInfo.database.c_str(), port, unix_socket, 0));
|
||||
|
||||
if (m_Mysql)
|
||||
{
|
||||
if (!m_reconnecting)
|
||||
{
|
||||
LOG_INFO("sql.sql", "MySQL client library: {}", mysql_get_client_info());
|
||||
LOG_INFO("sql.sql", "MySQL server ver: {} ", mysql_get_server_info(m_Mysql));
|
||||
}
|
||||
|
||||
LOG_INFO("sql.sql", "Connected to MySQL database at {}", m_connectionInfo.host);
|
||||
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, "utf8mb4");
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("sql.driver", "Could not connect to MySQL database at {}: {}", m_connectionInfo.host, mysql_error(mysqlInit));
|
||||
uint32 errorCode = mysql_errno(mysqlInit);
|
||||
mysql_close(mysqlInit);
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
|
||||
bool MySQLConnection::PrepareStatements()
|
||||
{
|
||||
DoPrepareStatements();
|
||||
return !m_prepareError;
|
||||
}
|
||||
|
||||
bool MySQLConnection::Execute(std::string_view sql)
|
||||
{
|
||||
if (!m_Mysql)
|
||||
return false;
|
||||
|
||||
{
|
||||
uint32 _s = getMSTime();
|
||||
|
||||
if (mysql_query(m_Mysql, std::string(sql).c_str()))
|
||||
{
|
||||
uint32 lErrno = mysql_errno(m_Mysql);
|
||||
|
||||
LOG_INFO("sql.sql", "SQL: {}", sql);
|
||||
LOG_ERROR("sql.sql", "[{}] {}", lErrno, mysql_error(m_Mysql));
|
||||
|
||||
if (_HandleMySQLErrno(lErrno, mysql_error(m_Mysql))) // If it returns true, an error was handled successfully (i.e. reconnection)
|
||||
return Execute(sql); // Try again
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
LOG_DEBUG("sql.sql", "[{} ms] SQL: {}", getMSTimeDiff(_s, getMSTime()), sql);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MySQLConnection::Execute(PreparedStatementBase* stmt)
|
||||
{
|
||||
if (!m_Mysql)
|
||||
return false;
|
||||
|
||||
uint32 index = stmt->GetIndex();
|
||||
|
||||
MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index);
|
||||
ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query
|
||||
|
||||
m_mStmt->BindParameters(stmt);
|
||||
|
||||
MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT();
|
||||
MYSQL_BIND* msql_BIND = m_mStmt->GetBind();
|
||||
|
||||
uint32 _s = getMSTime();
|
||||
|
||||
#if MYSQL_VERSION_ID >= 80300
|
||||
if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr))
|
||||
#else
|
||||
if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
|
||||
#endif
|
||||
{
|
||||
uint32 lErrno = mysql_errno(m_Mysql);
|
||||
LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT));
|
||||
|
||||
if (_HandleMySQLErrno(lErrno, mysql_stmt_error(msql_STMT))) // If it returns true, an error was handled successfully (i.e. reconnection)
|
||||
return Execute(stmt); // Try again
|
||||
|
||||
m_mStmt->ClearParameters();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mysql_stmt_execute(msql_STMT))
|
||||
{
|
||||
uint32 lErrno = mysql_errno(m_Mysql);
|
||||
LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT));
|
||||
|
||||
if (_HandleMySQLErrno(lErrno, mysql_stmt_error(msql_STMT))) // If it returns true, an error was handled successfully (i.e. reconnection)
|
||||
return Execute(stmt); // Try again
|
||||
|
||||
m_mStmt->ClearParameters();
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG("sql.sql", "[{} ms] SQL(p): {}", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString());
|
||||
|
||||
m_mStmt->ClearParameters();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MySQLConnection::_Query(PreparedStatementBase* stmt, MySQLPreparedStatement** mysqlStmt, MySQLResult** pResult, uint64* pRowCount, uint32* pFieldCount)
|
||||
{
|
||||
if (!m_Mysql)
|
||||
return false;
|
||||
|
||||
uint32 index = stmt->GetIndex();
|
||||
|
||||
MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index);
|
||||
ASSERT(m_mStmt); // Can only be null if preparation failed, server side error or bad query
|
||||
|
||||
m_mStmt->BindParameters(stmt);
|
||||
*mysqlStmt = m_mStmt;
|
||||
|
||||
MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT();
|
||||
MYSQL_BIND* msql_BIND = m_mStmt->GetBind();
|
||||
|
||||
uint32 _s = getMSTime();
|
||||
|
||||
#if MYSQL_VERSION_ID >= 80300
|
||||
if (mysql_stmt_bind_named_param(msql_STMT, msql_BIND, m_mStmt->GetParameterCount(), nullptr))
|
||||
#else
|
||||
if (mysql_stmt_bind_param(msql_STMT, msql_BIND))
|
||||
#endif
|
||||
{
|
||||
uint32 lErrno = mysql_errno(m_Mysql);
|
||||
LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT));
|
||||
|
||||
if (_HandleMySQLErrno(lErrno, mysql_stmt_error(msql_STMT))) // If it returns true, an error was handled successfully (i.e. reconnection)
|
||||
return _Query(stmt, mysqlStmt, pResult, pRowCount, pFieldCount); // Try again
|
||||
|
||||
m_mStmt->ClearParameters();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mysql_stmt_execute(msql_STMT))
|
||||
{
|
||||
uint32 lErrno = mysql_errno(m_Mysql);
|
||||
LOG_ERROR("sql.sql", "SQL(p): {}\n [ERROR]: [{}] {}", m_mStmt->getQueryString(), lErrno, mysql_stmt_error(msql_STMT));
|
||||
|
||||
if (_HandleMySQLErrno(lErrno, mysql_stmt_error(msql_STMT))) // If it returns true, an error was handled successfully (i.e. reconnection)
|
||||
return _Query(stmt, mysqlStmt, pResult, pRowCount, pFieldCount); // Try again
|
||||
|
||||
m_mStmt->ClearParameters();
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_DEBUG("sql.sql", "[{} ms] SQL(p): {}", getMSTimeDiff(_s, getMSTime()), m_mStmt->getQueryString());
|
||||
|
||||
m_mStmt->ClearParameters();
|
||||
|
||||
*pResult = reinterpret_cast<MySQLResult*>(mysql_stmt_result_metadata(msql_STMT));
|
||||
*pRowCount = mysql_stmt_affected_rows(msql_STMT);
|
||||
*pFieldCount = mysql_stmt_field_count(msql_STMT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ResultSet* MySQLConnection::Query(std::string_view sql)
|
||||
{
|
||||
if (sql.empty())
|
||||
return nullptr;
|
||||
|
||||
MySQLResult* result = nullptr;
|
||||
MySQLField* fields = nullptr;
|
||||
uint64 rowCount = 0;
|
||||
uint32 fieldCount = 0;
|
||||
|
||||
if (!_Query(sql, &result, &fields, &rowCount, &fieldCount))
|
||||
return nullptr;
|
||||
|
||||
return new ResultSet(result, fields, rowCount, fieldCount);
|
||||
}
|
||||
|
||||
bool MySQLConnection::_Query(std::string_view sql, MySQLResult** pResult, MySQLField** pFields, uint64* pRowCount, uint32* pFieldCount)
|
||||
{
|
||||
if (!m_Mysql)
|
||||
return false;
|
||||
|
||||
{
|
||||
uint32 _s = getMSTime();
|
||||
|
||||
if (mysql_query(m_Mysql, std::string(sql).c_str()))
|
||||
{
|
||||
uint32 lErrno = mysql_errno(m_Mysql);
|
||||
LOG_INFO("sql.sql", "SQL: {}", sql);
|
||||
LOG_ERROR("sql.sql", "[{}] {}", lErrno, mysql_error(m_Mysql));
|
||||
|
||||
if (_HandleMySQLErrno(lErrno, mysql_error(m_Mysql))) // If it returns true, an error was handled successfully (i.e. reconnection)
|
||||
return _Query(sql, pResult, pFields, pRowCount, pFieldCount); // We try again
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
LOG_DEBUG("sql.sql", "[{} ms] SQL: {}", getMSTimeDiff(_s, getMSTime()), sql);
|
||||
|
||||
*pResult = reinterpret_cast<MySQLResult*>(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 = reinterpret_cast<MySQLField*>(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(std::shared_ptr<TransactionBase> transaction)
|
||||
{
|
||||
std::vector<SQLElementData> const& queries = transaction->m_queries;
|
||||
if (queries.empty())
|
||||
return -1;
|
||||
|
||||
BeginTransaction();
|
||||
|
||||
for (auto const& data : queries)
|
||||
{
|
||||
switch (data.type)
|
||||
{
|
||||
case SQL_ELEMENT_PREPARED:
|
||||
{
|
||||
PreparedStatementBase* stmt = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
stmt = std::get<PreparedStatementBase*>(data.element);
|
||||
}
|
||||
catch (const std::bad_variant_access& ex)
|
||||
{
|
||||
LOG_FATAL("sql.sql", "> PreparedStatementBase not found in SQLElementData. {}", ex.what());
|
||||
ABORT();
|
||||
}
|
||||
|
||||
ASSERT(stmt);
|
||||
|
||||
if (!Execute(stmt))
|
||||
{
|
||||
LOG_WARN("sql.sql", "Transaction aborted. {} queries not executed.", queries.size());
|
||||
int errorCode = GetLastError();
|
||||
RollbackTransaction();
|
||||
return errorCode;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SQL_ELEMENT_RAW:
|
||||
{
|
||||
std::string sql{};
|
||||
|
||||
try
|
||||
{
|
||||
sql = std::get<std::string>(data.element);
|
||||
}
|
||||
catch (const std::bad_variant_access& ex)
|
||||
{
|
||||
LOG_FATAL("sql.sql", "> std::string not found in SQLElementData. {}", ex.what());
|
||||
ABORT();
|
||||
}
|
||||
|
||||
ASSERT(!sql.empty());
|
||||
|
||||
if (!Execute(sql))
|
||||
{
|
||||
LOG_WARN("sql.sql", "Transaction aborted. {} queries not executed.", queries.size());
|
||||
uint32 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;
|
||||
}
|
||||
|
||||
std::size_t MySQLConnection::EscapeString(char* to, const char* from, std::size_t length)
|
||||
{
|
||||
return mysql_real_escape_string(m_Mysql, to, from, length);
|
||||
}
|
||||
|
||||
void MySQLConnection::Ping()
|
||||
{
|
||||
mysql_ping(m_Mysql);
|
||||
}
|
||||
|
||||
uint32 MySQLConnection::GetLastError()
|
||||
{
|
||||
return mysql_errno(m_Mysql);
|
||||
}
|
||||
|
||||
bool MySQLConnection::LockIfReady()
|
||||
{
|
||||
return m_Mutex.try_lock();
|
||||
}
|
||||
|
||||
void MySQLConnection::Unlock()
|
||||
{
|
||||
m_Mutex.unlock();
|
||||
}
|
||||
|
||||
uint32 MySQLConnection::GetServerVersion() const
|
||||
{
|
||||
return mysql_get_server_version(m_Mysql);
|
||||
}
|
||||
|
||||
std::string MySQLConnection::GetServerInfo() const
|
||||
{
|
||||
return mysql_get_server_info(m_Mysql);
|
||||
}
|
||||
|
||||
MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index)
|
||||
{
|
||||
ASSERT(index < m_stmts.size(), "Tried to access invalid prepared statement index {} (max index {}) on database `{}`, connection type: {}",
|
||||
index, m_stmts.size(), m_connectionInfo.database, (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
|
||||
|
||||
MySQLPreparedStatement* ret = m_stmts[index].get();
|
||||
|
||||
if (!ret)
|
||||
LOG_ERROR("sql.sql", "Could not fetch prepared statement {} on database `{}`, connection type: {}.",
|
||||
index, m_connectionInfo.database, (m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MySQLConnection::PrepareStatement(uint32 index, std::string_view sql, ConnectionFlags flags)
|
||||
{
|
||||
// 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].reset();
|
||||
return;
|
||||
}
|
||||
|
||||
MYSQL_STMT* stmt = mysql_stmt_init(m_Mysql);
|
||||
if (!stmt)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "In mysql_stmt_init() id: {}, sql: \"{}\"", index, sql);
|
||||
LOG_ERROR("sql.sql", "{}", mysql_error(m_Mysql));
|
||||
m_prepareError = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mysql_stmt_prepare(stmt, std::string(sql).c_str(), static_cast<unsigned long>(sql.size())))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "In mysql_stmt_prepare() id: {}, sql: \"{}\"", index, sql);
|
||||
LOG_ERROR("sql.sql", "{}", mysql_stmt_error(stmt));
|
||||
mysql_stmt_close(stmt);
|
||||
m_prepareError = true;
|
||||
}
|
||||
else
|
||||
m_stmts[index] = std::make_unique<MySQLPreparedStatement>(reinterpret_cast<MySQLStmt*>(stmt), sql);
|
||||
}
|
||||
}
|
||||
|
||||
PreparedResultSet* MySQLConnection::Query(PreparedStatementBase* stmt)
|
||||
{
|
||||
MySQLPreparedStatement* mysqlStmt = nullptr;
|
||||
MySQLResult* result = nullptr;
|
||||
uint64 rowCount = 0;
|
||||
uint32 fieldCount = 0;
|
||||
|
||||
if (!_Query(stmt, &mysqlStmt, &result, &rowCount, &fieldCount))
|
||||
return nullptr;
|
||||
|
||||
if (mysql_more_results(m_Mysql))
|
||||
{
|
||||
mysql_next_result(m_Mysql);
|
||||
}
|
||||
|
||||
return new PreparedResultSet(mysqlStmt->GetSTMT(), result, rowCount, fieldCount);
|
||||
}
|
||||
|
||||
bool MySQLConnection::_HandleMySQLErrno(uint32 errNo, char const* err, uint8 attempts /*= 5*/)
|
||||
{
|
||||
std::string str = "";
|
||||
switch (errNo)
|
||||
{
|
||||
case CR_SERVER_GONE_ERROR:
|
||||
case CR_SERVER_LOST:
|
||||
case CR_SERVER_LOST_EXTENDED:
|
||||
{
|
||||
if (m_Mysql)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Lost the connection to the MySQL server!");
|
||||
|
||||
mysql_close(m_Mysql);
|
||||
m_Mysql = nullptr;
|
||||
}
|
||||
[[fallthrough]];
|
||||
}
|
||||
case CR_CONN_HOST_ERROR:
|
||||
{
|
||||
LOG_INFO("sql.sql", "Attempting to reconnect to the MySQL server...");
|
||||
|
||||
m_reconnecting = true;
|
||||
|
||||
uint32 const lErrno = Open();
|
||||
if (!lErrno)
|
||||
{
|
||||
// Don't remove 'this' pointer unless you want to skip loading all prepared statements...
|
||||
if (!this->PrepareStatements())
|
||||
{
|
||||
str = "Could not re-prepare statements!";
|
||||
LOG_FATAL("sql.sql", "{}", str);
|
||||
std::this_thread::sleep_for(10s);
|
||||
ABORT("{}\n\n[{}] {}", str, errNo, err);
|
||||
}
|
||||
|
||||
LOG_INFO("sql.sql", "Successfully reconnected to {} @{}:{} ({}).",
|
||||
m_connectionInfo.database, m_connectionInfo.host, m_connectionInfo.port_or_socket,
|
||||
(m_connectionFlags & CONNECTION_ASYNC) ? "asynchronous" : "synchronous");
|
||||
|
||||
m_reconnecting = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((--attempts) == 0)
|
||||
{
|
||||
// Shut down the server when the mysql server isn't
|
||||
// reachable for some time
|
||||
str = "Failed to reconnect to the MySQL server, terminating the server to prevent data corruption!";
|
||||
LOG_FATAL("sql.sql", "{}", str);
|
||||
|
||||
// We could also initiate a shutdown through using std::raise(SIGTERM)
|
||||
std::this_thread::sleep_for(10s);
|
||||
ABORT("{}\n\n[{}] {}", str, errNo, err);
|
||||
}
|
||||
else
|
||||
{
|
||||
// It's possible this attempted reconnect throws 2006 at us.
|
||||
// To prevent crazy recursive calls, sleep here.
|
||||
std::this_thread::sleep_for(3s); // Sleep 3 seconds
|
||||
return _HandleMySQLErrno(lErrno, mysql_error(m_Mysql), attempts); // Call self (recursive)
|
||||
}
|
||||
[[fallthrough]];
|
||||
}
|
||||
|
||||
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:
|
||||
str = "Your database structure is not up to date. Please make sure you've executed all queries in the sql/updates folders.";
|
||||
LOG_FATAL("sql.sql", "{}", str);
|
||||
std::this_thread::sleep_for(10s);
|
||||
ABORT("{}\n\n[{}] {}", str, errNo, err);
|
||||
return false;
|
||||
case ER_PARSE_ERROR:
|
||||
str = "Error while parsing SQL. Core fix required.";
|
||||
LOG_FATAL("sql.sql", "{}", str);
|
||||
std::this_thread::sleep_for(10s);
|
||||
ABORT("{}\n\n[{}] {}", str, errNo, err);
|
||||
return false;
|
||||
default:
|
||||
LOG_ERROR("sql.sql", "Unhandled MySQL errno {}. Unexpected behaviour possible.", errNo);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
121
src/server/database/Database/MySQLConnection.h
Normal file
121
src/server/database/Database/MySQLConnection.h
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 _MYSQLCONNECTION_H
|
||||
#define _MYSQLCONNECTION_H
|
||||
|
||||
#include "DatabaseEnvFwd.h"
|
||||
#include "Define.h"
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
template <typename T>
|
||||
class ProducerConsumerQueue;
|
||||
|
||||
class DatabaseWorker;
|
||||
class MySQLPreparedStatement;
|
||||
class SQLOperation;
|
||||
|
||||
enum ConnectionFlags
|
||||
{
|
||||
CONNECTION_ASYNC = 0x1,
|
||||
CONNECTION_SYNCH = 0x2,
|
||||
CONNECTION_BOTH = CONNECTION_ASYNC | CONNECTION_SYNCH
|
||||
};
|
||||
|
||||
struct AC_DATABASE_API MySQLConnectionInfo
|
||||
{
|
||||
explicit MySQLConnectionInfo(std::string_view infoString);
|
||||
|
||||
std::string user;
|
||||
std::string password;
|
||||
std::string database;
|
||||
std::string host;
|
||||
std::string port_or_socket;
|
||||
std::string ssl;
|
||||
};
|
||||
|
||||
class AC_DATABASE_API 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();
|
||||
|
||||
bool Execute(std::string_view sql);
|
||||
bool Execute(PreparedStatementBase* stmt);
|
||||
ResultSet* Query(std::string_view sql);
|
||||
PreparedResultSet* Query(PreparedStatementBase* stmt);
|
||||
bool _Query(std::string_view sql, MySQLResult** pResult, MySQLField** pFields, uint64* pRowCount, uint32* pFieldCount);
|
||||
bool _Query(PreparedStatementBase* stmt, MySQLPreparedStatement** mysqlStmt, MySQLResult** pResult, uint64* pRowCount, uint32* pFieldCount);
|
||||
|
||||
void BeginTransaction();
|
||||
void RollbackTransaction();
|
||||
void CommitTransaction();
|
||||
int ExecuteTransaction(std::shared_ptr<TransactionBase> transaction);
|
||||
std::size_t EscapeString(char* to, const char* from, std::size_t length);
|
||||
void Ping();
|
||||
|
||||
uint32 GetLastError();
|
||||
|
||||
protected:
|
||||
/// Tries to acquire lock. If lock is acquired by another thread
|
||||
/// the calling parent will just try another connection
|
||||
bool LockIfReady();
|
||||
|
||||
/// Called by parent databasepool. Will let other threads access this connection
|
||||
void Unlock();
|
||||
|
||||
[[nodiscard]] uint32 GetServerVersion() const;
|
||||
[[nodiscard]] std::string GetServerInfo() const;
|
||||
MySQLPreparedStatement* GetPreparedStatement(uint32 index);
|
||||
void PrepareStatement(uint32 index, std::string_view sql, ConnectionFlags flags);
|
||||
|
||||
virtual void DoPrepareStatements() = 0;
|
||||
virtual bool _HandleMySQLErrno(uint32 errNo, char const* err = "", uint8 attempts = 5);
|
||||
|
||||
typedef std::vector<std::unique_ptr<MySQLPreparedStatement>> PreparedStatementContainer;
|
||||
|
||||
PreparedStatementContainer m_stmts; //! PreparedStatements storage
|
||||
bool m_reconnecting; //! Are we reconnecting?
|
||||
bool m_prepareError; //! Was there any error while preparing statements?
|
||||
MySQLHandle* m_Mysql; //! MySQL Handle.
|
||||
|
||||
private:
|
||||
ProducerConsumerQueue<SQLOperation*>* m_queue; //! Queue shared with other asynchronous connections.
|
||||
std::unique_ptr<DatabaseWorker> m_worker; //! Core worker task.
|
||||
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
|
||||
34
src/server/database/Database/MySQLHacks.h
Normal file
34
src/server/database/Database/MySQLHacks.h
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 MySQLHacks_h__
|
||||
#define MySQLHacks_h__
|
||||
|
||||
#include "MySQLWorkaround.h"
|
||||
#include <type_traits>
|
||||
|
||||
struct MySQLHandle : MYSQL { };
|
||||
struct MySQLResult : MYSQL_RES { };
|
||||
struct MySQLField : MYSQL_FIELD { };
|
||||
struct MySQLBind : MYSQL_BIND { };
|
||||
struct MySQLStmt : MYSQL_STMT { };
|
||||
|
||||
// mysql 8 removed my_bool typedef (it was char) and started using bools directly
|
||||
// to maintain compatibility we use this trick to retrieve which type is being used
|
||||
using MySQLBool = std::remove_pointer_t<decltype(std::declval<MYSQL_BIND>().is_null)>;
|
||||
|
||||
#endif // MySQLHacks_h__
|
||||
209
src/server/database/Database/MySQLPreparedStatement.cpp
Normal file
209
src/server/database/Database/MySQLPreparedStatement.cpp
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "MySQLPreparedStatement.h"
|
||||
#include "Errors.h"
|
||||
#include "Log.h"
|
||||
#include "MySQLHacks.h"
|
||||
#include "PreparedStatement.h"
|
||||
|
||||
template<typename T>
|
||||
struct MySQLType { };
|
||||
|
||||
template<> struct MySQLType<uint8> : std::integral_constant<enum_field_types, MYSQL_TYPE_TINY> { };
|
||||
template<> struct MySQLType<uint16> : std::integral_constant<enum_field_types, MYSQL_TYPE_SHORT> { };
|
||||
template<> struct MySQLType<uint32> : std::integral_constant<enum_field_types, MYSQL_TYPE_LONG> { };
|
||||
template<> struct MySQLType<uint64> : std::integral_constant<enum_field_types, MYSQL_TYPE_LONGLONG> { };
|
||||
template<> struct MySQLType<int8> : std::integral_constant<enum_field_types, MYSQL_TYPE_TINY> { };
|
||||
template<> struct MySQLType<int16> : std::integral_constant<enum_field_types, MYSQL_TYPE_SHORT> { };
|
||||
template<> struct MySQLType<int32> : std::integral_constant<enum_field_types, MYSQL_TYPE_LONG> { };
|
||||
template<> struct MySQLType<int64> : std::integral_constant<enum_field_types, MYSQL_TYPE_LONGLONG> { };
|
||||
template<> struct MySQLType<float> : std::integral_constant<enum_field_types, MYSQL_TYPE_FLOAT> { };
|
||||
template<> struct MySQLType<double> : std::integral_constant<enum_field_types, MYSQL_TYPE_DOUBLE> { };
|
||||
|
||||
MySQLPreparedStatement::MySQLPreparedStatement(MySQLStmt* stmt, std::string_view queryString) :
|
||||
m_stmt(nullptr),
|
||||
m_Mstmt(stmt),
|
||||
m_bind(nullptr),
|
||||
m_queryString(std::string(queryString))
|
||||
{
|
||||
/// Initialize variable parameters
|
||||
m_paramCount = mysql_stmt_param_count(stmt);
|
||||
m_paramsSet.assign(m_paramCount, false);
|
||||
m_bind = new MySQLBind[m_paramCount];
|
||||
memset(m_bind, 0, sizeof(MySQLBind) * m_paramCount);
|
||||
|
||||
/// "If set to 1, causes mysql_stmt_store_result() to update the metadata MYSQL_FIELD->max_length value."
|
||||
MySQLBool bool_tmp = MySQLBool(1);
|
||||
mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &bool_tmp);
|
||||
}
|
||||
|
||||
MySQLPreparedStatement::~MySQLPreparedStatement()
|
||||
{
|
||||
ClearParameters();
|
||||
if (m_Mstmt->bind_result_done)
|
||||
{
|
||||
delete[] m_Mstmt->bind->length;
|
||||
delete[] m_Mstmt->bind->is_null;
|
||||
}
|
||||
|
||||
mysql_stmt_close(m_Mstmt);
|
||||
delete[] m_bind;
|
||||
}
|
||||
|
||||
void MySQLPreparedStatement::BindParameters(PreparedStatementBase* stmt)
|
||||
{
|
||||
m_stmt = stmt; // Cross reference them for debug output
|
||||
|
||||
uint8 pos = 0;
|
||||
for (PreparedStatementData const& data : stmt->GetParameters())
|
||||
{
|
||||
std::visit([&](auto&& param)
|
||||
{
|
||||
SetParameter(pos, param);
|
||||
}, data.data);
|
||||
|
||||
++pos;
|
||||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (pos < m_paramCount)
|
||||
LOG_WARN("sql.sql", "[WARNING]: BindParameters() for statement {} did not bind all allocated parameters", stmt->GetIndex());
|
||||
#endif
|
||||
}
|
||||
|
||||
void MySQLPreparedStatement::ClearParameters()
|
||||
{
|
||||
for (uint32 i=0; i < m_paramCount; ++i)
|
||||
{
|
||||
delete m_bind[i].length;
|
||||
m_bind[i].length = nullptr;
|
||||
delete[] (char*)m_bind[i].buffer;
|
||||
m_bind[i].buffer = nullptr;
|
||||
m_paramsSet[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ParamenterIndexAssertFail(uint32 stmtIndex, uint8 index, uint32 paramCount)
|
||||
{
|
||||
LOG_ERROR("sql.driver", "Attempted to bind parameter {}{} on a PreparedStatement {} (statement has only {} parameters)",
|
||||
uint32(index) + 1, (index == 1 ? "st" : (index == 2 ? "nd" : (index == 3 ? "rd" : "nd"))), stmtIndex, paramCount);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//- Bind on mysql level
|
||||
void MySQLPreparedStatement::AssertValidIndex(uint8 index)
|
||||
{
|
||||
ASSERT(index < m_paramCount || ParamenterIndexAssertFail(m_stmt->GetIndex(), index, m_paramCount));
|
||||
|
||||
if (m_paramsSet[index])
|
||||
LOG_ERROR("sql.sql", "[ERROR] Prepared Statement (id: {}) trying to bind value on already bound index ({}).", m_stmt->GetIndex(), index);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void MySQLPreparedStatement::SetParameter(const uint8 index, T value)
|
||||
{
|
||||
AssertValidIndex(index);
|
||||
m_paramsSet[index] = true;
|
||||
MYSQL_BIND* param = &m_bind[index];
|
||||
uint32 len = uint32(sizeof(T));
|
||||
param->buffer_type = MySQLType<T>::value;
|
||||
delete[] static_cast<char*>(param->buffer);
|
||||
param->buffer = new char[len];
|
||||
param->buffer_length = 0;
|
||||
param->is_null_value = 0;
|
||||
param->length = nullptr; // Only != NULL for strings
|
||||
param->is_unsigned = std::is_unsigned_v<T>;
|
||||
|
||||
memcpy(param->buffer, &value, len);
|
||||
}
|
||||
|
||||
void MySQLPreparedStatement::SetParameter(const uint8 index, bool value)
|
||||
{
|
||||
SetParameter(index, uint8(value ? 1 : 0));
|
||||
}
|
||||
|
||||
void MySQLPreparedStatement::SetParameter(const uint8 index, std::nullptr_t /*value*/)
|
||||
{
|
||||
AssertValidIndex(index);
|
||||
m_paramsSet[index] = true;
|
||||
MYSQL_BIND* param = &m_bind[index];
|
||||
param->buffer_type = MYSQL_TYPE_NULL;
|
||||
delete[] static_cast<char*>(param->buffer);
|
||||
param->buffer = nullptr;
|
||||
param->buffer_length = 0;
|
||||
param->is_null_value = 1;
|
||||
delete param->length;
|
||||
param->length = nullptr;
|
||||
}
|
||||
|
||||
void MySQLPreparedStatement::SetParameter(uint8 index, std::string const& value)
|
||||
{
|
||||
AssertValidIndex(index);
|
||||
m_paramsSet[index] = true;
|
||||
MYSQL_BIND* param = &m_bind[index];
|
||||
uint32 len = uint32(value.size());
|
||||
param->buffer_type = MYSQL_TYPE_VAR_STRING;
|
||||
delete[] static_cast<char*>(param->buffer);
|
||||
param->buffer = new char[len];
|
||||
param->buffer_length = len;
|
||||
param->is_null_value = 0;
|
||||
delete param->length;
|
||||
param->length = new unsigned long(len);
|
||||
|
||||
memcpy(param->buffer, value.c_str(), len);
|
||||
}
|
||||
|
||||
void MySQLPreparedStatement::SetParameter(uint8 index, std::vector<uint8> const& value)
|
||||
{
|
||||
AssertValidIndex(index);
|
||||
m_paramsSet[index] = true;
|
||||
MYSQL_BIND* param = &m_bind[index];
|
||||
uint32 len = uint32(value.size());
|
||||
param->buffer_type = MYSQL_TYPE_BLOB;
|
||||
delete[] static_cast<char*>(param->buffer);
|
||||
param->buffer = new char[len];
|
||||
param->buffer_length = len;
|
||||
param->is_null_value = 0;
|
||||
delete param->length;
|
||||
param->length = new unsigned long(len);
|
||||
|
||||
memcpy(param->buffer, value.data(), len);
|
||||
}
|
||||
|
||||
std::string MySQLPreparedStatement::getQueryString() const
|
||||
{
|
||||
std::string queryString(m_queryString);
|
||||
|
||||
std::size_t pos = 0;
|
||||
|
||||
for (PreparedStatementData const& data : m_stmt->GetParameters())
|
||||
{
|
||||
pos = queryString.find('?', pos);
|
||||
|
||||
std::string replaceStr = std::visit([&](auto&& data)
|
||||
{
|
||||
return PreparedStatementData::ToString(data);
|
||||
}, data.data);
|
||||
|
||||
queryString.replace(pos, 1, replaceStr);
|
||||
pos += replaceStr.length();
|
||||
}
|
||||
|
||||
return queryString;
|
||||
}
|
||||
73
src/server/database/Database/MySQLPreparedStatement.h
Normal file
73
src/server/database/Database/MySQLPreparedStatement.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 MySQLPreparedStatement_h__
|
||||
#define MySQLPreparedStatement_h__
|
||||
|
||||
#include "DatabaseEnvFwd.h"
|
||||
#include "Define.h"
|
||||
#include "MySQLWorkaround.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class MySQLConnection;
|
||||
class PreparedStatementBase;
|
||||
|
||||
//- Class of which the instances are unique per MySQLConnection
|
||||
//- access to these class objects is only done when a prepared statement task
|
||||
//- is executed.
|
||||
class AC_DATABASE_API MySQLPreparedStatement
|
||||
{
|
||||
friend class MySQLConnection;
|
||||
friend class PreparedStatementBase;
|
||||
|
||||
public:
|
||||
MySQLPreparedStatement(MySQLStmt* stmt, std::string_view queryString);
|
||||
~MySQLPreparedStatement();
|
||||
|
||||
void BindParameters(PreparedStatementBase* stmt);
|
||||
|
||||
uint32 GetParameterCount() const { return m_paramCount; }
|
||||
|
||||
protected:
|
||||
void SetParameter(const uint8 index, bool value);
|
||||
void SetParameter(const uint8 index, std::nullptr_t /*value*/);
|
||||
void SetParameter(const uint8 index, std::string const& value);
|
||||
void SetParameter(const uint8 index, std::vector<uint8> const& value);
|
||||
|
||||
template<typename T>
|
||||
void SetParameter(const uint8 index, T value);
|
||||
|
||||
MySQLStmt* GetSTMT() { return m_Mstmt; }
|
||||
MySQLBind* GetBind() { return m_bind; }
|
||||
PreparedStatementBase* m_stmt;
|
||||
void ClearParameters();
|
||||
void AssertValidIndex(const uint8 index);
|
||||
std::string getQueryString() const;
|
||||
|
||||
private:
|
||||
MySQLStmt* m_Mstmt;
|
||||
uint32 m_paramCount;
|
||||
std::vector<bool> m_paramsSet;
|
||||
MySQLBind* m_bind;
|
||||
std::string m_queryString{};
|
||||
|
||||
MySQLPreparedStatement(MySQLPreparedStatement const& right) = delete;
|
||||
MySQLPreparedStatement& operator=(MySQLPreparedStatement const& right) = delete;
|
||||
};
|
||||
|
||||
#endif // MySQLPreparedStatement_h__
|
||||
34
src/server/database/Database/MySQLThreading.cpp
Normal file
34
src/server/database/Database/MySQLThreading.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "MySQLThreading.h"
|
||||
#include "MySQLWorkaround.h"
|
||||
|
||||
void MySQL::Library_Init()
|
||||
{
|
||||
mysql_library_init(-1, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void MySQL::Library_End()
|
||||
{
|
||||
mysql_library_end();
|
||||
}
|
||||
|
||||
uint32 MySQL::GetLibraryVersion()
|
||||
{
|
||||
return MYSQL_VERSION_ID;
|
||||
}
|
||||
30
src/server/database/Database/MySQLThreading.h
Normal file
30
src/server/database/Database/MySQLThreading.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "Define.h"
|
||||
|
||||
namespace MySQL
|
||||
{
|
||||
AC_DATABASE_API void Library_Init();
|
||||
AC_DATABASE_API void Library_End();
|
||||
AC_DATABASE_API uint32 GetLibraryVersion();
|
||||
}
|
||||
|
||||
#endif
|
||||
21
src/server/database/Database/MySQLWorkaround.h
Normal file
21
src/server/database/Database/MySQLWorkaround.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifdef _WIN32 // hack for broken mysql.h not including the correct winsock header for SOCKET definition, fixed in 5.7
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <mysql.h>
|
||||
131
src/server/database/Database/PreparedStatement.cpp
Normal file
131
src/server/database/Database/PreparedStatement.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "Errors.h"
|
||||
#include "Log.h"
|
||||
#include "MySQLConnection.h"
|
||||
#include "MySQLWorkaround.h"
|
||||
#include "QueryResult.h"
|
||||
|
||||
PreparedStatementBase::PreparedStatementBase(uint32 index, uint8 capacity) :
|
||||
m_index(index),
|
||||
statement_data(capacity) { }
|
||||
|
||||
PreparedStatementBase::~PreparedStatementBase() { }
|
||||
|
||||
//- Bind to buffer
|
||||
template<typename T>
|
||||
Acore::Types::is_non_string_view_v<T> PreparedStatementBase::SetValidData(const uint8 index, T const& value)
|
||||
{
|
||||
ASSERT(index < statement_data.size());
|
||||
statement_data[index].data.emplace<T>(value);
|
||||
}
|
||||
|
||||
// Non template functions
|
||||
void PreparedStatementBase::SetValidData(const uint8 index)
|
||||
{
|
||||
ASSERT(index < statement_data.size());
|
||||
statement_data[index].data.emplace<std::nullptr_t>(nullptr);
|
||||
}
|
||||
|
||||
void PreparedStatementBase::SetValidData(const uint8 index, std::string_view value)
|
||||
{
|
||||
ASSERT(index < statement_data.size());
|
||||
statement_data[index].data.emplace<std::string>(value);
|
||||
}
|
||||
|
||||
template void PreparedStatementBase::SetValidData(const uint8 index, uint8 const& value);
|
||||
template void PreparedStatementBase::SetValidData(const uint8 index, int8 const& value);
|
||||
template void PreparedStatementBase::SetValidData(const uint8 index, uint16 const& value);
|
||||
template void PreparedStatementBase::SetValidData(const uint8 index, int16 const& value);
|
||||
template void PreparedStatementBase::SetValidData(const uint8 index, uint32 const& value);
|
||||
template void PreparedStatementBase::SetValidData(const uint8 index, int32 const& value);
|
||||
template void PreparedStatementBase::SetValidData(const uint8 index, uint64 const& value);
|
||||
template void PreparedStatementBase::SetValidData(const uint8 index, int64 const& value);
|
||||
template void PreparedStatementBase::SetValidData(const uint8 index, bool const& value);
|
||||
template void PreparedStatementBase::SetValidData(const uint8 index, float const& value);
|
||||
template void PreparedStatementBase::SetValidData(const uint8 index, std::string const& value);
|
||||
template void PreparedStatementBase::SetValidData(const uint8 index, std::vector<uint8> const& value);
|
||||
|
||||
//- Execution
|
||||
PreparedStatementTask::PreparedStatementTask(PreparedStatementBase* stmt, bool async) :
|
||||
m_stmt(stmt),
|
||||
m_result(nullptr)
|
||||
{
|
||||
m_has_result = async; // If it's async, then there's a result
|
||||
|
||||
if (async)
|
||||
m_result = new PreparedQueryResultPromise();
|
||||
}
|
||||
|
||||
PreparedStatementTask::~PreparedStatementTask()
|
||||
{
|
||||
delete m_stmt;
|
||||
|
||||
if (m_has_result && m_result)
|
||||
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(nullptr));
|
||||
return false;
|
||||
}
|
||||
|
||||
m_result->set_value(PreparedQueryResult(result));
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_conn->Execute(m_stmt);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::string PreparedStatementData::ToString(T value)
|
||||
{
|
||||
return Acore::StringFormat("{}", value);
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string PreparedStatementData::ToString(std::vector<uint8> /*value*/)
|
||||
{
|
||||
return "BINARY";
|
||||
}
|
||||
|
||||
template std::string PreparedStatementData::ToString(uint8);
|
||||
template std::string PreparedStatementData::ToString(uint16);
|
||||
template std::string PreparedStatementData::ToString(uint32);
|
||||
template std::string PreparedStatementData::ToString(uint64);
|
||||
template std::string PreparedStatementData::ToString(int8);
|
||||
template std::string PreparedStatementData::ToString(int16);
|
||||
template std::string PreparedStatementData::ToString(int32);
|
||||
template std::string PreparedStatementData::ToString(int64);
|
||||
template std::string PreparedStatementData::ToString(std::string);
|
||||
template std::string PreparedStatementData::ToString(float);
|
||||
template std::string PreparedStatementData::ToString(double);
|
||||
template std::string PreparedStatementData::ToString(bool);
|
||||
|
||||
std::string PreparedStatementData::ToString(std::nullptr_t /*value*/)
|
||||
{
|
||||
return "NULL";
|
||||
}
|
||||
184
src/server/database/Database/PreparedStatement.h
Normal file
184
src/server/database/Database/PreparedStatement.h
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "Define.h"
|
||||
#include "Duration.h"
|
||||
#include "SQLOperation.h"
|
||||
#include <future>
|
||||
#include <tuple>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace Acore::Types
|
||||
{
|
||||
template <typename T>
|
||||
using is_default = std::enable_if_t<std::is_arithmetic_v<T> || std::is_same_v<std::vector<uint8>, T>>;
|
||||
|
||||
template <typename T>
|
||||
using is_enum_v = std::enable_if_t<std::is_enum_v<T>>;
|
||||
|
||||
template <typename T>
|
||||
using is_non_string_view_v = std::enable_if_t<!std::is_base_of_v<std::string_view, T>>;
|
||||
}
|
||||
|
||||
struct PreparedStatementData
|
||||
{
|
||||
std::variant<
|
||||
bool,
|
||||
uint8,
|
||||
uint16,
|
||||
uint32,
|
||||
uint64,
|
||||
int8,
|
||||
int16,
|
||||
int32,
|
||||
int64,
|
||||
float,
|
||||
double,
|
||||
std::string,
|
||||
std::vector<uint8>,
|
||||
std::nullptr_t
|
||||
> data;
|
||||
|
||||
template<typename T>
|
||||
static std::string ToString(T value);
|
||||
|
||||
static std::string ToString(std::nullptr_t /*value*/);
|
||||
};
|
||||
|
||||
//- Upper-level class that is used in code
|
||||
class AC_DATABASE_API PreparedStatementBase
|
||||
{
|
||||
friend class PreparedStatementTask;
|
||||
|
||||
public:
|
||||
explicit PreparedStatementBase(uint32 index, uint8 capacity);
|
||||
virtual ~PreparedStatementBase();
|
||||
|
||||
// Set numerlic and default binary
|
||||
template<typename T>
|
||||
inline Acore::Types::is_default<T> SetData(const uint8 index, T value)
|
||||
{
|
||||
SetValidData(index, value);
|
||||
}
|
||||
|
||||
// Set enums
|
||||
template<typename T>
|
||||
inline Acore::Types::is_enum_v<T> SetData(const uint8 index, T value)
|
||||
{
|
||||
SetValidData(index, std::underlying_type_t<T>(value));
|
||||
}
|
||||
|
||||
// Set string_view
|
||||
inline void SetData(const uint8 index, std::string_view value)
|
||||
{
|
||||
SetValidData(index, value);
|
||||
}
|
||||
|
||||
// Set nullptr
|
||||
inline void SetData(const uint8 index, std::nullptr_t = nullptr)
|
||||
{
|
||||
SetValidData(index);
|
||||
}
|
||||
|
||||
// Set non default binary
|
||||
template<std::size_t Size>
|
||||
inline void SetData(const uint8 index, std::array<uint8, Size> const& value)
|
||||
{
|
||||
std::vector<uint8> vec(value.begin(), value.end());
|
||||
SetValidData(index, vec);
|
||||
}
|
||||
|
||||
// Set duration
|
||||
template<class _Rep, class _Period>
|
||||
inline void SetData(const uint8 index, std::chrono::duration<_Rep, _Period> const& value, bool convertToUin32 = true)
|
||||
{
|
||||
SetValidData(index, convertToUin32 ? static_cast<uint32>(value.count()) : value.count());
|
||||
}
|
||||
|
||||
// Set all
|
||||
template <typename... Args>
|
||||
inline void SetArguments(Args&&... args)
|
||||
{
|
||||
SetDataTuple(std::make_tuple(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
[[nodiscard]] uint32 GetIndex() const { return m_index; }
|
||||
[[nodiscard]] std::vector<PreparedStatementData> const& GetParameters() const { return statement_data; }
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
Acore::Types::is_non_string_view_v<T> SetValidData(const uint8 index, T const& value);
|
||||
|
||||
void SetValidData(const uint8 index);
|
||||
void SetValidData(const uint8 index, std::string_view value);
|
||||
|
||||
template<typename... Ts>
|
||||
inline void SetDataTuple(std::tuple<Ts...> const& argsList)
|
||||
{
|
||||
std::apply
|
||||
(
|
||||
[this](Ts const&... arguments)
|
||||
{
|
||||
uint8 index{ 0 };
|
||||
((SetData(index, arguments), index++), ...);
|
||||
}, argsList
|
||||
);
|
||||
}
|
||||
|
||||
uint32 m_index;
|
||||
|
||||
//- Buffer of parameters, not tied to MySQL in any way yet
|
||||
std::vector<PreparedStatementData> statement_data;
|
||||
|
||||
PreparedStatementBase(PreparedStatementBase const& right) = delete;
|
||||
PreparedStatementBase& operator=(PreparedStatementBase const& right) = delete;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class PreparedStatement : public PreparedStatementBase
|
||||
{
|
||||
public:
|
||||
explicit PreparedStatement(uint32 index, uint8 capacity) : PreparedStatementBase(index, capacity)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
PreparedStatement(PreparedStatement const& right) = delete;
|
||||
PreparedStatement& operator=(PreparedStatement const& right) = delete;
|
||||
};
|
||||
|
||||
//- Lower-level class, enqueuable operation
|
||||
class AC_DATABASE_API PreparedStatementTask : public SQLOperation
|
||||
{
|
||||
public:
|
||||
PreparedStatementTask(PreparedStatementBase* stmt, bool async = false);
|
||||
~PreparedStatementTask() override;
|
||||
|
||||
bool Execute() override;
|
||||
PreparedQueryResultFuture GetFuture() { return m_result->get_future(); }
|
||||
|
||||
protected:
|
||||
PreparedStatementBase* m_stmt;
|
||||
bool m_has_result;
|
||||
PreparedQueryResultPromise* m_result;
|
||||
};
|
||||
|
||||
#endif
|
||||
229
src/server/database/Database/QueryCallback.cpp
Normal file
229
src/server/database/Database/QueryCallback.cpp
Normal file
@@ -0,0 +1,229 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "QueryCallback.h"
|
||||
#include "Duration.h"
|
||||
#include "Errors.h"
|
||||
|
||||
template<typename T, typename... Args>
|
||||
inline void Construct(T& t, Args&&... args)
|
||||
{
|
||||
new (&t) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void Destroy(T& t)
|
||||
{
|
||||
t.~T();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void ConstructActiveMember(T* obj)
|
||||
{
|
||||
if (!obj->_isPrepared)
|
||||
Construct(obj->_string);
|
||||
else
|
||||
Construct(obj->_prepared);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void DestroyActiveMember(T* obj)
|
||||
{
|
||||
if (!obj->_isPrepared)
|
||||
Destroy(obj->_string);
|
||||
else
|
||||
Destroy(obj->_prepared);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void MoveFrom(T* to, T&& from)
|
||||
{
|
||||
ASSERT(to->_isPrepared == from._isPrepared);
|
||||
|
||||
if (!to->_isPrepared)
|
||||
to->_string = std::move(from._string);
|
||||
else
|
||||
to->_prepared = std::move(from._prepared);
|
||||
}
|
||||
|
||||
struct QueryCallback::QueryCallbackData
|
||||
{
|
||||
public:
|
||||
friend class QueryCallback;
|
||||
|
||||
QueryCallbackData(std::function<void(QueryCallback&, QueryResult)>&& callback) : _string(std::move(callback)), _isPrepared(false) { }
|
||||
QueryCallbackData(std::function<void(QueryCallback&, PreparedQueryResult)>&& callback) : _prepared(std::move(callback)), _isPrepared(true) { }
|
||||
|
||||
QueryCallbackData(QueryCallbackData&& right) noexcept
|
||||
{
|
||||
_isPrepared = right._isPrepared;
|
||||
ConstructActiveMember(this);
|
||||
MoveFrom(this, std::move(right));
|
||||
}
|
||||
|
||||
QueryCallbackData& operator=(QueryCallbackData&& right) noexcept
|
||||
{
|
||||
if (this != &right)
|
||||
{
|
||||
if (_isPrepared != right._isPrepared)
|
||||
{
|
||||
DestroyActiveMember(this);
|
||||
_isPrepared = right._isPrepared;
|
||||
ConstructActiveMember(this);
|
||||
}
|
||||
|
||||
MoveFrom(this, std::move(right));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~QueryCallbackData() { DestroyActiveMember(this); }
|
||||
|
||||
private:
|
||||
QueryCallbackData(QueryCallbackData const&) = delete;
|
||||
QueryCallbackData& operator=(QueryCallbackData const&) = delete;
|
||||
|
||||
template<typename T> friend void ConstructActiveMember(T* obj);
|
||||
template<typename T> friend void DestroyActiveMember(T* obj);
|
||||
template<typename T> friend void MoveFrom(T* to, T&& from);
|
||||
|
||||
union
|
||||
{
|
||||
std::function<void(QueryCallback&, QueryResult)> _string;
|
||||
std::function<void(QueryCallback&, PreparedQueryResult)> _prepared;
|
||||
};
|
||||
bool _isPrepared;
|
||||
};
|
||||
|
||||
// Not using initialization lists to work around segmentation faults when compiling with clang without precompiled headers
|
||||
QueryCallback::QueryCallback(QueryResultFuture&& result)
|
||||
{
|
||||
_isPrepared = false;
|
||||
Construct(_string, std::move(result));
|
||||
}
|
||||
|
||||
QueryCallback::QueryCallback(PreparedQueryResultFuture&& result)
|
||||
{
|
||||
_isPrepared = true;
|
||||
Construct(_prepared, std::move(result));
|
||||
}
|
||||
|
||||
QueryCallback::QueryCallback(QueryCallback&& right) noexcept
|
||||
{
|
||||
_isPrepared = right._isPrepared;
|
||||
ConstructActiveMember(this);
|
||||
MoveFrom(this, std::move(right));
|
||||
_callbacks = std::move(right._callbacks);
|
||||
}
|
||||
|
||||
QueryCallback& QueryCallback::operator=(QueryCallback&& right) noexcept
|
||||
{
|
||||
if (this != &right)
|
||||
{
|
||||
if (_isPrepared != right._isPrepared)
|
||||
{
|
||||
DestroyActiveMember(this);
|
||||
_isPrepared = right._isPrepared;
|
||||
ConstructActiveMember(this);
|
||||
}
|
||||
|
||||
MoveFrom(this, std::move(right));
|
||||
_callbacks = std::move(right._callbacks);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
QueryCallback::~QueryCallback()
|
||||
{
|
||||
DestroyActiveMember(this);
|
||||
}
|
||||
|
||||
QueryCallback&& QueryCallback::WithCallback(std::function<void(QueryResult)>&& callback)
|
||||
{
|
||||
return WithChainingCallback([callback](QueryCallback& /*this*/, QueryResult result) { callback(std::move(result)); });
|
||||
}
|
||||
|
||||
QueryCallback&& QueryCallback::WithPreparedCallback(std::function<void(PreparedQueryResult)>&& callback)
|
||||
{
|
||||
return WithChainingPreparedCallback([callback](QueryCallback& /*this*/, PreparedQueryResult result) { callback(std::move(result)); });
|
||||
}
|
||||
|
||||
QueryCallback&& QueryCallback::WithChainingCallback(std::function<void(QueryCallback&, QueryResult)>&& callback)
|
||||
{
|
||||
ASSERT(!_callbacks.empty() || !_isPrepared, "Attempted to set callback function for string query on a prepared async query");
|
||||
_callbacks.emplace(std::move(callback));
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
QueryCallback&& QueryCallback::WithChainingPreparedCallback(std::function<void(QueryCallback&, PreparedQueryResult)>&& callback)
|
||||
{
|
||||
ASSERT(!_callbacks.empty() || _isPrepared, "Attempted to set callback function for prepared query on a string async query");
|
||||
_callbacks.emplace(std::move(callback));
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
void QueryCallback::SetNextQuery(QueryCallback&& next)
|
||||
{
|
||||
MoveFrom(this, std::move(next));
|
||||
}
|
||||
|
||||
bool QueryCallback::InvokeIfReady()
|
||||
{
|
||||
QueryCallbackData& callback = _callbacks.front();
|
||||
auto checkStateAndReturnCompletion = [this]()
|
||||
{
|
||||
_callbacks.pop();
|
||||
bool hasNext = !_isPrepared ? _string.valid() : _prepared.valid();
|
||||
if (_callbacks.empty())
|
||||
{
|
||||
ASSERT(!hasNext);
|
||||
return true;
|
||||
}
|
||||
|
||||
// abort chain
|
||||
if (!hasNext)
|
||||
return true;
|
||||
|
||||
ASSERT(_isPrepared == _callbacks.front()._isPrepared);
|
||||
return false;
|
||||
};
|
||||
|
||||
if (!_isPrepared)
|
||||
{
|
||||
if (_string.valid() && _string.wait_for(0s) == std::future_status::ready)
|
||||
{
|
||||
QueryResultFuture f(std::move(_string));
|
||||
std::function<void(QueryCallback&, QueryResult)> cb(std::move(callback._string));
|
||||
cb(*this, f.get());
|
||||
return checkStateAndReturnCompletion();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_prepared.valid() && _prepared.wait_for(0s) == std::future_status::ready)
|
||||
{
|
||||
PreparedQueryResultFuture f(std::move(_prepared));
|
||||
std::function<void(QueryCallback&, PreparedQueryResult)> cb(std::move(callback._prepared));
|
||||
cb(*this, f.get());
|
||||
return checkStateAndReturnCompletion();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
70
src/server/database/Database/QueryCallback.h
Normal file
70
src/server/database/Database/QueryCallback.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _QUERY_CALLBACK_H
|
||||
#define _QUERY_CALLBACK_H
|
||||
|
||||
#include "DatabaseEnvFwd.h"
|
||||
#include "Define.h"
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <list>
|
||||
#include <queue>
|
||||
|
||||
class AC_DATABASE_API QueryCallback
|
||||
{
|
||||
public:
|
||||
explicit QueryCallback(QueryResultFuture&& result);
|
||||
explicit QueryCallback(PreparedQueryResultFuture&& result);
|
||||
|
||||
QueryCallback(QueryCallback&& right) noexcept;
|
||||
QueryCallback& operator=(QueryCallback&& right) noexcept;
|
||||
~QueryCallback();
|
||||
|
||||
QueryCallback&& WithCallback(std::function<void(QueryResult)>&& callback);
|
||||
QueryCallback&& WithPreparedCallback(std::function<void(PreparedQueryResult)>&& callback);
|
||||
|
||||
QueryCallback&& WithChainingCallback(std::function<void(QueryCallback&, QueryResult)>&& callback);
|
||||
QueryCallback&& WithChainingPreparedCallback(std::function<void(QueryCallback&, PreparedQueryResult)>&& callback);
|
||||
|
||||
// Moves std::future from next to this object
|
||||
void SetNextQuery(QueryCallback&& next);
|
||||
|
||||
// returns true when completed
|
||||
bool InvokeIfReady();
|
||||
|
||||
private:
|
||||
QueryCallback(QueryCallback const& right) = delete;
|
||||
QueryCallback& operator=(QueryCallback const& right) = delete;
|
||||
|
||||
template<typename T> friend void ConstructActiveMember(T* obj);
|
||||
template<typename T> friend void DestroyActiveMember(T* obj);
|
||||
template<typename T> friend void MoveFrom(T* to, T&& from);
|
||||
|
||||
union
|
||||
{
|
||||
QueryResultFuture _string;
|
||||
PreparedQueryResultFuture _prepared;
|
||||
};
|
||||
|
||||
bool _isPrepared;
|
||||
|
||||
struct QueryCallbackData;
|
||||
std::queue<QueryCallbackData, std::list<QueryCallbackData>> _callbacks;
|
||||
};
|
||||
|
||||
#endif // _QUERY_CALLBACK_H
|
||||
97
src/server/database/Database/QueryHolder.cpp
Normal file
97
src/server/database/Database/QueryHolder.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "QueryHolder.h"
|
||||
#include "Errors.h"
|
||||
#include "Log.h"
|
||||
#include "MySQLConnection.h"
|
||||
#include "PreparedStatement.h"
|
||||
#include "QueryResult.h"
|
||||
|
||||
bool SQLQueryHolderBase::SetPreparedQueryImpl(std::size_t index, PreparedStatementBase* stmt)
|
||||
{
|
||||
if (m_queries.size() <= index)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Query index ({}) out of range (size: {}) for prepared statement", uint32(index), (uint32)m_queries.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
m_queries[index].first = stmt;
|
||||
return true;
|
||||
}
|
||||
|
||||
PreparedQueryResult SQLQueryHolderBase::GetPreparedResult(std::size_t index) const
|
||||
{
|
||||
// Don't call to this function if the index is of a prepared statement
|
||||
ASSERT(index < m_queries.size(), "Query holder result index out of range, tried to access index {} but there are only {} results",
|
||||
index, m_queries.size());
|
||||
|
||||
return m_queries[index].second;
|
||||
}
|
||||
|
||||
void SQLQueryHolderBase::SetPreparedResult(std::size_t index, PreparedResultSet* result)
|
||||
{
|
||||
if (result && !result->GetRowCount())
|
||||
{
|
||||
delete result;
|
||||
result = nullptr;
|
||||
}
|
||||
|
||||
/// store the result in the holder
|
||||
if (index < m_queries.size())
|
||||
m_queries[index].second = PreparedQueryResult(result);
|
||||
}
|
||||
|
||||
SQLQueryHolderBase::~SQLQueryHolderBase()
|
||||
{
|
||||
for (std::pair<PreparedStatementBase*, PreparedQueryResult>& query : m_queries)
|
||||
{
|
||||
/// if the result was never used, free the resources
|
||||
/// results used already (getresult called) are expected to be deleted
|
||||
delete query.first;
|
||||
}
|
||||
}
|
||||
|
||||
void SQLQueryHolderBase::SetSize(std::size_t size)
|
||||
{
|
||||
/// to optimize push_back, reserve the number of queries about to be executed
|
||||
m_queries.resize(size);
|
||||
}
|
||||
|
||||
SQLQueryHolderTask::~SQLQueryHolderTask() = default;
|
||||
|
||||
bool SQLQueryHolderTask::Execute()
|
||||
{
|
||||
/// execute all queries in the holder and pass the results
|
||||
for (std::size_t i = 0; i < m_holder->m_queries.size(); ++i)
|
||||
if (PreparedStatementBase* stmt = m_holder->m_queries[i].first)
|
||||
m_holder->SetPreparedResult(i, m_conn->Query(stmt));
|
||||
|
||||
m_result.set_value();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SQLQueryHolderCallback::InvokeIfReady()
|
||||
{
|
||||
if (m_future.valid() && m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready)
|
||||
{
|
||||
m_callback(*m_holder);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
89
src/server/database/Database/QueryHolder.h
Normal file
89
src/server/database/Database/QueryHolder.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "SQLOperation.h"
|
||||
#include <vector>
|
||||
|
||||
class AC_DATABASE_API SQLQueryHolderBase
|
||||
{
|
||||
friend class SQLQueryHolderTask;
|
||||
|
||||
public:
|
||||
SQLQueryHolderBase() = default;
|
||||
virtual ~SQLQueryHolderBase();
|
||||
void SetSize(std::size_t size);
|
||||
PreparedQueryResult GetPreparedResult(std::size_t index) const;
|
||||
void SetPreparedResult(std::size_t index, PreparedResultSet* result);
|
||||
|
||||
protected:
|
||||
bool SetPreparedQueryImpl(std::size_t index, PreparedStatementBase* stmt);
|
||||
|
||||
private:
|
||||
std::vector<std::pair<PreparedStatementBase*, PreparedQueryResult>> m_queries;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class SQLQueryHolder : public SQLQueryHolderBase
|
||||
{
|
||||
public:
|
||||
bool SetPreparedQuery(std::size_t index, PreparedStatement<T>* stmt)
|
||||
{
|
||||
return SetPreparedQueryImpl(index, stmt);
|
||||
}
|
||||
};
|
||||
|
||||
class AC_DATABASE_API SQLQueryHolderTask : public SQLOperation
|
||||
{
|
||||
public:
|
||||
explicit SQLQueryHolderTask(std::shared_ptr<SQLQueryHolderBase> holder)
|
||||
: m_holder(std::move(holder)) { }
|
||||
|
||||
~SQLQueryHolderTask();
|
||||
|
||||
bool Execute() override;
|
||||
QueryResultHolderFuture GetFuture() { return m_result.get_future(); }
|
||||
|
||||
private:
|
||||
std::shared_ptr<SQLQueryHolderBase> m_holder;
|
||||
QueryResultHolderPromise m_result;
|
||||
};
|
||||
|
||||
class AC_DATABASE_API SQLQueryHolderCallback
|
||||
{
|
||||
public:
|
||||
SQLQueryHolderCallback(std::shared_ptr<SQLQueryHolderBase>&& holder, QueryResultHolderFuture&& future)
|
||||
: m_holder(std::move(holder)), m_future(std::move(future)) { }
|
||||
|
||||
SQLQueryHolderCallback(SQLQueryHolderCallback&&) = default;
|
||||
SQLQueryHolderCallback& operator=(SQLQueryHolderCallback&&) = default;
|
||||
|
||||
void AfterComplete(std::function<void(SQLQueryHolderBase const&)> callback) &
|
||||
{
|
||||
m_callback = std::move(callback);
|
||||
}
|
||||
|
||||
bool InvokeIfReady();
|
||||
|
||||
std::shared_ptr<SQLQueryHolderBase> m_holder;
|
||||
QueryResultHolderFuture m_future;
|
||||
std::function<void(SQLQueryHolderBase const&)> m_callback;
|
||||
};
|
||||
|
||||
#endif
|
||||
436
src/server/database/Database/QueryResult.cpp
Normal file
436
src/server/database/Database/QueryResult.cpp
Normal file
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "QueryResult.h"
|
||||
#include "Errors.h"
|
||||
#include "Field.h"
|
||||
#include "Log.h"
|
||||
#include "MySQLHacks.h"
|
||||
#include "MySQLWorkaround.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
static uint32 SizeForType(MYSQL_FIELD* field)
|
||||
{
|
||||
switch (field->type)
|
||||
{
|
||||
case MYSQL_TYPE_NULL:
|
||||
return 0;
|
||||
case MYSQL_TYPE_TINY:
|
||||
return 1;
|
||||
case MYSQL_TYPE_YEAR:
|
||||
case MYSQL_TYPE_SHORT:
|
||||
return 2;
|
||||
case MYSQL_TYPE_INT24:
|
||||
case MYSQL_TYPE_LONG:
|
||||
case MYSQL_TYPE_FLOAT:
|
||||
return 4;
|
||||
case MYSQL_TYPE_DOUBLE:
|
||||
case MYSQL_TYPE_LONGLONG:
|
||||
case MYSQL_TYPE_BIT:
|
||||
return 8;
|
||||
|
||||
case MYSQL_TYPE_TIMESTAMP:
|
||||
case MYSQL_TYPE_DATE:
|
||||
case MYSQL_TYPE_TIME:
|
||||
case MYSQL_TYPE_DATETIME:
|
||||
return sizeof(MYSQL_TIME);
|
||||
|
||||
case MYSQL_TYPE_TINY_BLOB:
|
||||
case MYSQL_TYPE_MEDIUM_BLOB:
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
case MYSQL_TYPE_BLOB:
|
||||
case MYSQL_TYPE_STRING:
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
return field->max_length + 1;
|
||||
|
||||
case MYSQL_TYPE_DECIMAL:
|
||||
case MYSQL_TYPE_NEWDECIMAL:
|
||||
return 64;
|
||||
|
||||
case MYSQL_TYPE_GEOMETRY:
|
||||
/*
|
||||
Following types are not sent over the wire:
|
||||
MYSQL_TYPE_ENUM:
|
||||
MYSQL_TYPE_SET:
|
||||
*/
|
||||
default:
|
||||
LOG_WARN("sql.sql", "SQL::SizeForType(): invalid field type {}", uint32(field->type));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
DatabaseFieldTypes MysqlTypeToFieldType(enum_field_types type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MYSQL_TYPE_NULL:
|
||||
return DatabaseFieldTypes::Null;
|
||||
case MYSQL_TYPE_TINY:
|
||||
return DatabaseFieldTypes::Int8;
|
||||
case MYSQL_TYPE_YEAR:
|
||||
case MYSQL_TYPE_SHORT:
|
||||
return DatabaseFieldTypes::Int16;
|
||||
case MYSQL_TYPE_INT24:
|
||||
case MYSQL_TYPE_LONG:
|
||||
return DatabaseFieldTypes::Int32;
|
||||
case MYSQL_TYPE_LONGLONG:
|
||||
case MYSQL_TYPE_BIT:
|
||||
return DatabaseFieldTypes::Int64;
|
||||
case MYSQL_TYPE_FLOAT:
|
||||
return DatabaseFieldTypes::Float;
|
||||
case MYSQL_TYPE_DOUBLE:
|
||||
return DatabaseFieldTypes::Double;
|
||||
case MYSQL_TYPE_DECIMAL:
|
||||
case MYSQL_TYPE_NEWDECIMAL:
|
||||
return DatabaseFieldTypes::Decimal;
|
||||
case MYSQL_TYPE_TIMESTAMP:
|
||||
case MYSQL_TYPE_DATE:
|
||||
case MYSQL_TYPE_TIME:
|
||||
case MYSQL_TYPE_DATETIME:
|
||||
return DatabaseFieldTypes::Date;
|
||||
case MYSQL_TYPE_TINY_BLOB:
|
||||
case MYSQL_TYPE_MEDIUM_BLOB:
|
||||
case MYSQL_TYPE_LONG_BLOB:
|
||||
case MYSQL_TYPE_BLOB:
|
||||
case MYSQL_TYPE_STRING:
|
||||
case MYSQL_TYPE_VAR_STRING:
|
||||
return DatabaseFieldTypes::Binary;
|
||||
default:
|
||||
LOG_WARN("sql.sql", "MysqlTypeToFieldType(): invalid field type {}", uint32(type));
|
||||
break;
|
||||
}
|
||||
|
||||
return DatabaseFieldTypes::Null;
|
||||
}
|
||||
|
||||
static std::string FieldTypeToString(enum_field_types type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MYSQL_TYPE_BIT: return "BIT";
|
||||
case MYSQL_TYPE_BLOB: return "BLOB";
|
||||
case MYSQL_TYPE_DATE: return "DATE";
|
||||
case MYSQL_TYPE_DATETIME: return "DATETIME";
|
||||
case MYSQL_TYPE_NEWDECIMAL: return "NEWDECIMAL";
|
||||
case MYSQL_TYPE_DECIMAL: return "DECIMAL";
|
||||
case MYSQL_TYPE_DOUBLE: return "DOUBLE";
|
||||
case MYSQL_TYPE_ENUM: return "ENUM";
|
||||
case MYSQL_TYPE_FLOAT: return "FLOAT";
|
||||
case MYSQL_TYPE_GEOMETRY: return "GEOMETRY";
|
||||
case MYSQL_TYPE_INT24: return "INT24";
|
||||
case MYSQL_TYPE_LONG: return "LONG";
|
||||
case MYSQL_TYPE_LONGLONG: return "LONGLONG";
|
||||
case MYSQL_TYPE_LONG_BLOB: return "LONG_BLOB";
|
||||
case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB";
|
||||
case MYSQL_TYPE_NEWDATE: return "NEWDATE";
|
||||
case MYSQL_TYPE_NULL: return "NULL";
|
||||
case MYSQL_TYPE_SET: return "SET";
|
||||
case MYSQL_TYPE_SHORT: return "SHORT";
|
||||
case MYSQL_TYPE_STRING: return "STRING";
|
||||
case MYSQL_TYPE_TIME: return "TIME";
|
||||
case MYSQL_TYPE_TIMESTAMP: return "TIMESTAMP";
|
||||
case MYSQL_TYPE_TINY: return "TINY";
|
||||
case MYSQL_TYPE_TINY_BLOB: return "TINY_BLOB";
|
||||
case MYSQL_TYPE_VAR_STRING: return "VAR_STRING";
|
||||
case MYSQL_TYPE_YEAR: return "YEAR";
|
||||
default: return "-Unknown-";
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeDatabaseFieldMetadata(QueryResultFieldMetadata* meta, MySQLField const* field, uint32 fieldIndex)
|
||||
{
|
||||
meta->TableName = field->org_table;
|
||||
meta->TableAlias = field->table;
|
||||
meta->Name = field->org_name;
|
||||
meta->Alias = field->name;
|
||||
meta->TypeName = FieldTypeToString(field->type);
|
||||
meta->Index = fieldIndex;
|
||||
meta->Type = MysqlTypeToFieldType(field->type);
|
||||
}
|
||||
}
|
||||
|
||||
ResultSet::ResultSet(MySQLResult* result, MySQLField* fields, uint64 rowCount, uint32 fieldCount) :
|
||||
_rowCount(rowCount),
|
||||
_fieldCount(fieldCount),
|
||||
_result(result),
|
||||
_fields(fields)
|
||||
{
|
||||
_fieldMetadata.resize(_fieldCount);
|
||||
_currentRow = new Field[_fieldCount];
|
||||
|
||||
for (uint32 i = 0; i < _fieldCount; i++)
|
||||
{
|
||||
InitializeDatabaseFieldMetadata(&_fieldMetadata[i], &_fields[i], i);
|
||||
_currentRow[i].SetMetadata(&_fieldMetadata[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ResultSet::~ResultSet()
|
||||
{
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
bool ResultSet::NextRow()
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
|
||||
if (!_result)
|
||||
return false;
|
||||
|
||||
row = mysql_fetch_row(_result);
|
||||
if (!row)
|
||||
{
|
||||
CleanUp();
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long* lengths = mysql_fetch_lengths(_result);
|
||||
if (!lengths)
|
||||
{
|
||||
LOG_WARN("sql.sql", "{}:mysql_fetch_lengths, cannot retrieve value lengths. Error {}.", __FUNCTION__, mysql_error(_result->handle));
|
||||
CleanUp();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < _fieldCount; i++)
|
||||
_currentRow[i].SetStructuredValue(row[i], lengths[i]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ResultSet::GetFieldName(uint32 index) const
|
||||
{
|
||||
ASSERT(index < _fieldCount);
|
||||
return _fields[index].name;
|
||||
}
|
||||
|
||||
void ResultSet::CleanUp()
|
||||
{
|
||||
if (_currentRow)
|
||||
{
|
||||
delete[] _currentRow;
|
||||
_currentRow = nullptr;
|
||||
}
|
||||
|
||||
if (_result)
|
||||
{
|
||||
mysql_free_result(_result);
|
||||
_result = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Field const& ResultSet::operator[](std::size_t index) const
|
||||
{
|
||||
ASSERT(index < _fieldCount);
|
||||
return _currentRow[index];
|
||||
}
|
||||
|
||||
void ResultSet::AssertRows(std::size_t sizeRows)
|
||||
{
|
||||
ASSERT(sizeRows == _fieldCount);
|
||||
}
|
||||
|
||||
PreparedResultSet::PreparedResultSet(MySQLStmt* stmt, MySQLResult* result, uint64 rowCount, uint32 fieldCount) :
|
||||
m_rowCount(rowCount),
|
||||
m_rowPosition(0),
|
||||
m_fieldCount(fieldCount),
|
||||
m_rBind(nullptr),
|
||||
m_stmt(stmt),
|
||||
m_metadataResult(result)
|
||||
{
|
||||
if (!m_metadataResult)
|
||||
return;
|
||||
|
||||
if (m_stmt->bind_result_done)
|
||||
{
|
||||
delete[] m_stmt->bind->length;
|
||||
delete[] m_stmt->bind->is_null;
|
||||
}
|
||||
|
||||
m_rBind = new MySQLBind[m_fieldCount];
|
||||
|
||||
//- for future readers wondering where this is freed - mysql_stmt_bind_result moves pointers to these
|
||||
// from m_rBind to m_stmt->bind and it is later freed by the `if (m_stmt->bind_result_done)` block just above here
|
||||
// MYSQL_STMT lifetime is equal to connection lifetime
|
||||
MySQLBool* m_isNull = new MySQLBool[m_fieldCount];
|
||||
unsigned long* m_length = new unsigned long[m_fieldCount];
|
||||
|
||||
memset(m_isNull, 0, sizeof(MySQLBool) * m_fieldCount);
|
||||
memset(m_rBind, 0, sizeof(MySQLBind) * m_fieldCount);
|
||||
memset(m_length, 0, sizeof(unsigned long) * m_fieldCount);
|
||||
|
||||
//- This is where we store the (entire) resultset
|
||||
if (mysql_stmt_store_result(m_stmt))
|
||||
{
|
||||
LOG_WARN("sql.sql", "{}:mysql_stmt_store_result, cannot bind result from MySQL server. Error: {}", __FUNCTION__, mysql_stmt_error(m_stmt));
|
||||
delete[] m_rBind;
|
||||
delete[] m_isNull;
|
||||
delete[] m_length;
|
||||
return;
|
||||
}
|
||||
|
||||
m_rowCount = mysql_stmt_num_rows(m_stmt);
|
||||
|
||||
//- This is where we prepare the buffer based on metadata
|
||||
MySQLField* field = reinterpret_cast<MySQLField*>(mysql_fetch_fields(m_metadataResult));
|
||||
m_fieldMetadata.resize(m_fieldCount);
|
||||
std::size_t rowSize = 0;
|
||||
|
||||
for (uint32 i = 0; i < m_fieldCount; ++i)
|
||||
{
|
||||
uint32 size = SizeForType(&field[i]);
|
||||
rowSize += size;
|
||||
|
||||
InitializeDatabaseFieldMetadata(&m_fieldMetadata[i], &field[i], i);
|
||||
|
||||
m_rBind[i].buffer_type = field[i].type;
|
||||
m_rBind[i].buffer_length = size;
|
||||
m_rBind[i].length = &m_length[i];
|
||||
m_rBind[i].is_null = &m_isNull[i];
|
||||
m_rBind[i].error = nullptr;
|
||||
m_rBind[i].is_unsigned = field[i].flags & UNSIGNED_FLAG;
|
||||
}
|
||||
|
||||
char* dataBuffer = new char[rowSize * m_rowCount];
|
||||
for (uint32 i = 0, offset = 0; i < m_fieldCount; ++i)
|
||||
{
|
||||
m_rBind[i].buffer = dataBuffer + offset;
|
||||
offset += m_rBind[i].buffer_length;
|
||||
}
|
||||
|
||||
//- This is where we bind the bind the buffer to the statement
|
||||
if (mysql_stmt_bind_result(m_stmt, m_rBind))
|
||||
{
|
||||
LOG_WARN("sql.sql", "{}:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: {}", __FUNCTION__, mysql_stmt_error(m_stmt));
|
||||
mysql_stmt_free_result(m_stmt);
|
||||
CleanUp();
|
||||
delete[] m_isNull;
|
||||
delete[] m_length;
|
||||
return;
|
||||
}
|
||||
|
||||
m_rows.resize(uint32(m_rowCount) * m_fieldCount);
|
||||
|
||||
while (_NextRow())
|
||||
{
|
||||
for (uint32 fIndex = 0; fIndex < m_fieldCount; ++fIndex)
|
||||
{
|
||||
m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetMetadata(&m_fieldMetadata[fIndex]);
|
||||
|
||||
unsigned long buffer_length = m_rBind[fIndex].buffer_length;
|
||||
unsigned long fetched_length = *m_rBind[fIndex].length;
|
||||
if (!*m_rBind[fIndex].is_null)
|
||||
{
|
||||
void* buffer = m_stmt->bind[fIndex].buffer;
|
||||
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:
|
||||
// warning - the string will not be null-terminated if there is no space for it in the buffer
|
||||
// when mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED
|
||||
// we cannot blindly null-terminate the data either as it may be retrieved as binary blob and not specifically a string
|
||||
// in this case using Field::GetCString will result in garbage
|
||||
/// @todo: remove Field::GetCString and use std::string_view in C++17
|
||||
if (fetched_length < buffer_length)
|
||||
*((char*)buffer + fetched_length) = '\0';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue((char const*)buffer, fetched_length);
|
||||
|
||||
// move buffer pointer to next part
|
||||
m_stmt->bind[fIndex].buffer = (char*)buffer + rowSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue(nullptr, *m_rBind[fIndex].length);
|
||||
}
|
||||
}
|
||||
|
||||
m_rowPosition++;
|
||||
}
|
||||
|
||||
m_rowPosition = 0;
|
||||
|
||||
/// All data is buffered, let go of mysql c api structures
|
||||
mysql_stmt_free_result(m_stmt);
|
||||
}
|
||||
|
||||
PreparedResultSet::~PreparedResultSet()
|
||||
{
|
||||
CleanUp();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Field* PreparedResultSet::Fetch() const
|
||||
{
|
||||
ASSERT(m_rowPosition < m_rowCount);
|
||||
return const_cast<Field*>(&m_rows[uint32(m_rowPosition) * m_fieldCount]);
|
||||
}
|
||||
|
||||
Field const& PreparedResultSet::operator[](std::size_t index) const
|
||||
{
|
||||
ASSERT(m_rowPosition < m_rowCount);
|
||||
ASSERT(index < m_fieldCount);
|
||||
return m_rows[uint32(m_rowPosition) * m_fieldCount + index];
|
||||
}
|
||||
|
||||
void PreparedResultSet::CleanUp()
|
||||
{
|
||||
if (m_metadataResult)
|
||||
mysql_free_result(m_metadataResult);
|
||||
|
||||
if (m_rBind)
|
||||
{
|
||||
delete[](char*)m_rBind->buffer;
|
||||
delete[] m_rBind;
|
||||
m_rBind = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void PreparedResultSet::AssertRows(std::size_t sizeRows)
|
||||
{
|
||||
ASSERT(m_rowPosition < m_rowCount);
|
||||
ASSERT(sizeRows == m_fieldCount, "> Tuple size != count fields");
|
||||
}
|
||||
151
src/server/database/Database/QueryResult.h
Normal file
151
src/server/database/Database/QueryResult.h
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "DatabaseEnvFwd.h"
|
||||
#include "Define.h"
|
||||
#include "Field.h"
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
template<typename T>
|
||||
struct ResultIterator
|
||||
{
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = T*;
|
||||
using reference = T&;
|
||||
|
||||
explicit ResultIterator(pointer ptr) : _ptr(ptr) { }
|
||||
|
||||
reference operator*() const { return *_ptr; }
|
||||
pointer operator->() { return _ptr; }
|
||||
ResultIterator& operator++() { if (!_ptr->NextRow()) _ptr = nullptr; return *this; }
|
||||
|
||||
bool operator!=(const ResultIterator& right) { return _ptr != right._ptr; }
|
||||
|
||||
private:
|
||||
pointer _ptr;
|
||||
};
|
||||
|
||||
class AC_DATABASE_API ResultSet
|
||||
{
|
||||
public:
|
||||
ResultSet(MySQLResult* result, MySQLField* fields, uint64 rowCount, uint32 fieldCount);
|
||||
~ResultSet();
|
||||
|
||||
bool NextRow();
|
||||
[[nodiscard]] uint64 GetRowCount() const { return _rowCount; }
|
||||
[[nodiscard]] uint32 GetFieldCount() const { return _fieldCount; }
|
||||
[[nodiscard]] std::string GetFieldName(uint32 index) const;
|
||||
|
||||
[[nodiscard]] Field* Fetch() const { return _currentRow; }
|
||||
Field const& operator[](std::size_t index) const;
|
||||
|
||||
template<typename... Ts>
|
||||
inline std::tuple<Ts...> FetchTuple()
|
||||
{
|
||||
AssertRows(sizeof...(Ts));
|
||||
|
||||
std::tuple<Ts...> theTuple = {};
|
||||
|
||||
std::apply([this](Ts&... args)
|
||||
{
|
||||
uint8 index{ 0 };
|
||||
((args = _currentRow[index].Get<Ts>(), index++), ...);
|
||||
}, theTuple);
|
||||
|
||||
return theTuple;
|
||||
}
|
||||
|
||||
auto begin() { return ResultIterator<ResultSet>(this); }
|
||||
static auto end() { return ResultIterator<ResultSet>(nullptr); }
|
||||
|
||||
protected:
|
||||
std::vector<QueryResultFieldMetadata> _fieldMetadata;
|
||||
uint64 _rowCount;
|
||||
Field* _currentRow;
|
||||
uint32 _fieldCount;
|
||||
|
||||
private:
|
||||
void CleanUp();
|
||||
void AssertRows(std::size_t sizeRows);
|
||||
|
||||
MySQLResult* _result;
|
||||
MySQLField* _fields;
|
||||
|
||||
ResultSet(ResultSet const& right) = delete;
|
||||
ResultSet& operator=(ResultSet const& right) = delete;
|
||||
};
|
||||
|
||||
class AC_DATABASE_API PreparedResultSet
|
||||
{
|
||||
public:
|
||||
PreparedResultSet(MySQLStmt* stmt, MySQLResult* result, uint64 rowCount, uint32 fieldCount);
|
||||
~PreparedResultSet();
|
||||
|
||||
bool NextRow();
|
||||
[[nodiscard]] uint64 GetRowCount() const { return m_rowCount; }
|
||||
[[nodiscard]] uint32 GetFieldCount() const { return m_fieldCount; }
|
||||
|
||||
[[nodiscard]] Field* Fetch() const;
|
||||
Field const& operator[](std::size_t index) const;
|
||||
|
||||
template<typename... Ts>
|
||||
inline std::tuple<Ts...> FetchTuple()
|
||||
{
|
||||
AssertRows(sizeof...(Ts));
|
||||
|
||||
std::tuple<Ts...> theTuple = {};
|
||||
|
||||
std::apply([this](Ts&... args)
|
||||
{
|
||||
uint8 index{ 0 };
|
||||
((args = m_rows[uint32(m_rowPosition) * m_fieldCount + index].Get<Ts>(), index++), ...);
|
||||
}, theTuple);
|
||||
|
||||
return theTuple;
|
||||
}
|
||||
|
||||
auto begin() { return ResultIterator<PreparedResultSet>(this); }
|
||||
static auto end() { return ResultIterator<PreparedResultSet>(nullptr); }
|
||||
|
||||
protected:
|
||||
std::vector<QueryResultFieldMetadata> m_fieldMetadata;
|
||||
std::vector<Field> m_rows;
|
||||
uint64 m_rowCount;
|
||||
uint64 m_rowPosition;
|
||||
uint32 m_fieldCount;
|
||||
|
||||
private:
|
||||
MySQLBind* m_rBind;
|
||||
MySQLStmt* m_stmt;
|
||||
MySQLResult* m_metadataResult; ///< Field metadata, returned by mysql_stmt_result_metadata
|
||||
|
||||
void CleanUp();
|
||||
bool _NextRow();
|
||||
|
||||
void AssertRows(std::size_t sizeRows);
|
||||
|
||||
PreparedResultSet(PreparedResultSet const& right) = delete;
|
||||
PreparedResultSet& operator=(PreparedResultSet const& right) = delete;
|
||||
};
|
||||
|
||||
#endif
|
||||
63
src/server/database/Database/SQLOperation.h
Normal file
63
src/server/database/Database/SQLOperation.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "DatabaseEnvFwd.h"
|
||||
#include "Define.h"
|
||||
#include <variant>
|
||||
|
||||
//- Type specifier of our element data
|
||||
enum SQLElementDataType
|
||||
{
|
||||
SQL_ELEMENT_RAW,
|
||||
SQL_ELEMENT_PREPARED
|
||||
};
|
||||
|
||||
//- The element
|
||||
struct SQLElementData
|
||||
{
|
||||
std::variant<PreparedStatementBase*, std::string> element;
|
||||
SQLElementDataType type;
|
||||
};
|
||||
|
||||
class MySQLConnection;
|
||||
|
||||
class AC_DATABASE_API SQLOperation
|
||||
{
|
||||
public:
|
||||
SQLOperation() = default;
|
||||
virtual ~SQLOperation() = default;
|
||||
|
||||
virtual int call()
|
||||
{
|
||||
Execute();
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual bool Execute() = 0;
|
||||
virtual void SetConnection(MySQLConnection* con) { m_conn = con; }
|
||||
|
||||
MySQLConnection* m_conn{nullptr};
|
||||
|
||||
private:
|
||||
SQLOperation(SQLOperation const& right) = delete;
|
||||
SQLOperation& operator=(SQLOperation const& right) = delete;
|
||||
};
|
||||
|
||||
#endif
|
||||
191
src/server/database/Database/Transaction.cpp
Normal file
191
src/server/database/Database/Transaction.cpp
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "Transaction.h"
|
||||
#include "Errors.h"
|
||||
#include "Log.h"
|
||||
#include "MySQLConnection.h"
|
||||
#include "PreparedStatement.h"
|
||||
#include "Timer.h"
|
||||
#include <mysqld_error.h>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
std::mutex TransactionTask::_deadlockLock;
|
||||
|
||||
constexpr Milliseconds DEADLOCK_MAX_RETRY_TIME_MS = 1min;
|
||||
|
||||
//- Append a raw ad-hoc query to the transaction
|
||||
void TransactionBase::Append(std::string_view sql)
|
||||
{
|
||||
SQLElementData data = {};
|
||||
data.type = SQL_ELEMENT_RAW;
|
||||
data.element = std::string(sql);
|
||||
m_queries.emplace_back(data);
|
||||
}
|
||||
|
||||
//- Append a prepared statement to the transaction
|
||||
void TransactionBase::AppendPreparedStatement(PreparedStatementBase* stmt)
|
||||
{
|
||||
SQLElementData data = {};
|
||||
data.type = SQL_ELEMENT_PREPARED;
|
||||
data.element = stmt;
|
||||
m_queries.emplace_back(data);
|
||||
}
|
||||
|
||||
void TransactionBase::Cleanup()
|
||||
{
|
||||
// This might be called by explicit calls to Cleanup or by the auto-destructor
|
||||
if (_cleanedUp)
|
||||
return;
|
||||
|
||||
for (SQLElementData& data : m_queries)
|
||||
{
|
||||
switch (data.type)
|
||||
{
|
||||
case SQL_ELEMENT_PREPARED:
|
||||
{
|
||||
try
|
||||
{
|
||||
PreparedStatementBase* stmt = std::get<PreparedStatementBase*>(data.element);
|
||||
ASSERT(stmt);
|
||||
|
||||
delete stmt;
|
||||
}
|
||||
catch (const std::bad_variant_access& ex)
|
||||
{
|
||||
LOG_FATAL("sql.sql", "> PreparedStatementBase not found in SQLElementData. {}", ex.what());
|
||||
ABORT();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SQL_ELEMENT_RAW:
|
||||
{
|
||||
try
|
||||
{
|
||||
std::get<std::string>(data.element).clear();
|
||||
}
|
||||
catch (const std::bad_variant_access& ex)
|
||||
{
|
||||
LOG_FATAL("sql.sql", "> std::string not found in SQLElementData. {}", ex.what());
|
||||
ABORT();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_queries.clear();
|
||||
_cleanedUp = true;
|
||||
}
|
||||
|
||||
bool TransactionTask::Execute()
|
||||
{
|
||||
int errorCode = TryExecute();
|
||||
|
||||
if (!errorCode)
|
||||
return true;
|
||||
|
||||
if (errorCode == ER_LOCK_DEADLOCK)
|
||||
{
|
||||
std::ostringstream threadIdStream;
|
||||
threadIdStream << std::this_thread::get_id();
|
||||
std::string threadId = threadIdStream.str();
|
||||
|
||||
{
|
||||
// Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other
|
||||
std::lock_guard<std::mutex> lock(_deadlockLock);
|
||||
|
||||
for (Milliseconds loopDuration{}, startMSTime = GetTimeMS(); loopDuration <= DEADLOCK_MAX_RETRY_TIME_MS; loopDuration = GetMSTimeDiffToNow(startMSTime))
|
||||
{
|
||||
if (!TryExecute())
|
||||
return true;
|
||||
|
||||
LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: {} ms, Thread Id: {}", loopDuration.count(), threadId);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERROR("sql.sql", "Fatal deadlocked SQL Transaction, it will not be retried anymore. Thread Id: {}", threadId);
|
||||
}
|
||||
|
||||
// Clean up now.
|
||||
CleanupOnFailure();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int TransactionTask::TryExecute()
|
||||
{
|
||||
return m_conn->ExecuteTransaction(m_trans);
|
||||
}
|
||||
|
||||
void TransactionTask::CleanupOnFailure()
|
||||
{
|
||||
m_trans->Cleanup();
|
||||
}
|
||||
|
||||
bool TransactionWithResultTask::Execute()
|
||||
{
|
||||
int errorCode = TryExecute();
|
||||
if (!errorCode)
|
||||
{
|
||||
m_result.set_value(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (errorCode == ER_LOCK_DEADLOCK)
|
||||
{
|
||||
std::ostringstream threadIdStream;
|
||||
threadIdStream << std::this_thread::get_id();
|
||||
std::string threadId = threadIdStream.str();
|
||||
|
||||
{
|
||||
// Make sure only 1 async thread retries a transaction so they don't keep dead-locking each other
|
||||
std::lock_guard<std::mutex> lock(_deadlockLock);
|
||||
|
||||
for (Milliseconds loopDuration{}, startMSTime = GetTimeMS(); loopDuration <= DEADLOCK_MAX_RETRY_TIME_MS; loopDuration = GetMSTimeDiffToNow(startMSTime))
|
||||
{
|
||||
if (!TryExecute())
|
||||
{
|
||||
m_result.set_value(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_WARN("sql.sql", "Deadlocked SQL Transaction, retrying. Loop timer: {} ms, Thread Id: {}", loopDuration.count(), threadId);
|
||||
}
|
||||
}
|
||||
|
||||
LOG_ERROR("sql.sql", "Fatal deadlocked SQL Transaction, it will not be retried anymore. Thread Id: {}", threadId);
|
||||
}
|
||||
|
||||
// Clean up now.
|
||||
CleanupOnFailure();
|
||||
m_result.set_value(false);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TransactionCallback::InvokeIfReady()
|
||||
{
|
||||
if (m_future.valid() && m_future.wait_for(0s) == std::future_status::ready)
|
||||
{
|
||||
m_callback(m_future.get());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
127
src/server/database/Database/Transaction.h
Normal file
127
src/server/database/Database/Transaction.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* 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 "DatabaseEnvFwd.h"
|
||||
#include "Define.h"
|
||||
#include "SQLOperation.h"
|
||||
#include "StringFormat.h"
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
/*! Transactions, high level class. */
|
||||
class AC_DATABASE_API TransactionBase
|
||||
{
|
||||
friend class TransactionTask;
|
||||
friend class MySQLConnection;
|
||||
|
||||
template <typename T>
|
||||
friend class DatabaseWorkerPool;
|
||||
|
||||
public:
|
||||
TransactionBase() = default;
|
||||
virtual ~TransactionBase() { Cleanup(); }
|
||||
|
||||
void Append(std::string_view sql);
|
||||
|
||||
template<typename... Args>
|
||||
void Append(std::string_view sql, Args&&... args)
|
||||
{
|
||||
Append(Acore::StringFormat(sql, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t GetSize() const { return m_queries.size(); }
|
||||
|
||||
protected:
|
||||
void AppendPreparedStatement(PreparedStatementBase* statement);
|
||||
void Cleanup();
|
||||
std::vector<SQLElementData> m_queries;
|
||||
|
||||
private:
|
||||
bool _cleanedUp{false};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Transaction : public TransactionBase
|
||||
{
|
||||
public:
|
||||
using TransactionBase::Append;
|
||||
|
||||
void Append(PreparedStatement<T>* statement)
|
||||
{
|
||||
AppendPreparedStatement(statement);
|
||||
}
|
||||
};
|
||||
|
||||
/*! Low level class*/
|
||||
class AC_DATABASE_API TransactionTask : public SQLOperation
|
||||
{
|
||||
template <class T>
|
||||
friend class DatabaseWorkerPool;
|
||||
|
||||
friend class DatabaseWorker;
|
||||
friend class TransactionCallback;
|
||||
|
||||
public:
|
||||
TransactionTask(std::shared_ptr<TransactionBase> trans) : m_trans(std::move(trans)) { }
|
||||
~TransactionTask() override = default;
|
||||
|
||||
protected:
|
||||
bool Execute() override;
|
||||
int TryExecute();
|
||||
void CleanupOnFailure();
|
||||
|
||||
std::shared_ptr<TransactionBase> m_trans;
|
||||
static std::mutex _deadlockLock;
|
||||
};
|
||||
|
||||
class AC_DATABASE_API TransactionWithResultTask : public TransactionTask
|
||||
{
|
||||
public:
|
||||
TransactionWithResultTask(std::shared_ptr<TransactionBase> trans) : TransactionTask(trans) { }
|
||||
|
||||
TransactionFuture GetFuture() { return m_result.get_future(); }
|
||||
|
||||
protected:
|
||||
bool Execute() override;
|
||||
|
||||
TransactionPromise m_result;
|
||||
};
|
||||
|
||||
class AC_DATABASE_API TransactionCallback
|
||||
{
|
||||
public:
|
||||
TransactionCallback(TransactionFuture&& future) : m_future(std::move(future)) { }
|
||||
TransactionCallback(TransactionCallback&&) = default;
|
||||
|
||||
TransactionCallback& operator=(TransactionCallback&&) = default;
|
||||
|
||||
void AfterComplete(std::function<void(bool)> callback) &
|
||||
{
|
||||
m_callback = std::move(callback);
|
||||
}
|
||||
|
||||
bool InvokeIfReady();
|
||||
|
||||
TransactionFuture m_future;
|
||||
std::function<void(bool)> m_callback;
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user