Index: src/shared/Database/Database.cpp =================================================================== --- src/shared/Database/Database.cpp (revision 4065) +++ src/shared/Database/Database.cpp (working copy) @@ -23,6 +23,12 @@ #include #include +Database::~Database() +{ + /*Delete objects*/ + delete m_delayThread; //This also delate m_threadBody +} + bool Database::Initialize(const char *) { // Enable logging of SQL commands (usally only GM commands) Index: src/shared/Database/Database.h =================================================================== --- src/shared/Database/Database.h (revision 4065) +++ src/shared/Database/Database.h (working copy) @@ -19,14 +19,29 @@ #if !defined(DATABASE_H) #define DATABASE_H +#include "zthread/Thread.h" +#include "Utilities/HashMap.h" +#include "Database/SqlDelayThread.h" + +#include + +class SqlTransaction; +typedef HM_NAMESPACE::hash_map TransactionMap; + +#define MAX_QUERY_LEN 1024 + class Database { protected: - Database() {} + Database() {}; + + TransactionMap m_tranQueues; ///< Transaction queue from diff. threads + SqlDelayThread* m_threadBody; ///< Pointer to delay sql executer + ZThread::Thread* m_delayThread; ///< Pointer to executer thread public: - virtual ~Database() {} + virtual ~Database(); virtual bool Initialize(const char *infoString); @@ -35,6 +50,7 @@ virtual bool Execute(const char *sql) = 0; virtual bool PExecute(const char *format,...) = 0; + virtual bool DirectExecute(const char* sql) = 0; // Writes SQL commands to a LOG file (see mangosd.conf "LogSQL") bool PExecuteLog(const char *format,...); @@ -65,4 +81,5 @@ // 0 - do not log, 1 - log sql commands uint32 m_logSQL; }; + #endif Index: src/shared/Database/DatabaseMysql.cpp =================================================================== --- src/shared/Database/DatabaseMysql.cpp (revision 4065) +++ src/shared/Database/DatabaseMysql.cpp (working copy) @@ -21,6 +21,8 @@ #include "Policies/SingletonImp.h" #include "Platform/Define.h" #include "../src/zthread/ThreadImpl.h" +#include "Database/MySQLDelayThread.h" +#include "SqlStatements.h" using namespace std; @@ -50,10 +52,15 @@ exit(1); } } + //New delay thread for delay execute + m_delayThread = new ZThread::Thread(m_threadBody = new MySQLDelayThread(this)); } DatabaseMysql::~DatabaseMysql() { + m_threadBody->Stop(); //Stop event + m_delayThread->wait(); //Wait for flush to DB + if (mMysql) mysql_close(mMysql); @@ -150,9 +157,9 @@ if(!format) return NULL; va_list ap; - char szQuery [1024]; + char szQuery [MAX_QUERY_LEN]; va_start(ap, format); - int res = vsnprintf( szQuery, 1024, format, ap ); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); if(res==-1) @@ -175,7 +182,7 @@ { // guarded block for thread-safe mySQL request - ZThread::Guard query_connection_guard((ZThread::ThreadImpl::current()==tranThread?tranMutex:mMutex)); + ZThread::Guard query_connection_guard(mMutex); if(mysql_query(mMysql, sql)) { @@ -215,22 +222,18 @@ { if (!mMysql) return false; - + + tranThread = ZThread::ThreadImpl::current(); // owner of this transaction + TransactionMap::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { + // Statement for transaction + i->second->DelayExecute(sql); + } + else { - // guarded block for thread-safe mySQL request - ZThread::Guard query_connection_guard((ZThread::ThreadImpl::current()==tranThread?tranMutex:mMutex)); - - if(mysql_query(mMysql, sql)) - { - sLog.outErrorDb("SQL: %s", sql); - sLog.outErrorDb("SQL ERROR: %s", mysql_error(mMysql)); - return false; - } - else - { - DEBUG_LOG("SQL: %s", sql); - } - // end guarded block + // Simple sql statement + m_threadBody->Delay(new SqlStatement(sql)); } return true; @@ -242,9 +245,9 @@ return false; va_list ap; - char szQuery [1024]; + char szQuery [MAX_QUERY_LEN]; va_start(ap, format); - int res = vsnprintf( szQuery, 1024, format, ap ); + int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); if(res==-1) @@ -256,6 +259,31 @@ return Execute(szQuery); } +bool DatabaseMysql::DirectExecute(const char* sql) +{ + if (!mMysql) + return false; + + { + // guarded block for thread-safe mySQL request + ZThread::Guard query_connection_guard(mMutex); + + if(mysql_query(mMysql, sql)) + { + sLog.outErrorDb("SQL: %s", sql); + sLog.outErrorDb("SQL ERROR: %s", mysql_error(mMysql)); + return false; + } + else + { + DEBUG_LOG("SQL: %s", sql); + } + // end guarded block + } + + return true; +} + bool DatabaseMysql::_TransactionCmd(const char *sql) { if (mysql_query(mMysql, sql)) @@ -275,16 +303,11 @@ { if (!mMysql) return false; - if (tranThread==ZThread::ThreadImpl::current()) - return false; // huh? this thread already started transaction - mMutex.acquire(); - if (!_TransactionCmd("START TRANSACTION")) - { - mMutex.release(); // can't start transaction - return false; - } - // transaction started tranThread = ZThread::ThreadImpl::current(); // owner of this transaction + TransactionMap::iterator i = m_tranQueues.find(tranThread); + if (i == m_tranQueues.end()) + // If for thread not existing queue create one + m_tranQueues[tranThread] = new SqlTransaction(); return true; } @@ -292,24 +315,32 @@ { if (!mMysql) return false; - if (tranThread!=ZThread::ThreadImpl::current()) + tranThread = ZThread::ThreadImpl::current(); + TransactionMap::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { + // We not need locking the transaction queue it is only one + // for every thread (no concurency there) + m_threadBody->Delay(i->second); + i->second = NULL; + return true; + } + else return false; - bool _res = _TransactionCmd("COMMIT"); - tranThread = NULL; - mMutex.release(); - return _res; } bool DatabaseMysql::RollbackTransaction() { if (!mMysql) return false; - if (tranThread!=ZThread::ThreadImpl::current()) - return false; - bool _res = _TransactionCmd("ROLLBACK"); - tranThread = NULL; - mMutex.release(); - return _res; + tranThread = ZThread::ThreadImpl::current(); + TransactionMap::iterator i = m_tranQueues.find(tranThread); + if (i != m_tranQueues.end() && i->second != NULL) + { + delete i->second; + i->second = NULL; + } + return true; } unsigned long DatabaseMysql::escape_string(char *to, const char *from, unsigned long length) Index: src/shared/Database/DatabaseMysql.h =================================================================== --- src/shared/Database/DatabaseMysql.h (revision 4065) +++ src/shared/Database/DatabaseMysql.h (working copy) @@ -45,6 +45,7 @@ QueryResult* Query(const char *sql); bool Execute(const char *sql); bool PExecute(const char *format,...); + bool DirectExecute(const char* sql); bool BeginTransaction(); bool CommitTransaction(); bool RollbackTransaction(); Index: src/shared/Database/SQLStatements.cpp =================================================================== --- src/shared/Database/SQLStatements.cpp (revision 0) +++ src/shared/Database/SQLStatements.cpp (revision 0) @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "DatabaseEnv.h" +#include "SQLStatements.h" + +void SqlStatement::Execute(Database *db) +{ + db->DirectExecute(m_sql); +} + +void SqlTransaction::Execute(Database *db) +{ + if(m_queue.empty()) return; + db->DirectExecute("START TRANSACTION"); + while(!m_queue.empty()) + { + const char *sql = (const char*)m_queue.front(); + m_queue.pop(); + db->DirectExecute(sql); + delete [] sql; + } + db->DirectExecute("COMMIT"); +} + +void SqlQuery::Execute(Database *db) +{ + m_result = db->PQuery(m_sql); + Callback(); +} + +/*class Obj +{ +public: + void SomeMethod(QueryResult *q) + { + Player *ply; + ply-> + } + +} *obj;*/ + +class ObjInterface1; +class ObjInterface2; +class ObjFullInterface; +class Obj2Interface1; +class Obj2Interface2; +class Obj2FullInterface; + +class BaseObj +{ + +}; + +class Obj : public BaseObj +{ + public: + ObjInterface1* GetInterface1() { return (ObjInterface1*)this; } + ObjInterface2* GetInterface2() { return (ObjInterface2*)this; } + ObjFullInterface* GetFullInterface() { return (ObjFullInterface*)this; } +}; + +class ObjInterface1 : public BaseObj +{ + public: + void I1_AFunc() {} +}; + +class ObjInterface2 : public BaseObj +{ + public: + void I2_AFunc() {} +}; + +class ObjFullInterface : public ObjInterface1, public ObjInterface2 +{ + +}; + +class BaseObj2 : public BaseObj +{ + +}; + +class Obj2 : public BaseObj2 +{ + public: + Obj2Interface1* GetInterface1() { return (Obj2Interface1*)this; } + Obj2Interface2* GetInterface2() { return (Obj2Interface2*)this; } + Obj2FullInterface* GetFullInterface() { return (Obj2FullInterface*)this; } +}; + +class Obj2Interface1 : public BaseObj2, public ObjInterface1 +{ + public: + void I1_BFunc() {} +}; + +class Obj2Interface2 : public BaseObj2, public ObjInterface2 +{ + public: + void I2_BFunc() {} +}; + +class Obj2FullInterface : public Obj2Interface1, public Obj2Interface2 +{ + +}; + +void Test() +{ + Obj *obj = (Obj*)new ObjFullInterface; + obj->GetInterface1()->I1_AFunc(); + Obj2 *obj2 = (Obj2*)new Obj2FullInterface; + obj2->GetInterface1()->I1_BFunc(); +} + +void SqlQueryQueue::Update() +{ + while(!empty()) + { + SqlQuery * query =next(); + query->Callback(); + } + //_SqlQuery *query = new _SqlQuery("select ...", obj, &Obj::SomeMethod); +} \ No newline at end of file Index: src/shared/Database/SQLStatements.h =================================================================== --- src/shared/Database/SQLStatements.h (revision 0) +++ src/shared/Database/SQLStatements.h (revision 0) @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __SQLSTATEMENTS_H +#define __SQLSTATEMENTS_H + +#include +using namespace std; + +// ---- BASE --- + +class Database; + +class SqlOperation +{ + public: + SqlOperation() {} + virtual void Execute(Database *db) = 0; +}; + +// ---- ASYNC STATEMENTS / TRANSACTIONS ---- + +class SqlStatement : public SqlOperation +{ + private: + const char *m_sql; + public: + SqlStatement(const char *sql) : m_sql(strdup(sql)), SqlOperation() {} + ~SqlStatement() { delete[] m_sql; } + void Execute(Database *db); +}; + +class SqlTransaction : public SqlOperation +{ + private: + queue m_queue; + public: + SqlTransaction() : SqlOperation() {} + void DelayExecute(const char *sql) { m_queue.push(strdup(sql)); } + void Execute(Database *db); +}; + +// ---- ASYNC QUERIES ---- + +class SqlQueryQueue; +class SqlQuery; +class QueryResult; + +class SqlQueryQueue : public ZThread::LockedQueue +{ + public: + SqlQueryQueue() : ZThread::LockedQueue() {} + void Update(); +}; + +class SqlQuery : public SqlOperation +{ + private: + QueryResult *m_result; + const char *m_sql; + public: + SqlQuery(const char *sql) + : m_sql(strdup(sql)), m_result(NULL), SqlOperation() {} + QueryResult *GetResult() { return m_result; } + void Execute(Database *db); + virtual void Callback() = 0; +}; + +template +class _SqlQuery : public SqlQuery +{ + private: + typedef void (Class::*Method)(QueryResult*); + Class *m_object; + Method m_method; + public: + _SqlQuery(const char *sql, Class *object, Method method) + : m_object(object), m_method(method), SqlQuery(sql) {} + void Callback() { (m_object->*m_method)(GetResult()); } +}; + +#endif \ No newline at end of file