From 3d113feb2861512e1fcacc4a0d8f2386fa81fa1e Mon Sep 17 00:00:00 2001 From: Mordred Date: Sat, 30 Sep 2017 23:51:01 +0200 Subject: [PATCH] New mysql system implemented, strongly borrowed from TrinityCore --- bin/config/settings_zone.xml | 2 + src/libraries | 2 +- .../Database/CharaDbConnection.cpp | 24 ++ .../Database/CharaDbConnection.h | 36 +++ .../Server_Common/Database/DbConnection.cpp | 244 +++++++++++++++ .../Server_Common/Database/DbConnection.h | 107 +++++++ .../Server_Common/Database/DbLoader.cpp | 108 +++++++ src/servers/Server_Common/Database/DbLoader.h | 52 ++++ .../Server_Common/Database/DbWorker.cpp | 39 +++ src/servers/Server_Common/Database/DbWorker.h | 38 +++ .../Server_Common/Database/DbWorkerPool.cpp | 284 ++++++++++++++++++ .../Server_Common/Database/DbWorkerPool.h | 89 ++++++ .../Server_Common/Database/Operation.h | 41 +++ .../Database/PreparedStatement.cpp | 149 +++++++++ .../Database/PreparedStatement.h | 83 +++++ .../Server_Common/Database/StatementTask.cpp | 72 +++++ .../Server_Common/Database/StatementTask.h | 54 ++++ .../Server_Common/Util/LockedWaitQueue.h | 11 +- src/servers/Server_Zone/ServerZone.cpp | 229 ++++++++------ 19 files changed, 1566 insertions(+), 98 deletions(-) create mode 100644 src/servers/Server_Common/Database/CharaDbConnection.cpp create mode 100644 src/servers/Server_Common/Database/CharaDbConnection.h create mode 100644 src/servers/Server_Common/Database/DbConnection.cpp create mode 100644 src/servers/Server_Common/Database/DbConnection.h create mode 100644 src/servers/Server_Common/Database/DbLoader.cpp create mode 100644 src/servers/Server_Common/Database/DbLoader.h create mode 100644 src/servers/Server_Common/Database/DbWorker.cpp create mode 100644 src/servers/Server_Common/Database/DbWorker.h create mode 100644 src/servers/Server_Common/Database/DbWorkerPool.cpp create mode 100644 src/servers/Server_Common/Database/DbWorkerPool.h create mode 100644 src/servers/Server_Common/Database/Operation.h create mode 100644 src/servers/Server_Common/Database/PreparedStatement.cpp create mode 100644 src/servers/Server_Common/Database/PreparedStatement.h create mode 100644 src/servers/Server_Common/Database/StatementTask.cpp create mode 100644 src/servers/Server_Common/Database/StatementTask.h diff --git a/bin/config/settings_zone.xml b/bin/config/settings_zone.xml index 2b0a3a4a..fee76340 100644 --- a/bin/config/settings_zone.xml +++ b/bin/config/settings_zone.xml @@ -14,6 +14,8 @@ root sapphire + 2 + 2 diff --git a/src/libraries b/src/libraries index 5512f4d4..5a9881ae 160000 --- a/src/libraries +++ b/src/libraries @@ -1 +1 @@ -Subproject commit 5512f4d48186cf0796449215a5b03d3229ec9b31 +Subproject commit 5a9881aee513b1daed1598755225fb2a5b59221b diff --git a/src/servers/Server_Common/Database/CharaDbConnection.cpp b/src/servers/Server_Common/Database/CharaDbConnection.cpp new file mode 100644 index 00000000..ede6b909 --- /dev/null +++ b/src/servers/Server_Common/Database/CharaDbConnection.cpp @@ -0,0 +1,24 @@ +#include "CharaDbConnection.h" +#include + +Core::Db::CharaDbConnection::CharaDbConnection( ConnectionInfo& connInfo ) : DbConnection( connInfo ) +{ +} + +Core::Db::CharaDbConnection::CharaDbConnection( Core::LockedWaitQueue< Operation * >* q, + ConnectionInfo& connInfo) : DbConnection( q, connInfo ) +{ +} + +Core::Db::CharaDbConnection::~CharaDbConnection() +{ +} + +void Core::Db::CharaDbConnection::doPrepareStatements() +{ + if( !m_reconnecting ) + m_stmts.resize( MAX_STATEMENTS ); + + prepareStatement( CHAR_INS_TEST, "INSERT INTO zoneservers ( id, ip, port ) VALUES ( ?, ?, ?);", CONNECTION_BOTH ); + +} \ No newline at end of file diff --git a/src/servers/Server_Common/Database/CharaDbConnection.h b/src/servers/Server_Common/Database/CharaDbConnection.h new file mode 100644 index 00000000..847427f1 --- /dev/null +++ b/src/servers/Server_Common/Database/CharaDbConnection.h @@ -0,0 +1,36 @@ +#ifndef SAPPHIRE_CHARACONNECTION_H +#define SAPPHIRE_CHARACONNECTION_H + +#include "DbConnection.h" + +namespace Core +{ +namespace Db +{ +class DbConnectionInfo; + +enum CharaDbStatements : uint32_t +{ + CHAR_INS_TEST, + + MAX_STATEMENTS +}; + +class CharaDbConnection : public DbConnection +{ +public: + typedef CharaDbStatements Statements; + + CharaDbConnection( ConnectionInfo& connInfo ); + CharaDbConnection( Core::LockedWaitQueue< Operation* >* q, ConnectionInfo &connInfo ); + + ~CharaDbConnection(); + + void doPrepareStatements() override; + +}; + +} +} + +#endif //SAPPHIRE_CHARACONNECTION_H diff --git a/src/servers/Server_Common/Database/DbConnection.cpp b/src/servers/Server_Common/Database/DbConnection.cpp new file mode 100644 index 00000000..f3045d3f --- /dev/null +++ b/src/servers/Server_Common/Database/DbConnection.cpp @@ -0,0 +1,244 @@ +#include "DbConnection.h" +#include "DbWorker.h" +#include "src/libraries/sapphire/mysqlConnector/MySqlConnector.h" + +#include "src/servers/Server_Common/Logging/Logger.h" +#include "PreparedStatement.h" + + +extern Core::Logger g_log; + +Core::Db::DbConnection::DbConnection( ConnectionInfo &connInfo ) : + m_reconnecting( false ), + m_prepareError( false ), + m_queue( nullptr ), + m_pConnection( nullptr ), + m_connectionInfo( connInfo ), + m_connectionFlags( CONNECTION_SYNCH ) +{ + +} + +Core::Db::DbConnection::DbConnection( Core::LockedWaitQueue* queue, Core::Db::ConnectionInfo& connInfo ) : + m_reconnecting( false ), + m_prepareError( false ), + m_queue( queue ), + m_pConnection( nullptr ), + m_connectionInfo( connInfo ), + m_connectionFlags( CONNECTION_ASYNC ) +{ + m_worker = std::unique_ptr< DbWorker >( new DbWorker( m_queue, this ) ); +} + +Core::Db::DbConnection::~DbConnection() +{ + close(); +} + +void Core::Db::DbConnection::close() +{ + m_worker.reset(); + m_stmts.clear(); + + if( m_pConnection ) + { + m_pConnection->close(); + m_pConnection.reset(); + } + + +} + +uint32_t Core::Db::DbConnection::open() +{ + Mysql::MySqlBase base; + Mysql::optionMap options; + options[ MYSQL_OPT_RECONNECT ] = "1"; + options[ MYSQL_SET_CHARSET_NAME ] = "utf8"; + + try + { + m_pConnection = std::shared_ptr< Mysql::Connection >( base.connect( m_connectionInfo.host, + m_connectionInfo.user, + m_connectionInfo.password, + options, + m_connectionInfo.port ) ); + m_pConnection->setSchema( m_connectionInfo.database ); + } + catch( std::runtime_error& e ) + { + g_log.error( e.what() ); + return 1; + } + + return 0; +} + +uint32_t Core::Db::DbConnection::getLastError() +{ + return m_pConnection->getErrorNo(); +} + +void Core::Db::DbConnection::ping() +{ + m_pConnection->ping(); +} + +bool Core::Db::DbConnection::lockIfReady() +{ + return m_mutex.try_lock(); +} + +void Core::Db::DbConnection::unlock() +{ + m_mutex.unlock(); +} + +void Core::Db::DbConnection::beginTransaction() +{ + m_pConnection->beginTransaction(); +} + +void Core::Db::DbConnection::rollbackTransaction() +{ + m_pConnection->rollbackTransaction(); +} + +void Core::Db::DbConnection::commitTransaction() +{ + m_pConnection->commitTransaction(); +} + +bool Core::Db::DbConnection::execute( const std::string& sql ) +{ + try + { + Mysql::Statement* stmt( m_pConnection->createStatement() ); + bool result = stmt->execute( sql ); + return result; + } + catch( std::runtime_error& e ) + { + g_log.error( e.what() ); + return false; + } +} + +Mysql::ResultSet *Core::Db::DbConnection::query( const std::string& sql ) +{ + try + { + Mysql::Statement* stmt( m_pConnection->createStatement() ); + Mysql::ResultSet* result = stmt->executeQuery( sql ); + return result; + } + catch( std::runtime_error& e ) + { + g_log.error( e.what() ); + return nullptr; + } +} + + +Mysql::ResultSet* Core::Db::DbConnection::query( Core::Db::PreparedStatement* stmt ) +{ + Mysql::ResultSet* res = nullptr; + if( !stmt ) + return nullptr; + + uint32_t index = stmt->getIndex(); + + Mysql::PreparedStatement* pStmt = getPreparedStatement( index ); + + if( !pStmt ) + return nullptr; + + stmt->setMysqlPS( pStmt ); + try + { + stmt->bindParameters(); + return pStmt->executeQuery(); + } + catch( std::runtime_error& e ) + { + g_log.error( e.what() ); + return nullptr; + } + +} + +bool Core::Db::DbConnection::execute( Core::Db::PreparedStatement* stmt ) +{ + if( !stmt ) + return false; + + uint32_t index = stmt->getIndex(); + + Mysql::PreparedStatement* pStmt = getPreparedStatement( index ); + + if( !pStmt ) + return false; + + stmt->setMysqlPS( pStmt ); + try + { + stmt->bindParameters(); + return pStmt->execute(); + } + catch( std::runtime_error& e ) + { + g_log.error( e.what() ); + return nullptr; + } +} + +Mysql::PreparedStatement* Core::Db::DbConnection::getPreparedStatement( uint32_t index ) +{ + assert( index < m_stmts.size() ); + Mysql::PreparedStatement* ret = m_stmts[index].get(); + if( !ret ) + nullptr; + + return ret; +} + +void Core::Db::DbConnection::prepareStatement( uint32_t index, const std::string &sql, Core::Db::ConnectionFlags flags ) +{ + m_queries.insert( PreparedStatementMap::value_type( index, std::make_pair( sql, 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::PreparedStatement* pStmt = nullptr; + + try + { + pStmt = m_pConnection->prepareStatement( sql ); + } + catch( std::runtime_error& e ) + { + g_log.error( e.what() ); + m_prepareError = true; + } + + m_stmts[index] = std::unique_ptr< Mysql::PreparedStatement >( pStmt ); + +} + +bool Core::Db::DbConnection::prepareStatements() +{ + doPrepareStatements(); + return !m_prepareError; +} + + + + + + diff --git a/src/servers/Server_Common/Database/DbConnection.h b/src/servers/Server_Common/Database/DbConnection.h new file mode 100644 index 00000000..0e623dd0 --- /dev/null +++ b/src/servers/Server_Common/Database/DbConnection.h @@ -0,0 +1,107 @@ +#ifndef _SAPPHIRE_DBCONNECTION_H +#define _SAPPHIRE_DBCONNECTION_H + +#include +#include +#include +#include +#include +#include "src/servers/Server_Common/Util/LockedWaitQueue.h" +#include + +namespace Mysql +{ + class Connection; + class ResultSet; + class PreparedResultSet; + class PreparedStatement; +} + +namespace Core +{ +namespace Db +{ + class DatabaseWorker; + class PreparedStatement; + class Operation; + class DbWorker; + + enum ConnectionFlags + { + CONNECTION_ASYNC = 0x1, + CONNECTION_SYNCH = 0x2, + CONNECTION_BOTH = CONNECTION_ASYNC | CONNECTION_SYNCH + }; + + struct ConnectionInfo + { + std::string user; + std::string password; + std::string database; + std::string host; + uint16_t port; + uint8_t syncThreads; + uint8_t asyncThreads; + }; + + typedef std::map< uint32_t, std::pair< std::string, ConnectionFlags > > PreparedStatementMap; + + class DbConnection + { + public: + // Constructor for synchronous connections. + DbConnection( ConnectionInfo& connInfo ); + // Constructor for asynchronous connections. + DbConnection( Core::LockedWaitQueue< Operation* >* queue, ConnectionInfo& connInfo ); + virtual ~DbConnection(); + + virtual uint32_t open(); + void close(); + + bool prepareStatements(); + + bool execute( const std::string& sql ); + bool execute( PreparedStatement* stmt ); + Mysql::ResultSet* query( const std::string& sql ); + Mysql::ResultSet* query( PreparedStatement* stmt ); + + void beginTransaction(); + void rollbackTransaction(); + void commitTransaction(); + + void ping(); + + uint32_t getLastError(); + bool lockIfReady(); + + void unlock(); + + std::shared_ptr< Mysql::Connection > getConnection() { return m_pConnection; } + Mysql::PreparedStatement* getPreparedStatement( uint32_t index ); + void prepareStatement( uint32_t index, const std::string& sql, ConnectionFlags flags ); + + virtual void doPrepareStatements() = 0; + + protected: + std::vector< std::unique_ptr< Mysql::PreparedStatement > > m_stmts; + PreparedStatementMap m_queries; + bool m_reconnecting; + bool m_prepareError; + + private: + LockedWaitQueue< Operation* >* m_queue; + std::unique_ptr< DbWorker > m_worker; + std::shared_ptr< Mysql::Connection > m_pConnection; + ConnectionInfo& m_connectionInfo; + ConnectionFlags m_connectionFlags; + std::mutex m_mutex; + + DbConnection( DbConnection const& right ) = delete; + DbConnection& operator=( DbConnection const& right ) = delete; + }; + +} +} + + +#endif diff --git a/src/servers/Server_Common/Database/DbLoader.cpp b/src/servers/Server_Common/Database/DbLoader.cpp new file mode 100644 index 00000000..5e537856 --- /dev/null +++ b/src/servers/Server_Common/Database/DbLoader.cpp @@ -0,0 +1,108 @@ +#include "DbLoader.h" +#include +#include "CharaDbConnection.h" +#include "DbWorkerPool.h" +#include "src/servers/Server_Common/Logging/Logger.h" + +extern Core::Logger g_log; + +Core::Db::DbLoader::DbLoader() +{ +} + +template +Core::Db::DbLoader& Core::Db::DbLoader::addDb( Core::Db::DbWorkerPool< T >& pool, const ConnectionInfo& info ) +{ + + m_open.push([this, info, &pool]() -> bool + { + const uint8_t asyncThreads = info.asyncThreads; + const uint8_t synchThreads = info.syncThreads; + + if( asyncThreads < 1 || asyncThreads > 32 ) + { + g_log.error( "database: invalid number of worker threads specified. Please pick a value between 1 and 32." ); + return false; + } + + pool.setConnectionInfo( info, asyncThreads, synchThreads ); + if( uint32_t error = pool.open() ) + { + // Database does not exist + if( error == ER_BAD_DB_ERROR ) + { + return false; + } + + if( error ) + { + g_log.error( "DatabasePool failed to open." ); + return false; + } + } + + m_close.push( [&pool] { pool.close(); } ); + return true; + }); + + + m_prepare.push([this, info, &pool]() -> bool + { + if( !pool.prepareStatements() ) + { + g_log.error( "Could not prepare statements of the database, see log for details." ); + return false; + } + return true; + }); + + return *this; +} + +bool Core::Db::DbLoader::initDbs() +{ + if( !openDatabases() ) + return false; + + if( !prepareStatements() ) + return false; + + return true; +} + +bool Core::Db::DbLoader::openDatabases() +{ + return process( m_open ); +} + +bool Core::Db::DbLoader::prepareStatements() +{ + return process( m_prepare ); +} + +bool Core::Db::DbLoader::process( std::queue< Predicate >& queue ) +{ + while( !queue.empty() ) + { + if( !queue.front()() ) + { + // Close all open databases which have a registered close operation + while( !m_close.empty() ) + { + m_close.top()(); + m_close.pop(); + } + + return false; + } + + queue.pop(); + } + return true; +} + +template +Core::Db::DbLoader& + Core::Db::DbLoader::addDb< Core::Db::CharaDbConnection >( Core::Db::DbWorkerPool< Core::Db::CharaDbConnection >&, + const ConnectionInfo& ); + diff --git a/src/servers/Server_Common/Database/DbLoader.h b/src/servers/Server_Common/Database/DbLoader.h new file mode 100644 index 00000000..acbb6944 --- /dev/null +++ b/src/servers/Server_Common/Database/DbLoader.h @@ -0,0 +1,52 @@ +#ifndef SAPPHIRE_DBLOADER_H +#define SAPPHIRE_DBLOADER_H + +#include +#include +#include +#include +#include +#include "DbConnection.h" + +namespace Core +{ + namespace Db + { + + template< class T > + class DbWorkerPool; + + class DbLoader + { + public: + DbLoader(); + + template< class T > + DbLoader& addDb( DbWorkerPool< T >& pool, const ConnectionInfo& info ); + + bool initDbs(); + + enum DbTypeFlags + { + DATABASE_NONE = 0, + DATABASE_CHARACTER = 1, + DATABASE_MASK_ALL = DATABASE_CHARACTER + }; + + private: + bool openDatabases(); + bool prepareStatements(); + + using Predicate = std::function< bool() >; + using Closer = std::function< void() >; + + bool process( std::queue< Predicate >& queue ); + + std::queue< Predicate > m_open; + std::queue< Predicate > m_prepare; + std::stack< Closer > m_close; + }; +} +} + +#endif //SAPPHIRE_DBLOADER_H diff --git a/src/servers/Server_Common/Database/DbWorker.cpp b/src/servers/Server_Common/Database/DbWorker.cpp new file mode 100644 index 00000000..47ce7947 --- /dev/null +++ b/src/servers/Server_Common/Database/DbWorker.cpp @@ -0,0 +1,39 @@ +#include "DbWorker.h" +#include "Operation.h" +#include + +Core::Db::DbWorker::DbWorker( Core::LockedWaitQueue< Operation* >* newQueue, DbConnection* pConn ) +{ + m_pConn = pConn; + m_queue = newQueue; + m_cancelationToken = false; + m_workerThread = std::thread( &DbWorker::workerThread, this ); +} + +Core::Db::DbWorker::~DbWorker() +{ + m_cancelationToken = true; + m_queue->cancel(); + m_workerThread.join(); +} + +void Core::Db::DbWorker::workerThread() +{ + if( !m_queue ) + return; + + while( true ) + { + Operation* operation = nullptr; + + m_queue->waitAndPop( operation ); + + if( m_cancelationToken || !operation ) + return; + + operation->setConnection( m_pConn ); + operation->call(); + + delete operation; + } +} diff --git a/src/servers/Server_Common/Database/DbWorker.h b/src/servers/Server_Common/Database/DbWorker.h new file mode 100644 index 00000000..be114888 --- /dev/null +++ b/src/servers/Server_Common/Database/DbWorker.h @@ -0,0 +1,38 @@ +#ifndef SAPPHIRE_DBWORKER_H +#define SAPPHIRE_DBWORKER_H + +#include +#include +#include + +namespace Core +{ +namespace Db +{ + class DbConnection; + class Operation; + + class DbWorker + { + public: + DbWorker( LockedWaitQueue< Operation* >* newQueue, DbConnection* connection ); + ~DbWorker(); + + private: + LockedWaitQueue< Operation* >* m_queue; + DbConnection* m_pConn; + + void workerThread(); + std::thread m_workerThread; + + std::atomic< bool > m_cancelationToken; + + DbWorker( DbWorker const& right ) = delete; + DbWorker& operator=( DbWorker const& right ) = delete; + }; +} +} + + + +#endif //SAPPHIRE_DBWORKER_H diff --git a/src/servers/Server_Common/Database/DbWorkerPool.cpp b/src/servers/Server_Common/Database/DbWorkerPool.cpp new file mode 100644 index 00000000..a23e8a4f --- /dev/null +++ b/src/servers/Server_Common/Database/DbWorkerPool.cpp @@ -0,0 +1,284 @@ +#include "DbWorkerPool.h" +#include "DbConnection.h" +#include "PreparedStatement.h" +#include +#include "StatementTask.h" +#include "Operation.h" +#include "CharaDbConnection.h" + +#include +extern Core::Logger g_log; + +class PingOperation : public Core::Db::Operation +{ + bool execute() override + { + m_pConn->ping(); + return true; + } +}; + +template< class T > +Core::Db::DbWorkerPool::DbWorkerPool() + : m_queue( new Core::LockedWaitQueue< Operation* >() ), + m_asyncThreads( 0 ), + m_synchThreads( 0 ) +{ +} + +template< class T > +Core::Db::DbWorkerPool< T >::~DbWorkerPool() +{ + m_queue->cancel(); +} + +template< class T > +void Core::Db::DbWorkerPool::setConnectionInfo( const ConnectionInfo& info, + uint8_t asyncThreads, + uint8_t synchThreads) +{ + m_connectionInfo = info; + m_asyncThreads = asyncThreads; + m_synchThreads = synchThreads; +} + +template< class T > +uint32_t Core::Db::DbWorkerPool< T >::open() +{ + g_log.info( "[DbPool] Opening DatabasePool " + getDatabaseName() + + " Asynchronous connections: " + std::to_string( m_asyncThreads ) + + " Synchronous connections: " + std::to_string( m_synchThreads ) ); + + uint32_t error = openConnections( IDX_ASYNC, m_asyncThreads ); + + if( error ) + return error; + + error = openConnections( IDX_SYNCH, m_synchThreads ); + + if( !error ) + { + g_log.info( "[DbPool] DatabasePool " + getDatabaseName() + " opened successfully. " + + std::to_string( ( m_connections[IDX_SYNCH].size() + m_connections[IDX_ASYNC].size() ) ) + + " total connections running." ); + } + + return error; +} + +template< class T > +void Core::Db::DbWorkerPool< T >::close() +{ + g_log.info("[DbPool] Closing down DatabasePool " + getDatabaseName() ); + m_connections[IDX_ASYNC].clear(); + m_connections[IDX_SYNCH].clear(); + g_log.info("[DbPool] All connections on DatabasePool " + getDatabaseName() + "closed." ); +} + +template< class T > +bool Core::Db::DbWorkerPool::prepareStatements() +{ + for( auto& connections : m_connections ) + for( auto& connection : connections ) + { + connection->lockIfReady(); + if( !connection->prepareStatements() ) + { + connection->unlock(); + close(); + return false; + } + else + connection->unlock(); + } + + return true; +} + +template< class T > +Mysql::ResultSet* Core::Db::DbWorkerPool::query( const std::string& sql, T* connection ) +{ + if( !connection ) + connection = getFreeConnection(); + + Mysql::ResultSet* result = connection->query( sql ); + connection->unlock(); + + return result; +} + +template< class T > +Mysql::ResultSet* Core::Db::DbWorkerPool::query( PreparedStatement* stmt ) +{ + auto connection = getFreeConnection(); + Mysql::ResultSet* ret = connection->query( stmt ); + connection->unlock(); + + return ret; +} + +template< class T > +Core::Db::PreparedStatement* Core::Db::DbWorkerPool< T >::getPreparedStatement( PreparedStatementIndex index ) +{ + return new PreparedStatement( index ); +} + +template< class T > +void Core::Db::DbWorkerPool< T >::escapeString( std::string& str ) +{ + if( str.empty() ) + return; + + char* buf = new char[str.size() * 2 + 1]; + escapeString( buf, str.c_str(), str.size() ); + str = buf; + delete[] buf; +} + +template< class T > +void Core::Db::DbWorkerPool< T >::keepAlive() +{ + for( auto& connection : m_connections[IDX_SYNCH] ) + { + if( connection->lockIfReady() ) + { + connection->ping(); + connection->unlock(); + } + } + + const auto count = m_connections[IDX_ASYNC].size(); + for( uint8_t i = 0; i < count; ++i ) + enqueue( new PingOperation ); +} + +template< class T > +uint32_t Core::Db::DbWorkerPool< T >::openConnections( InternalIndex type, uint8_t numConnections ) +{ + for( uint8_t i = 0; i < numConnections; ++i ) + { + // Create the connection + auto connection = [&] { + switch (type) + { + case IDX_ASYNC: + return std::unique_ptr( new T( m_queue.get(), m_connectionInfo ) ); + case IDX_SYNCH: + return std::unique_ptr( new T( m_connectionInfo ) ); + default: + return std::unique_ptr(); + } + }(); + + if( uint32_t error = connection->open() ) + { + // Failed to open a connection or invalid version, abort and cleanup + m_connections[type].clear(); + return error; + } + else + { + m_connections[type].push_back( std::move( connection ) ); + } + } + + return 0; +} + +template< class T > +unsigned long Core::Db::DbWorkerPool< T >::escapeString( char *to, const char *from, unsigned long length ) +{ + if (!to || !from || !length) + return 0; + + return mysql_real_escape_string( + m_connections[IDX_SYNCH].front()->getConnection()->getRawCon(), to, from, length); +} + +template< class T > +void Core::Db::DbWorkerPool< T >::enqueue( Operation* op ) +{ + m_queue->push( op ); +} + +template< class T > +T* Core::Db::DbWorkerPool< T >::getFreeConnection() +{ + uint8_t i = 0; + const auto numCons = m_connections[IDX_SYNCH].size(); + T* connection = nullptr; + + while( true ) + { + connection = m_connections[IDX_SYNCH][i++ % numCons].get(); + + if (connection->lockIfReady()) + break; + } + + return connection; +} + +template< class T > +const std::string& Core::Db::DbWorkerPool< T >::getDatabaseName() const +{ + return m_connectionInfo.database; +} + +template< class T > +void Core::Db::DbWorkerPool< T >::execute( const std::string& sql ) +{ + StatementTask* task = new StatementTask( sql ); + enqueue( task ); +} + +template< class T > +void Core::Db::DbWorkerPool< T >::execute( PreparedStatement* stmt ) +{ + PreparedStatementTask* task = new PreparedStatementTask(stmt); + enqueue( task ); +} + +template< class T > +void Core::Db::DbWorkerPool< T >::directExecute( const std::string& sql ) +{ + T* connection = getFreeConnection(); + connection->execute( sql ); + connection->unlock(); +} + +template< class T > +void Core::Db::DbWorkerPool< T >::directExecute( PreparedStatement* stmt ) +{ + T* connection = getFreeConnection(); + connection->execute( stmt ); + connection->unlock(); + + //! Delete proxy-class. Not needed anymore + delete stmt; +} + +/* +template +void DatabaseWorkerPool::ExecuteOrAppend(SQLTransaction& trans, const char* sql) +{ + if (!trans) + Execute(sql); + else + trans->Append(sql); +} + +template +void DatabaseWorkerPool::ExecuteOrAppend(SQLTransaction& trans, PreparedStatement* stmt) +{ + if (!trans) + Execute(stmt); + else + trans->Append(stmt); +} +*/ + +template class Core::Db::DbWorkerPool< Core::Db::CharaDbConnection >; +//template class TC_DATABASE_API DatabaseWorkerPool; +//template class TC_DATABASE_API DatabaseWorkerPool; +//template class TC_DATABASE_API DatabaseWorkerPool; diff --git a/src/servers/Server_Common/Database/DbWorkerPool.h b/src/servers/Server_Common/Database/DbWorkerPool.h new file mode 100644 index 00000000..ce4c1dec --- /dev/null +++ b/src/servers/Server_Common/Database/DbWorkerPool.h @@ -0,0 +1,89 @@ +#ifndef SAPPHIRE_DBWORKERPOOL_H +#define SAPPHIRE_DBWORKERPOOL_H + +#include +#include +#include +#include +#include +#include "DbConnection.h" +namespace Core +{ +namespace Db +{ + +template< typename T > +class LockedWaitQueue; +class Operation; +class PreparedStatement; +struct ConnectionInfo; + +template< class T > +class DbWorkerPool +{ +private: + enum InternalIndex + { + IDX_ASYNC, + IDX_SYNCH, + IDX_SIZE + }; + +public: + DbWorkerPool(); + + ~DbWorkerPool(); + + void setConnectionInfo( const ConnectionInfo& info, uint8_t asyncThreads, uint8_t synchThreads); + + uint32_t open(); + + void close(); + + bool prepareStatements(); + + inline ConnectionInfo getConnectionInfo() const + { + return m_connectionInfo; + } + + // Async execution + void execute( const std::string& sql ); + void execute( PreparedStatement* stmt ); + + // Sync execution + void directExecute( const std::string& sql ); + void directExecute( PreparedStatement* stmt ); + Mysql::ResultSet* query( const std::string& sql, T* connection = nullptr ); + Mysql::ResultSet* query( PreparedStatement* stmt ); + + typedef typename T::Statements PreparedStatementIndex; + + PreparedStatement* getPreparedStatement( PreparedStatementIndex index ); + + void escapeString( std::string& str ); + + void keepAlive(); + +private: + uint32_t openConnections( InternalIndex type, uint8_t numConnections ); + + unsigned long escapeString( char *to, const char *from, unsigned long length ); + + void enqueue( Operation* op ); + + T* getFreeConnection(); + + const std::string& getDatabaseName() const; + + std::unique_ptr< Core::LockedWaitQueue< Operation* > > m_queue; + std::array< std::vector< std::unique_ptr< T > >, IDX_SIZE > m_connections; + ConnectionInfo m_connectionInfo; + uint8_t m_asyncThreads; + uint8_t m_synchThreads; +}; + +} +} + +#endif //SAPPHIRE_DBWORKERPOOL_H diff --git a/src/servers/Server_Common/Database/Operation.h b/src/servers/Server_Common/Database/Operation.h new file mode 100644 index 00000000..fff52e73 --- /dev/null +++ b/src/servers/Server_Common/Database/Operation.h @@ -0,0 +1,41 @@ +#ifndef SAPPHIRE_OPERATION_H +#define SAPPHIRE_OPERATION_H + +namespace Mysql +{ + class Connection; +} + +namespace Core +{ +namespace Db +{ + class DbConnection; + class PreparedStatement; + + class Operation + { + public: + Operation() : m_pConn( nullptr ) { } + virtual ~Operation() { } + + virtual int call() + { + execute(); + return 0; + } + virtual bool execute() = 0; + virtual void setConnection( DbConnection* pCon ) { m_pConn = pCon; } + + DbConnection* m_pConn; + + private: + Operation( Operation const& right ) = delete; + Operation& operator=( Operation const& right ) = delete; + }; + + +} +} + +#endif //SAPPHIRE_OPERATION_H diff --git a/src/servers/Server_Common/Database/PreparedStatement.cpp b/src/servers/Server_Common/Database/PreparedStatement.cpp new file mode 100644 index 00000000..dac5a4b0 --- /dev/null +++ b/src/servers/Server_Common/Database/PreparedStatement.cpp @@ -0,0 +1,149 @@ +#include "PreparedStatement.h" +#include "DbConnection.h" + +#include + +#include + +Core::Db::PreparedStatement::PreparedStatement( uint32_t index ) : + m_stmt( nullptr ), + m_index( index ) { } + +Core::Db::PreparedStatement::~PreparedStatement() { } + +void Core::Db::PreparedStatement::bindParameters() +{ + assert( m_stmt ); + + uint8_t i = 1; + for( ; i < m_statementData.size(); i++ ) + { + switch( m_statementData[i].type) + { + case TYPE_BOOL: + m_stmt->setBoolean( i, m_statementData[i].data.boolean ); + break; + case TYPE_UI: + m_stmt->setUInt( i, m_statementData[i].data.ui32 ); + break; + case TYPE_I: + m_stmt->setInt( i, m_statementData[i].data.i32 ); + break; + case TYPE_UI64: + m_stmt->setUInt64( i, m_statementData[i].data.ui64 ); + break; + case TYPE_I64: + m_stmt->setInt64( i, m_statementData[i].data.i64 ); + break; + case TYPE_DOUBLE: + m_stmt->setDouble( i, m_statementData[i].data.d ); + break; + case TYPE_STRING: + m_stmt->setString( i, std::string( reinterpret_cast< char* >( m_statementData[i].binary.data() ) ) ); + break; + case TYPE_BINARY: + { + std::stringstream is; + is.rdbuf()->pubsetbuf( reinterpret_cast< char* >( &m_statementData[i].binary[0] ), + m_statementData[i].binary.size() ); + m_stmt->setBlob( i, &is ); + } + break; + case TYPE_NULL: + m_stmt->setNull( i, 0 ); + break; + } + } +} + +//- Bind to buffer +void Core::Db::PreparedStatement::setBool( uint8_t index, const bool value ) +{ + if( index >= m_statementData.size() ) + m_statementData.resize( index + 1 ); + + m_statementData[index].data.boolean = value; + m_statementData[index].type = TYPE_BOOL; +} + +void Core::Db::PreparedStatement::setUInt( uint8_t index, uint32_t value ) +{ + if( index >= m_statementData.size() ) + m_statementData.resize(index+1); + + m_statementData[index].data.ui32 = value; + m_statementData[index].type = TYPE_UI; +} + +void Core::Db::PreparedStatement::setUInt64( uint8_t index, uint64_t value ) +{ + if( index >= m_statementData.size() ) + m_statementData.resize( index + 1 ); + + m_statementData[index].data.ui64 = value; + m_statementData[index].type = TYPE_UI64; +} + +void Core::Db::PreparedStatement::setInt( uint8_t index, int32_t value ) +{ + if( index >= m_statementData.size() ) + m_statementData.resize( index + 1 ); + + m_statementData[index].data.i32 = value; + m_statementData[index].type = TYPE_I; +} + +void Core::Db::PreparedStatement::setInt64( uint8_t index, int64_t value ) +{ + if( index >= m_statementData.size() ) + m_statementData.resize(index+1); + + m_statementData[index].data.i64 = value; + m_statementData[index].type = TYPE_I64; +} + +void Core::Db::PreparedStatement::setDouble( uint8_t index, double value ) +{ + if( index >= m_statementData.size() ) + m_statementData.resize( index + 1 ); + + m_statementData[index].data.d = value; + m_statementData[index].type = TYPE_DOUBLE; +} + +void Core::Db::PreparedStatement::setString( uint8_t index, const std::string& value ) +{ + if( index >= m_statementData.size() ) + m_statementData.resize( index + 1 ); + + m_statementData[index].binary.resize( value.length() + 1 ); + memcpy( m_statementData[index].binary.data(), value.c_str(), value.length() + 1 ); + m_statementData[index].type = TYPE_STRING; +} + +void Core::Db::PreparedStatement::setBinary( uint8_t index, const std::vector< uint8_t >& value ) +{ + if( index >= m_statementData.size() ) + m_statementData.resize( index + 1 ); + + m_statementData[index].binary = value; + m_statementData[index].type = TYPE_BINARY; +} + +void Core::Db::PreparedStatement::setNull( uint8_t index ) +{ + if( index >= m_statementData.size() ) + m_statementData.resize( index + 1 ); + + m_statementData[index].type = TYPE_NULL; +} + +uint32_t Core::Db::PreparedStatement::getIndex() const +{ + return m_index; +} + +void Core::Db::PreparedStatement::setMysqlPS( Mysql::PreparedStatement* pStmt ) +{ + m_stmt = pStmt; +} diff --git a/src/servers/Server_Common/Database/PreparedStatement.h b/src/servers/Server_Common/Database/PreparedStatement.h new file mode 100644 index 00000000..30d5ed6f --- /dev/null +++ b/src/servers/Server_Common/Database/PreparedStatement.h @@ -0,0 +1,83 @@ +#ifndef SAPPHIRE_PREPAREDSTATEMENT_H +#define SAPPHIRE_PREPAREDSTATEMENT_H +#include +#include +#include +#include "Operation.h" + +namespace Mysql +{ + class PreparedStatement; +} + +namespace Core +{ +namespace Db +{ + union PreparedStatementDataUnion + { + bool boolean; + uint32_t ui32; + int32_t i32; + uint64_t ui64; + int64_t i64; + double d; + }; + + enum PreparedStatementValueType + { + TYPE_BOOL, + TYPE_UI, + TYPE_UI64, + TYPE_I, + TYPE_I64, + TYPE_DOUBLE, + TYPE_STRING, + TYPE_BINARY, + TYPE_NULL + }; + + struct PreparedStatementData + { + PreparedStatementDataUnion data; + PreparedStatementValueType type; + std::vector< uint8_t > binary; + }; + + class PreparedStatement + { + public: + explicit PreparedStatement( uint32_t index ); + ~PreparedStatement(); + + void setBool( uint8_t index, bool value ); + void setUInt( uint8_t index, uint32_t value ); + void setUInt64( uint8_t index, uint64_t value ); + void setInt( uint8_t index, int32_t value ); + void setInt64( uint8_t index, int64_t value ); + void setDouble( uint8_t index, double value ); + void setString( uint8_t index, const std::string& value ); + void setBinary( uint8_t index, const std::vector< uint8_t >& value ); + void setNull( uint8_t index ); + + uint32_t getIndex() const; + void setMysqlPS( Mysql::PreparedStatement* pStmt ); + + void bindParameters(); + + protected: + Mysql::PreparedStatement* m_stmt; + uint32_t m_index; + std::vector< PreparedStatementData > m_statementData; + + PreparedStatement( PreparedStatement const& right ) = delete; + PreparedStatement& operator=( PreparedStatement const& right ) = delete; + }; + + +} +} + + + +#endif //SAPPHIRE_PREPAREDSTATEMENT_H diff --git a/src/servers/Server_Common/Database/StatementTask.cpp b/src/servers/Server_Common/Database/StatementTask.cpp new file mode 100644 index 00000000..de26c70f --- /dev/null +++ b/src/servers/Server_Common/Database/StatementTask.cpp @@ -0,0 +1,72 @@ +#include "StatementTask.h" +#include +#include "Operation.h" +#include "DbConnection.h" +#include "PreparedStatement.h" + +Core::Db::StatementTask::StatementTask( const std::string &sql, bool async ) +{ + m_sql = sql; + m_hasResult = async; // If the operation is async, then there's a result + //if (async) + // m_result = new QueryResultPromise(); +} + +Core::Db::StatementTask::~StatementTask() +{ + //if( m_hasResult && m_result != nullptr) + // delete m_result; +} + +bool Core::Db::StatementTask::execute() +{ + if( m_hasResult ) + { + /*ResultSet* result = m_conn->Query(m_sql); + if (!result || !result->GetRowCount() || !result->NextRow()) + { + delete result; + m_result->set_value(QueryResult(NULL)); + return false; + } + + m_result->set_value(QueryResult(result)); + return true;*/ + } + + return m_pConn->execute( m_sql ); +} + + + +Core::Db::PreparedStatementTask::PreparedStatementTask( Core::Db::PreparedStatement *stmt, bool async ) : + m_stmt(stmt) + //, m_result(nullptr) +{ + m_hasResult = async; // If the operation is async, then there's a result +} + +Core::Db::PreparedStatementTask::~PreparedStatementTask() +{ + delete m_stmt; + //if (m_has_result && m_result != nullptr) + // delete m_result; +} + +bool Core::Db::PreparedStatementTask::execute() +{ + //if (m_has_result) + //{ + // PreparedResultSet* result = m_conn->Query(m_stmt); + // if (!result || !result->GetRowCount()) + // { + // delete result; + // m_result->set_value(PreparedQueryResult(NULL)); + // return false; + // } + // m_result->set_value(PreparedQueryResult(result)); + // return true; + //} + + return m_pConn->execute( m_stmt ); +} diff --git a/src/servers/Server_Common/Database/StatementTask.h b/src/servers/Server_Common/Database/StatementTask.h new file mode 100644 index 00000000..cac96905 --- /dev/null +++ b/src/servers/Server_Common/Database/StatementTask.h @@ -0,0 +1,54 @@ +#ifndef SAPPHIRE_STATEMENTTASK_H +#define SAPPHIRE_STATEMENTTASK_H + +#include +#include "Operation.h" + +namespace Core +{ +namespace Db +{ + class PreparedStatement; + + class StatementTask : public Operation + { + public: + + StatementTask( const std::string& sql, bool async = false ); + + ~StatementTask(); + + bool execute() override; + + // QueryResultFuture getFuture() const + // { + // return m_result->get_future(); + // } + + private: + std::string m_sql; + bool m_hasResult; +// QueryResultPromise *m_result; + }; + + //- Lower-level class, enqueuable operation + class PreparedStatementTask : public Operation + { + public: + PreparedStatementTask( PreparedStatement* stmt, bool async = false); + ~PreparedStatementTask(); + + bool execute() override; + //PreparedQueryResultFuture getFuture() { return m_result->get_future(); } + + protected: + PreparedStatement* m_stmt; + bool m_hasResult; + //PreparedQueryResultPromise* m_result; + }; + +} +} + + +#endif //SAPPHIRE_STATEMENTTASK_H diff --git a/src/servers/Server_Common/Util/LockedWaitQueue.h b/src/servers/Server_Common/Util/LockedWaitQueue.h index 22c1964f..1ac2eae9 100644 --- a/src/servers/Server_Common/Util/LockedWaitQueue.h +++ b/src/servers/Server_Common/Util/LockedWaitQueue.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace Core { @@ -35,7 +36,7 @@ public: { std::lock_guard< std::mutex > lock( m_queueLock ); - return _queue.empty(); + return m_queue.empty(); } bool pop( T& value ) @@ -80,17 +81,17 @@ public: m_queue.pop(); } - _shutdown = true; + m_shutdown = true; - _condition.notify_all(); + m_condition.notify_all(); } private: template< typename E = T > - typename std::enable_if::value>::type deleteQueuedObject( E& obj ) { delete obj; } + typename std::enable_if< std::is_pointer< E >::value >::type deleteQueuedObject( E& obj ) { delete obj; } template< typename E = T > - typename std::enable_if::value>::type deleteQueuedObject( E const& ) { } + typename std::enable_if< !std::is_pointer< E >::value >::type deleteQueuedObject( E const& ) { } }; } diff --git a/src/servers/Server_Zone/ServerZone.cpp b/src/servers/Server_Zone/ServerZone.cpp index 270d472c..c7218dbb 100644 --- a/src/servers/Server_Zone/ServerZone.cpp +++ b/src/servers/Server_Zone/ServerZone.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,11 @@ #include #include +#include +#include +#include +#include + Core::Logger g_log; Core::Db::Database g_database; @@ -45,7 +51,7 @@ Core::Scripting::ScriptManager g_scriptMgr; Core::Data::ExdData g_exdData; Core::ZoneMgr g_zoneMgr; Core::LinkshellMgr g_linkshellMgr; - +Core::Db::DbWorkerPool< Core::Db::CharaDbConnection > CharacterDatabase; Core::ServerZone::ServerZone( const std::string& configPath, uint16_t serverId ) : m_configPath( configPath ) @@ -167,99 +173,137 @@ bool Core::ServerZone::loadSettings( int32_t argc, char* argv[] ) return false; } - try + Core::Db::DbLoader loader; + + Core::Db::ConnectionInfo info; + info.password = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Pass", "" ); + info.host = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Host", "127.0.0.1" ); + info.database = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Database", "sapphire" ); + info.port = m_pConfig->getValue< uint16_t >( "Settings.General.Mysql.Port", 3306 ); + info.user = m_pConfig->getValue< std::string >( "Settings.General.Mysql.Username", "root" ); + info.syncThreads = m_pConfig->getValue< uint8_t >( "Settings.General.Mysql.SyncThreads", 2 ); + info.asyncThreads = m_pConfig->getValue< uint8_t >( "Settings.General.Mysql.AsyncThreads", 2 ); + + loader.addDb( CharacterDatabase, info ); + if( !loader.initDbs() ) + return false; + + // execute() runs asynchronous + CharacterDatabase.execute( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( 101, '127.0.0.1', 54555);" ); + CharacterDatabase.execute( "DELETE FROM zoneservers WHERE id = 101" ); + + // query runs synchronous + boost::scoped_ptr< Mysql::ResultSet > res( CharacterDatabase.query( "SELECT id,ip,port FROM zoneservers" ) ); + while( res->next() ) { - // bunch of test cases for db wrapper - Mysql::MySqlBase base; - g_log.info( base.getVersionInfo() ); - - Mysql::optionMap options; - options[ MYSQL_OPT_RECONNECT ] = "1"; - - boost::scoped_ptr< Mysql::Connection > con( base.connect( "127.0.0.1", "root", "", options ) ); - - if( con->getAutoCommit() ) - g_log.info( "autocommit active" ); - - con->setAutoCommit( false ); - - if( !con->getAutoCommit() ) - g_log.info( "autocommit inactive" ); - - con->setAutoCommit( true ); - - if( con->getAutoCommit() ) - g_log.info( "autocommit active" ); - - con->setSchema( "sapphire" ); - - boost::scoped_ptr< Mysql::Statement > stmt( con->createStatement() ); - bool t1 = stmt->execute( "DELETE FROM zoneservers WHERE id = 101" ); - t1 = stmt->execute( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( 101, '127.0.0.1', 54555);" ); - // t1 = stmt->execute( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( 101, '127.0.0.1', 54555);" ); // throws duplicate entry - t1 = stmt->execute( "DELETE FROM zoneservers WHERE id = 101" ); - t1 = stmt->execute( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( 101, '127.0.0.1', 54555);" ); - //t1 = stmt->execute( "DELETE FROM zoneservers WHERE id = 101" ); - - //boost::scoped_ptr< Mysql::Statement > stmt1( con->createStatement() ); - //bool t2 = stmt1->execute( "INSERT INTO BLARGH!" ); // throws error - - boost::scoped_ptr< Mysql::Statement > stmt2( con->createStatement() ); - boost::scoped_ptr< Mysql::ResultSet > res( stmt2->executeQuery( "SELECT id,ip,port FROM zoneservers" ) ); - - while( res->next() ) - { - g_log.info( "id: " + std::to_string( res->getUInt( "id" ) ) ); - g_log.info( "ip: " + res->getString( "ip" ) ); - g_log.info( "port: " + std::to_string( res->getUInt( "port" ) ) ); - - // alternatively ( slightly faster ) - // g_log.info( "id: " + std::to_string( res->getUInt( 1 ) ) ); - // g_log.info( "ip: " + res->getString( 2 ) ); - // g_log.info( "port: " + std::to_string( res->getUInt( 3 ) ) ); - - } - - // binary data test - boost::scoped_ptr< Mysql::Statement > stmt3( con->createStatement() ); - boost::scoped_ptr< Mysql::ResultSet > res1( stmt3->executeQuery( "SELECT * FROM charabase" ) ); - - while( res1->next() ) - { - auto blob = res1->getBlobVector( "Customize" ); - } - - boost::scoped_ptr< Mysql::PreparedStatement > pstmt2( con->prepareStatement( "DELETE FROM zoneservers WHERE id = ?" ) ); - pstmt2->setInt( 1, 1021 ); - pstmt2->execute(); - - pstmt2->setInt( 1, 1001 ); - pstmt2->execute(); - - boost::scoped_ptr< Mysql::PreparedStatement > pstmt( con->prepareStatement( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( ?, ?, ?);" ) ); - pstmt->setInt( 1, 1001 ); - pstmt->setString( 2, "123.123.123.123" ); - pstmt->setInt( 3, 5454 ); - pstmt->execute(); - - pstmt->setInt( 1, 1021 ); - pstmt->setString( 2, "173.173.173.173" ); - pstmt->setInt( 3, 5151 ); - pstmt->execute(); - - boost::scoped_ptr< Mysql::PreparedStatement > pstmt1( con->prepareStatement( "DELETE FROM zoneservers WHERE id = ?" ) ); - pstmt->setInt( 1, 1021 ); - pstmt->execute(); - - pstmt->setInt( 1, 1001 ); - pstmt->execute(); - - } - catch( std::runtime_error e ) - { - g_log.error( e.what() ); + g_log.info( "id: " + std::to_string( res->getUInt( "id" ) ) ); + g_log.info( "ip: " + res->getString( "ip" ) ); + g_log.info( "port: " + std::to_string( res->getUInt( "port" ) ) ); } + auto stmt = CharacterDatabase.getPreparedStatement( Core::Db::CharaDbStatements::CHAR_INS_TEST ); + stmt->setUInt( 1, 2345 ); + stmt->setString( 2, "123.123.123.123" ); + stmt->setUInt( 3, 3306 ); + CharacterDatabase.execute( stmt ); + + //stmt->setUInt( 1, 245 ); + //stmt->setString( 2, "12.12.12.12" ); + //stmt->setUInt( 3, 3306 ); + //CharacterDatabase.execute( stmt ); + //try + //{ + // // bunch of test cases for db wrapper + // Mysql::MySqlBase base; + // g_log.info( base.getVersionInfo() ); + + // Mysql::optionMap options; + // options[ MYSQL_OPT_RECONNECT ] = "1"; + + // boost::scoped_ptr< Mysql::Connection > con( base.connect( "127.0.0.1", "root", "", options, 3306 ) ); + + // if( con->getAutoCommit() ) + // g_log.info( "autocommit active" ); + + // con->setAutoCommit( false ); + + // if( !con->getAutoCommit() ) + // g_log.info( "autocommit inactive" ); + + // con->setAutoCommit( true ); + + // if( con->getAutoCommit() ) + // g_log.info( "autocommit active" ); + + // con->setSchema( "sapphire" ); + + // boost::scoped_ptr< Mysql::Statement > stmt( con->createStatement() ); + // bool t1 = stmt->execute( "DELETE FROM zoneservers WHERE id = 101" ); + // t1 = stmt->execute( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( 101, '127.0.0.1', 54555);" ); + // // t1 = stmt->execute( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( 101, '127.0.0.1', 54555);" ); // throws duplicate entry + // t1 = stmt->execute( "DELETE FROM zoneservers WHERE id = 101" ); + // t1 = stmt->execute( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( 101, '127.0.0.1', 54555);" ); + // //t1 = stmt->execute( "DELETE FROM zoneservers WHERE id = 101" ); + + // //boost::scoped_ptr< Mysql::Statement > stmt1( con->createStatement() ); + // //bool t2 = stmt1->execute( "INSERT INTO BLARGH!" ); // throws error + + // boost::scoped_ptr< Mysql::Statement > stmt2( con->createStatement() ); + // boost::scoped_ptr< Mysql::ResultSet > res( stmt2->executeQuery( "SELECT id,ip,port FROM zoneservers" ) ); + + // while( res->next() ) + // { + // g_log.info( "id: " + std::to_string( res->getUInt( "id" ) ) ); + // g_log.info( "ip: " + res->getString( "ip" ) ); + // g_log.info( "port: " + std::to_string( res->getUInt( "port" ) ) ); + + // // alternatively ( slightly faster ) + // // g_log.info( "id: " + std::to_string( res->getUInt( 1 ) ) ); + // // g_log.info( "ip: " + res->getString( 2 ) ); + // // g_log.info( "port: " + std::to_string( res->getUInt( 3 ) ) ); + + // } + + // // binary data test + // boost::scoped_ptr< Mysql::Statement > stmt3( con->createStatement() ); + // boost::scoped_ptr< Mysql::ResultSet > res1( stmt3->executeQuery( "SELECT * FROM charabase" ) ); + + // while( res1->next() ) + // { + // auto blob = res1->getBlobVector( "Customize" ); + // } + + // boost::scoped_ptr< Mysql::PreparedStatement > pstmt2( con->prepareStatement( "DELETE FROM zoneservers WHERE id = ?" ) ); + // pstmt2->setInt( 1, 1021 ); + // pstmt2->execute(); + + // pstmt2->setInt( 1, 1001 ); + // pstmt2->execute(); + + // boost::scoped_ptr< Mysql::PreparedStatement > pstmt( con->prepareStatement( "INSERT INTO zoneservers ( id, ip, port ) VALUES ( ?, ?, ?);" ) ); + // pstmt->setInt( 1, 1001 ); + // pstmt->setString( 2, "123.123.123.123" ); + // pstmt->setInt( 3, 5454 ); + // pstmt->execute(); + + // pstmt->setInt( 1, 1021 ); + // pstmt->setString( 2, "173.173.173.173" ); + // pstmt->setInt( 3, 5151 ); + // pstmt->execute(); + + // boost::scoped_ptr< Mysql::PreparedStatement > pstmt1( con->prepareStatement( "DELETE FROM zoneservers WHERE id = ?" ) ); + // pstmt->setInt( 1, 1021 ); + // pstmt->execute(); + + // pstmt->setInt( 1, 1001 ); + // pstmt->execute(); + + //} + //catch( std::runtime_error e ) + //{ + // g_log.error( e.what() ); + //} + Db::DatabaseParams params; params.bufferSize = 16384; @@ -290,7 +334,8 @@ void Core::ServerZone::run( int32_t argc, char* argv[] ) g_log.info( "===========================================================" ); g_log.info( "Sapphire Server Project " ); - g_log.info( "Version: x.y.z" ); + g_log.info( "Version: " + Core::Version::VERSION ); + g_log.info( "GitHash: " + Core::Version::GIT_HASH ); g_log.info( "Compiled: " __DATE__ " " __TIME__ ); g_log.info( "===========================================================" );