diff options
Diffstat (limited to 'src/shared/Database')
22 files changed, 422 insertions, 0 deletions
diff --git a/src/shared/Database/DBCFileLoader.cpp b/src/shared/Database/DBCFileLoader.cpp index adf796fbce6..e7ebeedecfb 100644 --- a/src/shared/Database/DBCFileLoader.cpp +++ b/src/shared/Database/DBCFileLoader.cpp @@ -17,42 +17,59 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include <stdio.h> #include <stdlib.h> #include <string.h> + #include "DBCFileLoader.h" + DBCFileLoader::DBCFileLoader() { data = NULL; fieldsOffset = NULL; } + bool DBCFileLoader::Load(const char *filename, const char *fmt) { + uint32 header; if(data) { delete [] data; data=NULL; } + FILE * f=fopen(filename,"rb"); if(!f)return false; + if(fread(&header,4,1,f)!=1) // Number of records return false; + EndianConvert(header); if(header!=0x43424457) return false; //'WDBC' + if(fread(&recordCount,4,1,f)!=1) // Number of records return false; + EndianConvert(recordCount); + if(fread(&fieldCount,4,1,f)!=1) // Number of fields return false; + EndianConvert(fieldCount); + if(fread(&recordSize,4,1,f)!=1) // Size of a record return false; + EndianConvert(recordSize); + if(fread(&stringSize,4,1,f)!=1) // String size return false; + EndianConvert(stringSize); + fieldsOffset = new uint32[fieldCount]; fieldsOffset[0] = 0; for(uint32 i = 1; i < fieldCount; i++) @@ -63,13 +80,17 @@ bool DBCFileLoader::Load(const char *filename, const char *fmt) else // 4 byte fields (int32/float/strings) fieldsOffset[i] += 4; } + data = new unsigned char[recordSize*recordCount+stringSize]; stringTable = data + recordSize*recordCount; + if(fread(data,recordSize*recordCount+stringSize,1,f)!=1) return false; + fclose(f); return true; } + DBCFileLoader::~DBCFileLoader() { if(data) @@ -77,11 +98,13 @@ DBCFileLoader::~DBCFileLoader() if(fieldsOffset) delete [] fieldsOffset; } + DBCFileLoader::Record DBCFileLoader::getRecord(size_t id) { assert(data); return Record(*this, data + id*recordSize); } + uint32 DBCFileLoader::GetFormatRecordSize(const char * format,int32* index_pos) { uint32 recordsize = 0; @@ -107,10 +130,13 @@ uint32 DBCFileLoader::GetFormatRecordSize(const char * format,int32* index_pos) recordsize += 1; break; } + if(index_pos) *index_pos = i; + return recordsize; } + char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char**& indexTable, uint32 sqlRecordCount, uint32 sqlHighestIndex, char *& sqlDataTable) { /* @@ -120,14 +146,18 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** float field1, int field2 }entry; + this func will generate entry[rows] data; */ + typedef char * ptr; if(strlen(format)!=fieldCount) return NULL; + //get struct size and index pos int32 i; uint32 recordsize=GetFormatRecordSize(format,&i); + if(i>=0) { uint32 maxi=0; @@ -137,9 +167,11 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** uint32 ind=getRecord(y).getUInt (i); if(ind>maxi)maxi=ind; } + // If higher index avalible from sql - use it instead of dbcs if (sqlHighestIndex > maxi) maxi = sqlHighestIndex; + ++maxi; records=maxi; indexTable=new ptr[maxi]; @@ -150,8 +182,11 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** records = recordCount + sqlRecordCount; indexTable = new ptr[recordCount+ sqlRecordCount]; } + char* dataTable= new char[(recordCount + sqlRecordCount)*recordsize]; + uint32 offset=0; + for(uint32 y =0;y<recordCount;++y) { if(i>=0) @@ -160,6 +195,7 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** } else indexTable[y]=&dataTable[offset]; + for(uint32 x=0;x<fieldCount;x++) { switch(format[x]) @@ -185,15 +221,20 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char** } } sqlDataTable = dataTable + offset; + return dataTable; } + char* DBCFileLoader::AutoProduceStrings(const char* format, char* dataTable) { if(strlen(format)!=fieldCount) return NULL; + char* stringPool= new char[stringSize]; memcpy(stringPool,stringTable,stringSize); + uint32 offset=0; + for(uint32 y =0;y<recordCount;y++) { for(uint32 x=0;x<fieldCount;x++) @@ -219,6 +260,7 @@ char* DBCFileLoader::AutoProduceStrings(const char* format, char* dataTable) break; } } + return stringPool; } diff --git a/src/shared/Database/DBCFileLoader.h b/src/shared/Database/DBCFileLoader.h index fd1f5539ee3..ef29af84bc1 100644 --- a/src/shared/Database/DBCFileLoader.h +++ b/src/shared/Database/DBCFileLoader.h @@ -15,11 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DBC_FILE_LOADER_H #define DBC_FILE_LOADER_H #include "Platform/Define.h" #include "Utilities/ByteConverter.h" #include <cassert> + enum { FT_NA='x', //not used or unknown, 4 byte size @@ -34,12 +36,15 @@ enum FT_SQL_PRESENT='p', //Used in sql format to mark column present in sql dbc FT_SQL_ABSENT='a' //Used in sql format to mark column absent in sql dbc }; + class DBCFileLoader { public: DBCFileLoader(); ~DBCFileLoader(); + bool Load(const char *filename, const char *fmt); + class Record { public: @@ -62,6 +67,7 @@ class DBCFileLoader assert(field < file.fieldCount); return *reinterpret_cast<uint8*>(offset+file.GetOffset(field)); } + const char *getString(size_t field) const { assert(field < file.fieldCount); @@ -69,15 +75,20 @@ class DBCFileLoader assert(stringOffset < file.stringSize); return reinterpret_cast<char*>(file.stringTable + stringOffset); } + private: Record(DBCFileLoader &file_, unsigned char *offset_): offset(offset_), file(file_) {} unsigned char *offset; DBCFileLoader &file; + friend class DBCFileLoader; + }; + // Get record by id Record getRecord(size_t id); /// Get begin iterator over records + uint32 GetNumRows() const { return recordCount;} uint32 GetRowSize() const { return recordSize;} uint32 GetCols() const { return fieldCount; } @@ -87,6 +98,7 @@ class DBCFileLoader char* AutoProduceStrings(const char* fmt, char* dataTable); static uint32 GetFormatRecordSize(const char * format, int32 * index_pos = NULL); private: + uint32 recordSize; uint32 recordCount; uint32 fieldCount; diff --git a/src/shared/Database/DBCStore.h b/src/shared/Database/DBCStore.h index 60e533a88e1..e02265ec523 100644 --- a/src/shared/Database/DBCStore.h +++ b/src/shared/Database/DBCStore.h @@ -15,10 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DBCSTORE_H #define DBCSTORE_H + #include "DBCFileLoader.h" #include "Log.h" + struct SqlDbc { const std::string * formatString; @@ -38,6 +41,7 @@ struct SqlDbc else if (sqlTableName[i] == '.') sqlTableName[i] = '_'; } + // Get sql index position DBCFileLoader::GetFormatRecordSize(fmt, &indexPos); if (indexPos>=0) @@ -55,6 +59,7 @@ struct SqlDbc } } }; + template<class T> class DBCStorage { @@ -62,16 +67,19 @@ class DBCStorage public: explicit DBCStorage(const char *f) : nCount(0), fieldCount(0), fmt(f), indexTable(NULL), m_dataTable(NULL) { } ~DBCStorage() { Clear(); } + T const* LookupEntry(uint32 id) const { return (id>=nCount)?NULL:indexTable[id]; } uint32 GetNumRows() const { return nCount; } char const* GetFormat() const { return fmt; } uint32 GetFieldCount() const { return fieldCount; } + bool Load(char const* fn, SqlDbc * sql) { DBCFileLoader dbc; // Check if load was sucessful, only then continue if(!dbc.Load(fn, fmt)) return false; + uint32 sqlRecordCount = 0; uint32 sqlHighestIndex = 0; Field *fields = NULL; @@ -83,6 +91,7 @@ class DBCStorage if (sql->indexPos >= 0) query +=" ORDER BY + " + *sql->indexName + " DESC"; query += ";"; + result = WorldDatabase.Query(query.c_str()); if (result) { @@ -103,7 +112,9 @@ class DBCStorage char * sqlDataTable; fieldCount = dbc.GetCols(); m_dataTable = (T*)dbc.AutoProduceData(fmt,nCount,(char**&)indexTable, sqlRecordCount, sqlHighestIndex, sqlDataTable); + m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); + // Insert sql data into arrays if (result) { @@ -115,6 +126,7 @@ class DBCStorage { if (!fields) fields = result->Fetch(); + if(sql->indexPos >= 0) { uint32 id = fields[sql->sqlIndexPos].GetUInt32(); @@ -129,6 +141,7 @@ class DBCStorage indexTable[rowIndex]=(T*)&sqlDataTable[offset]; uint32 columnNumber = 0; uint32 sqlColumnNumber = 0; + for(;columnNumber < sql->formatString->size();++columnNumber) { if ((*sql->formatString)[columnNumber] == FT_SQL_ABSENT) @@ -198,35 +211,44 @@ class DBCStorage delete result; return false; } + fields = NULL; ++rowIndex; }while (result->NextRow()); } delete result; } + // error in dbc file at loading if NULL return indexTable!=NULL; } + bool LoadStringsFrom(char const* fn) { // DBC must be already loaded using Load if(!indexTable) return false; + DBCFileLoader dbc; // Check if load was successful, only then continue if(!dbc.Load(fn, fmt)) return false; + m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable)); + return true; } + void Clear() { if (!indexTable) return; + delete[] ((char*)indexTable); indexTable = NULL; delete[] ((char*)m_dataTable); m_dataTable = NULL; + while(!m_stringPoolList.empty()) { delete[] m_stringPoolList.front(); @@ -234,6 +256,7 @@ class DBCStorage } nCount = 0; } + private: char const* fmt; uint32 nCount; @@ -242,4 +265,5 @@ class DBCStorage T* m_dataTable; StringPoolList m_stringPoolList; }; + #endif diff --git a/src/shared/Database/Database.cpp b/src/shared/Database/Database.cpp index 9bfae3479bf..572d3db6f1d 100644 --- a/src/shared/Database/Database.cpp +++ b/src/shared/Database/Database.cpp @@ -17,17 +17,22 @@ * 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 "Config/ConfigEnv.h" + #include "Common.h" #include "../../game/UpdateFields.h" + #include <ctime> #include <iostream> #include <fstream> + Database::~Database() { /*Delete objects*/ } + bool Database::Initialize(const char *) { // Enable logging of SQL commands (usally only GM commands) @@ -39,37 +44,46 @@ bool Database::Initialize(const char *) if((m_logsDir.at(m_logsDir.length()-1)!='/') && (m_logsDir.at(m_logsDir.length()-1)!='\\')) m_logsDir.append("/"); } + return true; } + void Database::ThreadStart() { } + void Database::ThreadEnd() { } + void Database::escape_string(std::string& str) { if(str.empty()) return; + char* buf = new char[str.size()*2+1]; escape_string(buf,str.c_str(),str.size()); str = buf; delete[] buf; } + bool Database::PExecuteLog(const char * format,...) { if (!format) return false; + va_list ap; char szQuery [MAX_QUERY_LEN]; va_start(ap, format); int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); + if(res==-1) { sLog.outError("SQL Query truncated (and not execute) for format: %s",format); return false; } + if( m_logSQL ) { time_t curr; @@ -78,6 +92,7 @@ bool Database::PExecuteLog(const char * format,...) local=*(localtime(&curr)); // dereference and assign char fName[128]; sprintf( fName, "%04d-%02d-%02d_logSQL.sql", local.tm_year+1900, local.tm_mon+1, local.tm_mday ); + FILE* log_file; std::string logsDir_fname = m_logsDir+fName; log_file = fopen(logsDir_fname.c_str(), "a"); @@ -92,58 +107,74 @@ bool Database::PExecuteLog(const char * format,...) sLog.outError("SQL-Logging is disabled - Log file for the SQL commands could not be openend: %s",fName); } } + return Execute(szQuery); } + void Database::SetResultQueue(SqlResultQueue * queue) { m_queryQueues[ACE_Based::Thread::current()] = queue; + } + QueryResult* Database::PQuery(const char *format,...) { if(!format) return NULL; + va_list ap; char szQuery [MAX_QUERY_LEN]; va_start(ap, format); int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); + if(res==-1) { sLog.outError("SQL Query truncated (and not execute) for format: %s",format); return false; } + return Query(szQuery); } + QueryNamedResult* Database::PQueryNamed(const char *format,...) { if(!format) return NULL; + va_list ap; char szQuery [MAX_QUERY_LEN]; va_start(ap, format); int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); + if(res==-1) { sLog.outError("SQL Query truncated (and not execute) for format: %s",format); return false; } + return QueryNamed(szQuery); } + bool Database::PExecute(const char * format,...) { if (!format) return false; + va_list ap; char szQuery [MAX_QUERY_LEN]; va_start(ap, format); int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); + if(res==-1) { sLog.outError("SQL Query truncated (and not execute) for format: %s",format); return false; } + return Execute(szQuery); } + bool Database::_UpdateDataBlobValue(const uint32 guid, const uint32 field, const int32 value) { return PExecute( @@ -153,6 +184,7 @@ bool Database::_UpdateDataBlobValue(const uint32 guid, const uint32 field, const "' ',SUBSTRING_INDEX(`data`,' ',%i)) WHERE guid=%u", field, field+1, value, -int32(PLAYER_END-field), guid); } + bool Database::_SetDataBlobValue(const uint32 guid, const uint32 field, const uint32 value) { return PExecute( @@ -161,22 +193,27 @@ bool Database::_SetDataBlobValue(const uint32 guid, const uint32 field, const ui "%u,' ',SUBSTRING_INDEX(`data`,' ',%i)) WHERE guid=%u", field, value, -int32(PLAYER_END-field), guid); } + bool Database::DirectPExecute(const char * format,...) { if (!format) return false; + va_list ap; char szQuery [MAX_QUERY_LEN]; va_start(ap, format); int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); + if(res==-1) { sLog.outError("SQL Query truncated (and not execute) for format: %s",format); return false; } + return DirectExecute(szQuery); } + bool Database::CheckRequiredField( char const* table_name, char const* required_name ) { // check required field @@ -186,7 +223,9 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_ delete result; return true; } + // check fail, prepare readabale error message + // search current required_* field in DB QueryNamedResult* result2 = PQueryNamed("SELECT * FROM %s LIMIT 1",table_name); if(result2) @@ -201,7 +240,9 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_ break; } } + delete result2; + if(!reqName.empty()) sLog.outErrorDb("Table `%s` have field `%s` but expected `%s`! Not all sql updates applied?",table_name,reqName.c_str(),required_name); else @@ -209,5 +250,6 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_ } else sLog.outErrorDb("Table `%s` fields list query fail but expected have `%s`! No records in `%s`?",table_name,required_name,table_name); + return false; } diff --git a/src/shared/Database/Database.h b/src/shared/Database/Database.h index 34438d994dc..6172a61c5f9 100644 --- a/src/shared/Database/Database.h +++ b/src/shared/Database/Database.h @@ -17,35 +17,48 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DATABASE_H #define DATABASE_H + #include "Threading.h" #include "Utilities/UnorderedMap.h" #include "Database/SqlDelayThread.h" + class SqlTransaction; class SqlResultQueue; class SqlQueryHolder; + typedef UNORDERED_MAP<ACE_Based::Thread* , SqlTransaction*> TransactionQueues; typedef UNORDERED_MAP<ACE_Based::Thread* , SqlResultQueue*> QueryQueues; + #define MAX_QUERY_LEN 32*1024 + class TRINITY_DLL_SPEC Database { protected: Database() : m_threadBody(NULL), m_delayThread(NULL) {}; + TransactionQueues m_tranQueues; ///< Transaction queues from diff. threads QueryQueues m_queryQueues; ///< Query queues from diff threads SqlDelayThread* m_threadBody; ///< Pointer to delay sql executer (owned by m_delayThread) ACE_Based::Thread* m_delayThread; ///< Pointer to executer thread + public: + virtual ~Database(); + virtual bool Initialize(const char *infoString); virtual void InitDelayThread() = 0; virtual void HaltDelayThread() = 0; + virtual QueryResult* Query(const char *sql) = 0; QueryResult* PQuery(const char *format,...) ATTR_PRINTF(2,3); virtual QueryNamedResult* QueryNamed(const char *sql) = 0; QueryNamedResult* PQueryNamed(const char *format,...) ATTR_PRINTF(2,3); + /// Async queries and query holders, implemented in DatabaseImpl.h + // Query / member template<class Class> bool AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const char *sql); @@ -83,14 +96,18 @@ class TRINITY_DLL_SPEC Database bool DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*), SqlQueryHolder *holder); template<class Class, typename ParamType1> bool DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*, ParamType1), SqlQueryHolder *holder, ParamType1 param1); + virtual bool Execute(const char *sql) = 0; bool PExecute(const char *format,...) ATTR_PRINTF(2,3); virtual bool DirectExecute(const char* sql) = 0; bool DirectPExecute(const char *format,...) ATTR_PRINTF(2,3); + bool _UpdateDataBlobValue(const uint32 guid, const uint32 field, const int32 value); bool _SetDataBlobValue(const uint32 guid, const uint32 field, const uint32 value); + // Writes SQL commands to a LOG file (see Trinityd.conf "LogSQL") bool PExecuteLog(const char *format,...) ATTR_PRINTF(2,3); + virtual bool BeginTransaction() // nothing do if DB not support transactions { return true; @@ -103,15 +120,20 @@ class TRINITY_DLL_SPEC Database { return false; } + virtual operator bool () const = 0; + virtual unsigned long escape_string(char *to, const char *from, unsigned long length) { strncpy(to,from,length); return length; } void escape_string(std::string& str); + // must be called before first query in thread (one time for thread using one from existed Database objects) virtual void ThreadStart(); // must be called before finish thread run (one time for thread using one from existed Database objects) virtual void ThreadEnd(); + // sets the result queue of the current thread, be careful what thread you call this from void SetResultQueue(SqlResultQueue * queue); + bool CheckRequiredField(char const* table_name, char const* required_name); private: bool m_logSQL; diff --git a/src/shared/Database/DatabaseEnv.h b/src/shared/Database/DatabaseEnv.h index 2e89d74e97c..d5d6867e82f 100644 --- a/src/shared/Database/DatabaseEnv.h +++ b/src/shared/Database/DatabaseEnv.h @@ -17,13 +17,17 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #if !defined(DATABASEENV_H) #define DATABASEENV_H + #include "Common.h" #include "Log.h" #include "Errors.h" + #include "Database/Field.h" #include "Database/QueryResult.h" + #ifdef DO_POSTGRESQL #include "Database/QueryResultPostgre.h" #include "Database/Database.h" @@ -43,8 +47,10 @@ typedef DatabaseMysql DatabaseType; #define _CONCAT3_(A,B,C) "CONCAT( " A " , " B " , " C " )" #define _OFFSET_ "LIMIT %d,1" #endif + extern DatabaseType WorldDatabase; extern DatabaseType CharacterDatabase; extern DatabaseType loginDatabase; + #endif diff --git a/src/shared/Database/DatabaseImpl.h b/src/shared/Database/DatabaseImpl.h index ac856664290..7cbd0ed8ba5 100644 --- a/src/shared/Database/DatabaseImpl.h +++ b/src/shared/Database/DatabaseImpl.h @@ -17,9 +17,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Database/Database.h" #include "Database/SqlOperations.h" + /// Function body definitions for the template function members of the Database class + #define ASYNC_QUERY_BODY(sql, queue_itr) \ if (!sql) return false; \ \ @@ -30,6 +33,7 @@ queue_itr = m_queryQueues.find(queryThread); \ if (queue_itr == m_queryQueues.end()) return false; \ } + #define ASYNC_PQUERY_BODY(format, szQuery) \ if(!format) return false; \ \ @@ -48,6 +52,7 @@ return false; \ } \ } + #define ASYNC_DELAYHOLDER_BODY(holder, queue_itr) \ if (!holder) return false; \ \ @@ -58,7 +63,9 @@ queue_itr = m_queryQueues.find(queryThread); \ if (queue_itr == m_queryQueues.end()) return false; \ } + // -- Query / member -- + template<class Class> bool Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const char *sql) @@ -66,6 +73,7 @@ Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const c ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class>(object, method), itr->second)); } + template<class Class, typename ParamType1> bool Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql) @@ -73,6 +81,7 @@ Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamTyp ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class, ParamType1>(object, method, (QueryResult*)NULL, param1), itr->second)); } + template<class Class, typename ParamType1, typename ParamType2> bool Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *sql) @@ -80,6 +89,7 @@ Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamTyp ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class, ParamType1, ParamType2>(object, method, (QueryResult*)NULL, param1, param2), itr->second)); } + template<class Class, typename ParamType1, typename ParamType2, typename ParamType3> bool Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *sql) @@ -87,7 +97,9 @@ Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamTyp ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class, ParamType1, ParamType2, ParamType3>(object, method, (QueryResult*)NULL, param1, param2, param3), itr->second)); } + // -- Query / static -- + template<typename ParamType1> bool Database::AsyncQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql) @@ -95,6 +107,7 @@ Database::AsyncQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1 ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::SQueryCallback<ParamType1>(method, (QueryResult*)NULL, param1), itr->second)); } + template<typename ParamType1, typename ParamType2> bool Database::AsyncQuery(void (*method)(QueryResult*, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *sql) @@ -102,6 +115,7 @@ Database::AsyncQuery(void (*method)(QueryResult*, ParamType1, ParamType2), Param ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::SQueryCallback<ParamType1, ParamType2>(method, (QueryResult*)NULL, param1, param2), itr->second)); } + template<typename ParamType1, typename ParamType2, typename ParamType3> bool Database::AsyncQuery(void (*method)(QueryResult*, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *sql) @@ -109,7 +123,9 @@ Database::AsyncQuery(void (*method)(QueryResult*, ParamType1, ParamType2, ParamT ASYNC_QUERY_BODY(sql, itr) return m_threadBody->Delay(new SqlQuery(sql, new Trinity::SQueryCallback<ParamType1, ParamType2, ParamType3>(method, (QueryResult*)NULL, param1, param2, param3), itr->second)); } + // -- PQuery / member -- + template<class Class> bool Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*), const char *format,...) @@ -117,6 +133,7 @@ Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*), const ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(object, method, szQuery); } + template<class Class, typename ParamType1> bool Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) @@ -124,6 +141,7 @@ Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamTy ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(object, method, param1, szQuery); } + template<class Class, typename ParamType1, typename ParamType2> bool Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *format,...) @@ -131,6 +149,7 @@ Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamTy ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(object, method, param1, param2, szQuery); } + template<class Class, typename ParamType1, typename ParamType2, typename ParamType3> bool Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *format,...) @@ -138,7 +157,9 @@ Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamTy ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(object, method, param1, param2, param3, szQuery); } + // -- PQuery / static -- + template<typename ParamType1> bool Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...) @@ -146,6 +167,7 @@ Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(method, param1, szQuery); } + template<typename ParamType1, typename ParamType2> bool Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *format,...) @@ -153,6 +175,7 @@ Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1, ParamType2), Para ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(method, param1, param2, szQuery); } + template<typename ParamType1, typename ParamType2, typename ParamType3> bool Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *format,...) @@ -160,7 +183,9 @@ Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1, ParamType2, Param ASYNC_PQUERY_BODY(format, szQuery) return AsyncQuery(method, param1, param2, param3, szQuery); } + // -- QueryHolder -- + template<class Class> bool Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*), SqlQueryHolder *holder) @@ -168,6 +193,7 @@ Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, Sq ASYNC_DELAYHOLDER_BODY(holder, itr) return holder->Execute(new Trinity::QueryCallback<Class, SqlQueryHolder*>(object, method, (QueryResult*)NULL, holder), m_threadBody, itr->second); } + template<class Class, typename ParamType1> bool Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*, ParamType1), SqlQueryHolder *holder, ParamType1 param1) @@ -175,6 +201,7 @@ Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, Sq ASYNC_DELAYHOLDER_BODY(holder, itr) return holder->Execute(new Trinity::QueryCallback<Class, SqlQueryHolder*, ParamType1>(object, method, (QueryResult*)NULL, holder, param1), m_threadBody, itr->second); } + #undef ASYNC_QUERY_BODY #undef ASYNC_PQUERY_BODY #undef ASYNC_DELAYHOLDER_BODY diff --git a/src/shared/Database/DatabaseMysql.cpp b/src/shared/Database/DatabaseMysql.cpp index 235fb96f127..f08ea67cbbe 100644 --- a/src/shared/Database/DatabaseMysql.cpp +++ b/src/shared/Database/DatabaseMysql.cpp @@ -17,7 +17,9 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DO_POSTGRESQL + #include "Util.h" #include "Policies/SingletonImp.h" #include "Platform/Define.h" @@ -26,15 +28,19 @@ #include "Database/MySQLDelayThread.h" #include "Database/SqlOperations.h" #include "Timer.h" + void DatabaseMysql::ThreadStart() { mysql_thread_init(); } + void DatabaseMysql::ThreadEnd() { mysql_thread_end(); } + size_t DatabaseMysql::db_count = 0; + DatabaseMysql::DatabaseMysql() : Database(), mMysql(0) { // before first connection @@ -42,6 +48,7 @@ DatabaseMysql::DatabaseMysql() : Database(), mMysql(0) { // Mysql Library Init mysql_library_init(-1, NULL, NULL); + if (!mysql_thread_safe()) { sLog.outError("FATAL ERROR: Used MySQL library isn't thread-safe."); @@ -49,20 +56,26 @@ DatabaseMysql::DatabaseMysql() : Database(), mMysql(0) } } } + DatabaseMysql::~DatabaseMysql() { if (m_delayThread) HaltDelayThread(); + if (mMysql) mysql_close(mMysql); + //Free Mysql library pointers for last ~DB if(--db_count == 0) mysql_library_end(); } + bool DatabaseMysql::Initialize(const char *infoString) { + if(!Database::Initialize(infoString)) return false; + tranThread = NULL; MYSQL *mysqlInit; mysqlInit = mysql_init(NULL); @@ -71,13 +84,19 @@ bool DatabaseMysql::Initialize(const char *infoString) sLog.outError( "Could not initialize Mysql connection" ); return false; } + InitDelayThread(); + Tokens tokens = StrSplit(infoString, ";"); + Tokens::iterator iter; + std::string host, port_or_socket, user, password, database; int port; char const* unix_socket; + iter = tokens.begin(); + if(iter != tokens.end()) host = *iter++; if(iter != tokens.end()) @@ -88,6 +107,7 @@ bool DatabaseMysql::Initialize(const char *infoString) password = *iter++; if(iter != tokens.end()) database = *iter++; + mysql_options(mysqlInit,MYSQL_SET_CHARSET_NAME,"utf8"); #ifdef WIN32 if(host==".") // named pipe use option (Windows) @@ -117,14 +137,17 @@ bool DatabaseMysql::Initialize(const char *infoString) unix_socket = 0; } #endif + mMysql = mysql_real_connect(mysqlInit, host.c_str(), user.c_str(), password.c_str(), database.c_str(), port, unix_socket, 0); + if (mMysql) { sLog.outDetail( "Connected to MySQL database at %s", host.c_str()); sLog.outString( "MySQL client library: %s", mysql_get_client_info()); sLog.outString( "MySQL server ver: %s ", mysql_get_server_info( mMysql)); + /*----------SET AUTOCOMMIT ON---------*/ // It seems mysql 5.0.x have enabled this feature // by default. In crash case you can lose data!!! @@ -138,10 +161,12 @@ bool DatabaseMysql::Initialize(const char *infoString) else sLog.outDetail("AUTOCOMMIT NOT SET TO 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 PExecute("SET NAMES `utf8`"); PExecute("SET CHARACTER SET `utf8`"); + #if MYSQL_VERSION_ID >= 50003 my_bool my_true = (my_bool)1; if (mysql_options(mMysql, MYSQL_OPT_RECONNECT, &my_true)) @@ -155,6 +180,7 @@ bool DatabaseMysql::Initialize(const char *infoString) #else #warning "Your mySQL client lib version does not support reconnecting after a timeout.\nIf this causes you any trouble we advice you to upgrade your mySQL client libs to at least mySQL 5.0.13 to resolve this problem." #endif + return true; } else @@ -165,10 +191,12 @@ bool DatabaseMysql::Initialize(const char *infoString) return false; } } + bool DatabaseMysql::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount) { if (!mMysql) return 0; + { // guarded block for thread-safe mySQL request ACE_Guard<ACE_Thread_Mutex> query_connection_guard(mMutex); @@ -187,54 +215,72 @@ bool DatabaseMysql::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **p sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql ); #endif } + *pResult = mysql_store_result(mMysql); *pRowCount = mysql_affected_rows(mMysql); *pFieldCount = mysql_field_count(mMysql); // end guarded block } + if (!*pResult ) return false; + if (!*pRowCount) { mysql_free_result(*pResult); return false; } + *pFields = mysql_fetch_fields(*pResult); return true; } + QueryResult* DatabaseMysql::Query(const char *sql) { MYSQL_RES *result = NULL; MYSQL_FIELD *fields = NULL; uint64 rowCount = 0; uint32 fieldCount = 0; + if(!_Query(sql,&result,&fields,&rowCount,&fieldCount)) return NULL; + QueryResultMysql *queryResult = new QueryResultMysql(result, fields, rowCount, fieldCount); + queryResult->NextRow(); + return queryResult; } + QueryNamedResult* DatabaseMysql::QueryNamed(const char *sql) { MYSQL_RES *result = NULL; MYSQL_FIELD *fields = NULL; uint64 rowCount = 0; uint32 fieldCount = 0; + if(!_Query(sql,&result,&fields,&rowCount,&fieldCount)) return NULL; + QueryFieldNames names(fieldCount); for (uint32 i = 0; i < fieldCount; i++) names[i] = fields[i].name; + QueryResultMysql *queryResult = new QueryResultMysql(result, fields, rowCount, fieldCount); + queryResult->NextRow(); + return new QueryNamedResult(queryResult,names); } + bool DatabaseMysql::Execute(const char *sql) { if (!mMysql) return false; + // don't use queued execution if it has not been initialized if (!m_threadBody) return DirectExecute(sql); + tranThread = ACE_Based::Thread::current(); // owner of this transaction TransactionQueues::iterator i = m_tranQueues.find(tranThread); if (i != m_tranQueues.end() && i->second != NULL) @@ -246,15 +292,19 @@ bool DatabaseMysql::Execute(const char *sql) // Simple sql statement m_threadBody->Delay(new SqlStatement(sql)); } + return true; } + bool DatabaseMysql::DirectExecute(const char* sql) { if (!mMysql) return false; + { // guarded block for thread-safe mySQL request ACE_Guard<ACE_Thread_Mutex> query_connection_guard(mMutex); + #ifdef MANGOS_DEBUG uint32 _s = getMSTime(); #endif @@ -272,8 +322,10 @@ bool DatabaseMysql::DirectExecute(const char* sql) } // end guarded block } + return true; } + bool DatabaseMysql::_TransactionCmd(const char *sql) { if (mysql_query(mMysql, sql)) @@ -288,15 +340,18 @@ bool DatabaseMysql::_TransactionCmd(const char *sql) } return true; } + bool DatabaseMysql::BeginTransaction() { if (!mMysql) return false; + // don't use queued execution if it has not been initialized if (!m_threadBody) { if (tranThread == ACE_Based::Thread::current()) return false; // huh? this thread already started transaction + mMutex.acquire(); if (!_TransactionCmd("START TRANSACTION")) { @@ -305,19 +360,24 @@ bool DatabaseMysql::BeginTransaction() } return true; // transaction started } + tranThread = ACE_Based::Thread::current(); // owner of this transaction TransactionQueues::iterator i = m_tranQueues.find(tranThread); if (i != m_tranQueues.end() && i->second != NULL) // If for thread exists queue and also contains transaction // delete that transaction (not allow trans in trans) delete i->second; + m_tranQueues[tranThread] = new SqlTransaction(); + return true; } + bool DatabaseMysql::CommitTransaction() { if (!mMysql) return false; + // don't use queued execution if it has not been initialized if (!m_threadBody) { @@ -328,6 +388,7 @@ bool DatabaseMysql::CommitTransaction() mMutex.release(); return _res; } + tranThread = ACE_Based::Thread::current(); TransactionQueues::iterator i = m_tranQueues.find(tranThread); if (i != m_tranQueues.end() && i->second != NULL) @@ -339,10 +400,12 @@ bool DatabaseMysql::CommitTransaction() else return false; } + bool DatabaseMysql::RollbackTransaction() { if (!mMysql) return false; + // don't use queued execution if it has not been initialized if (!m_threadBody) { @@ -353,6 +416,7 @@ bool DatabaseMysql::RollbackTransaction() mMutex.release(); return _res; } + tranThread = ACE_Based::Thread::current(); TransactionQueues::iterator i = m_tranQueues.find(tranThread); if (i != m_tranQueues.end() && i->second != NULL) @@ -362,22 +426,28 @@ bool DatabaseMysql::RollbackTransaction() } return true; } + unsigned long DatabaseMysql::escape_string(char *to, const char *from, unsigned long length) { if (!mMysql || !to || !from || !length) return 0; + return(mysql_real_escape_string(mMysql, to, from, length)); } + void DatabaseMysql::InitDelayThread() { assert(!m_delayThread); + //New delay thread for delay execute m_threadBody = new MySQLDelayThread(this); // will deleted at m_delayThread delete m_delayThread = new ACE_Based::Thread(m_threadBody); } + void DatabaseMysql::HaltDelayThread() { if (!m_threadBody || !m_delayThread) return; + m_threadBody->Stop(); //Stop event m_delayThread->wait(); //Wait for flush to DB delete m_delayThread; //This also deletes m_threadBody diff --git a/src/shared/Database/DatabaseMysql.h b/src/shared/Database/DatabaseMysql.h index 3a7fa4f5def..4612ebfc462 100644 --- a/src/shared/Database/DatabaseMysql.h +++ b/src/shared/Database/DatabaseMysql.h @@ -17,13 +17,17 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DO_POSTGRESQL + #ifndef _DATABASEMYSQL_H #define _DATABASEMYSQL_H + #include "Database.h" #include "Policies/Singleton.h" #include "ace/Thread_Mutex.h" #include "ace/Guard_T.h" + #ifdef WIN32 #define FD_SETSIZE 1024 #include <winsock2.h> @@ -31,12 +35,15 @@ #else #include <mysql.h> #endif + class TRINITY_DLL_SPEC DatabaseMysql : public Database { friend class Trinity::OperatorNew<DatabaseMysql>; + public: DatabaseMysql(); ~DatabaseMysql(); + //! Initializes Mysql and connects to a server. /*! infoString should be formated like hostname;username;password;database. */ bool Initialize(const char *infoString); @@ -49,18 +56,25 @@ class TRINITY_DLL_SPEC DatabaseMysql : public Database bool BeginTransaction(); bool CommitTransaction(); bool RollbackTransaction(); + operator bool () const { return mMysql != NULL; } + unsigned long escape_string(char *to, const char *from, unsigned long length); using Database::escape_string; + // must be call before first query in thread void ThreadStart(); // must be call before finish thread run void ThreadEnd(); private: ACE_Thread_Mutex mMutex; + ACE_Based::Thread * tranThread; + MYSQL *mMysql; + static size_t db_count; + bool _TransactionCmd(const char *sql); bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount); }; diff --git a/src/shared/Database/Field.cpp b/src/shared/Database/Field.cpp index 2467eabd448..9a1fbfa5178 100644 --- a/src/shared/Database/Field.cpp +++ b/src/shared/Database/Field.cpp @@ -17,21 +17,28 @@ * 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" + Field::Field() : mValue(NULL), mType(DB_TYPE_UNKNOWN) { } + Field::Field(Field &f) { const char *value; + value = f.GetString(); + if (value && (mValue = new char[strlen(value) + 1])) strcpy(mValue, value); else mValue = NULL; + mType = f.GetType(); } + Field::Field(const char *value, enum Field::DataTypes type) : mType(type) { @@ -40,13 +47,16 @@ mType(type) else mValue = NULL; } + Field::~Field() { if(mValue) delete [] mValue; } + void Field::SetValue(const char *value) { if(mValue) delete [] mValue; + if (value) { mValue = new char[strlen(value) + 1]; diff --git a/src/shared/Database/Field.h b/src/shared/Database/Field.h index fd259423aef..d1238f838a5 100644 --- a/src/shared/Database/Field.h +++ b/src/shared/Database/Field.h @@ -17,11 +17,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #if !defined(FIELD_H) #define FIELD_H + class Field { public: + enum DataTypes { DB_TYPE_UNKNOWN = 0x00, @@ -30,11 +33,15 @@ class Field DB_TYPE_FLOAT = 0x03, DB_TYPE_BOOL = 0x04 }; + Field(); Field(Field &f); Field(const char *value, enum DataTypes type); + ~Field(); + enum DataTypes GetType() const { return mType; } + const char *GetString() const { return mValue; } std::string GetCppString() const { @@ -69,8 +76,11 @@ class Field else return 0; } + void SetType(enum DataTypes type) { mType = type; } + void SetValue(const char *value); + private: char *mValue; enum DataTypes mType; diff --git a/src/shared/Database/MySQLDelayThread.h b/src/shared/Database/MySQLDelayThread.h index f8dba08bddc..fcebe3fbd35 100644 --- a/src/shared/Database/MySQLDelayThread.h +++ b/src/shared/Database/MySQLDelayThread.h @@ -17,9 +17,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef __MYSQLDELAYTHREAD_H #define __MYSQLDELAYTHREAD_H + #include "Database/SqlDelayThread.h" + class MySQLDelayThread : public SqlDelayThread { public: diff --git a/src/shared/Database/QueryResult.h b/src/shared/Database/QueryResult.h index 9d5bb57e4e9..f9f1a009833 100644 --- a/src/shared/Database/QueryResult.h +++ b/src/shared/Database/QueryResult.h @@ -17,39 +17,52 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #if !defined(QUERYRESULT_H) #define QUERYRESULT_H + class TRINITY_DLL_SPEC QueryResult { public: QueryResult(uint64 rowCount, uint32 fieldCount) : mFieldCount(fieldCount), mRowCount(rowCount) {} + virtual ~QueryResult() {} + virtual bool NextRow() = 0; + Field *Fetch() const { return mCurrentRow; } + const Field & operator [] (int index) const { return mCurrentRow[index]; } + uint32 GetFieldCount() const { return mFieldCount; } uint64 GetRowCount() const { return mRowCount; } + protected: Field *mCurrentRow; uint32 mFieldCount; uint64 mRowCount; }; + typedef std::vector<std::string> QueryFieldNames; + class MANGOS_DLL_SPEC QueryNamedResult { public: explicit QueryNamedResult(QueryResult* query, QueryFieldNames const& names) : mQuery(query), mFieldNames(names) {} ~QueryNamedResult() { delete mQuery; } + // compatible interface with QueryResult bool NextRow() { return mQuery->NextRow(); } Field *Fetch() const { return mQuery->Fetch(); } uint32 GetFieldCount() const { return mQuery->GetFieldCount(); } uint64 GetRowCount() const { return mQuery->GetRowCount(); } Field const& operator[] (int index) const { return (*mQuery)[index]; } + // named access Field const& operator[] (const std::string &name) const { return mQuery->Fetch()[GetField_idx(name)]; } QueryFieldNames const& GetFieldNames() const { return mFieldNames; } + uint32 GetField_idx(const std::string &name) const { for(size_t idx = 0; idx < mFieldNames.size(); ++idx) @@ -60,9 +73,11 @@ class MANGOS_DLL_SPEC QueryNamedResult ASSERT(false && "unknown field name"); return uint32(-1); } + protected: QueryResult *mQuery; QueryFieldNames mFieldNames; }; + #endif diff --git a/src/shared/Database/QueryResultMysql.cpp b/src/shared/Database/QueryResultMysql.cpp index ef8a77ec002..2e4738469c9 100644 --- a/src/shared/Database/QueryResultMysql.cpp +++ b/src/shared/Database/QueryResultMysql.cpp @@ -17,35 +17,47 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DO_POSTGRESQL + #include "DatabaseEnv.h" + QueryResultMysql::QueryResultMysql(MYSQL_RES *result, MYSQL_FIELD *fields, uint64 rowCount, uint32 fieldCount) : QueryResult(rowCount, fieldCount), mResult(result) { + mCurrentRow = new Field[mFieldCount]; ASSERT(mCurrentRow); + for (uint32 i = 0; i < mFieldCount; i++) mCurrentRow[i].SetType(ConvertNativeType(fields[i].type)); } + QueryResultMysql::~QueryResultMysql() { EndQuery(); } + bool QueryResultMysql::NextRow() { MYSQL_ROW row; + if (!mResult) return false; + row = mysql_fetch_row(mResult); if (!row) { EndQuery(); return false; } + for (uint32 i = 0; i < mFieldCount; i++) mCurrentRow[i].SetValue(row[i]); + return true; } + void QueryResultMysql::EndQuery() { if (mCurrentRow) @@ -53,12 +65,14 @@ void QueryResultMysql::EndQuery() delete [] mCurrentRow; mCurrentRow = 0; } + if (mResult) { mysql_free_result(mResult); mResult = 0; } } + enum Field::DataTypes QueryResultMysql::ConvertNativeType(enum_field_types mysqlType) const { switch (mysqlType) @@ -75,6 +89,7 @@ enum Field::DataTypes QueryResultMysql::ConvertNativeType(enum_field_types mysql case FIELD_TYPE_NULL: return Field::DB_TYPE_STRING; case FIELD_TYPE_TINY: + case FIELD_TYPE_SHORT: case FIELD_TYPE_LONG: case FIELD_TYPE_INT24: diff --git a/src/shared/Database/QueryResultMysql.h b/src/shared/Database/QueryResultMysql.h index fb9d422ebf2..89aceb12b13 100644 --- a/src/shared/Database/QueryResultMysql.h +++ b/src/shared/Database/QueryResultMysql.h @@ -17,9 +17,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef DO_POSTGRESQL + #if !defined(QUERYRESULTMYSQL_H) #define QUERYRESULTMYSQL_H + #ifdef WIN32 #define FD_SETSIZE 1024 #include <winsock2.h> @@ -27,15 +30,20 @@ #else #include <mysql.h> #endif + class QueryResultMysql : public QueryResult { public: QueryResultMysql(MYSQL_RES *result, MYSQL_FIELD *fields, uint64 rowCount, uint32 fieldCount); + ~QueryResultMysql(); + bool NextRow(); + private: enum Field::DataTypes ConvertNativeType(enum_field_types mysqlType) const; void EndQuery(); + MYSQL_RES *mResult; }; #endif diff --git a/src/shared/Database/SQLStorage.cpp b/src/shared/Database/SQLStorage.cpp index e4de05c7c77..372baafe278 100644 --- a/src/shared/Database/SQLStorage.cpp +++ b/src/shared/Database/SQLStorage.cpp @@ -17,13 +17,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "SQLStorage.h" #include "SQLStorageImpl.h" + #ifdef DO_POSTGRESQL extern DatabasePostgre WorldDatabase; #else extern DatabaseMysql WorldDatabase; #endif + const char CreatureInfosrcfmt[]="iiiiiiiisssiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiliiis"; const char CreatureInfodstfmt[]="iiiiiiiisssiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiliiii"; const char CreatureDataAddonInfofmt[]="iiiiiiis"; @@ -37,6 +40,7 @@ const char ItemPrototypedstfmt[]="iiiisiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii const char PageTextfmt[]="isi"; const char InstanceTemplatesrcfmt[]="iiiiiiffffs"; const char InstanceTemplatedstfmt[]="iiiiiiffffi"; + SQLStorage sCreatureStorage(CreatureInfosrcfmt, CreatureInfodstfmt, "entry","creature_template"); SQLStorage sCreatureDataAddonStorage(CreatureDataAddonInfofmt,"guid","creature_addon"); SQLStorage sCreatureModelStorage(CreatureModelfmt,"modelid","creature_model_info"); @@ -46,6 +50,7 @@ SQLStorage sGOStorage(GameObjectInfosrcfmt, GameObjectInfodstfmt, "entry","gameo SQLStorage sItemStorage(ItemPrototypesrcfmt, ItemPrototypedstfmt, "entry","item_template"); SQLStorage sPageTextStore(PageTextfmt,"entry","page_text"); SQLStorage sInstanceTemplate(InstanceTemplatesrcfmt, InstanceTemplatedstfmt, "map","instance_template"); + void SQLStorage::Free () { uint32 offset=0; @@ -55,6 +60,7 @@ void SQLStorage::Free () for(uint32 y=0;y<MaxEntry;y++) if(pIndex[y]) delete [] *(char**)((char*)(pIndex[y])+offset); + offset += sizeof(char*); } else if (dst_format[x]==FT_LOGIC) @@ -63,9 +69,11 @@ void SQLStorage::Free () offset += sizeof(char); else offset += 4; + delete [] pIndex; delete [] data; } + void SQLStorage::Load() { SQLStorageLoader loader; diff --git a/src/shared/Database/SQLStorage.h b/src/shared/Database/SQLStorage.h index 96f817c64e7..cc165af532e 100644 --- a/src/shared/Database/SQLStorage.h +++ b/src/shared/Database/SQLStorage.h @@ -17,21 +17,27 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef SQLSTORAGE_H #define SQLSTORAGE_H + #include "Common.h" #include "Database/DatabaseEnv.h" + class SQLStorage { template<class T> friend struct SQLStorageLoaderBase; + public: + SQLStorage(const char* fmt, const char * _entry_field, const char * sqlname) { src_format = fmt; dst_format = fmt; init(_entry_field, sqlname); } + SQLStorage(const char* src_fmt, const char* dst_fmt, const char * _entry_field, const char * sqlname) { src_format = src_fmt; @@ -39,10 +45,12 @@ class SQLStorage init(_entry_field, sqlname); } + ~SQLStorage() { Free(); } + template<class T> T const* LookupEntry(uint32 id) const { @@ -52,12 +60,16 @@ class SQLStorage return NULL; return reinterpret_cast<T const*>(pIndex[id]); } + uint32 RecordCount; uint32 MaxEntry; uint32 iNumFields; + char const* GetTableName() const { return table; } + void Load(); void Free(); + private: void init(const char * _entry_field, const char * sqlname) { @@ -68,7 +80,9 @@ class SQLStorage iNumFields = strlen(src_format); MaxEntry = 0; } + char** pIndex; + char *data; const char *src_format; const char *dst_format; @@ -76,11 +90,13 @@ class SQLStorage const char *entry_field; //bool HasString; }; + template <class T> struct SQLStorageLoaderBase { public: void Load(SQLStorage &storage); + template<class S, class D> void convert(uint32 field_pos, S src, D &dst); template<class S> @@ -88,13 +104,16 @@ struct SQLStorageLoaderBase template<class D> void convert_from_str(uint32 field_pos, char * src, D& dst); void convert_str_to_str(uint32 field_pos, char *src, char *&dst); + private: template<class V> void storeValue(V value, SQLStorage &store, char *p, int x, uint32 &offset); void storeValue(char * value, SQLStorage &store, char *p, int x, uint32 &offset); }; + struct SQLStorageLoader : public SQLStorageLoaderBase<SQLStorageLoader> { }; + #endif diff --git a/src/shared/Database/SQLStorageImpl.h b/src/shared/Database/SQLStorageImpl.h index c229327a0ec..b511bdad68c 100644 --- a/src/shared/Database/SQLStorageImpl.h +++ b/src/shared/Database/SQLStorageImpl.h @@ -15,15 +15,18 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "ProgressBar.h" #include "Log.h" #include "DBCFileLoader.h" + template<class T> template<class S, class D> void SQLStorageLoaderBase<T>::convert(uint32 /*field_pos*/, S src, D &dst) { dst = D(src); } + template<class T> void SQLStorageLoaderBase<T>::convert_str_to_str(uint32 /*field_pos*/, char *src, char *&dst) { @@ -39,6 +42,7 @@ void SQLStorageLoaderBase<T>::convert_str_to_str(uint32 /*field_pos*/, char *src memcpy(dst, src, l); } } + template<class T> template<class S> void SQLStorageLoaderBase<T>::convert_to_str(uint32 /*field_pos*/, S /*src*/, char * & dst) @@ -46,12 +50,14 @@ void SQLStorageLoaderBase<T>::convert_to_str(uint32 /*field_pos*/, S /*src*/, ch dst = new char[1]; *dst = 0; } + template<class T> template<class D> void SQLStorageLoaderBase<T>::convert_from_str(uint32 /*field_pos*/, char * /*src*/, D& dst) { dst = 0; } + template<class T> template<class V> void SQLStorageLoaderBase<T>::storeValue(V value, SQLStorage &store, char *p, int x, uint32 &offset) @@ -81,6 +87,7 @@ void SQLStorageLoaderBase<T>::storeValue(V value, SQLStorage &store, char *p, in break; } } + template<class T> void SQLStorageLoaderBase<T>::storeValue(char * value, SQLStorage &store, char *p, int x, uint32 &offset) { @@ -109,6 +116,7 @@ void SQLStorageLoaderBase<T>::storeValue(char * value, SQLStorage &store, char * break; } } + template<class T> void SQLStorageLoaderBase<T>::Load(SQLStorage &store) { @@ -120,8 +128,10 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store) sLog.outError("Error loading %s table (not exist?)\n", store.table); exit(1); // Stop server at loading non exited table or not accessable table } + maxi = (*result)[0].GetUInt32()+1; delete result; + result = WorldDatabase.PQuery("SELECT COUNT(*) FROM %s", store.table); if(result) { @@ -131,15 +141,19 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store) } else store.RecordCount = 0; + result = WorldDatabase.PQuery("SELECT * FROM %s", store.table); + if(!result) { sLog.outError("%s table is empty!\n", store.table); store.RecordCount = 0; return; } + uint32 recordsize = 0; uint32 offset = 0; + if(store.iNumFields != result->GetFieldCount()) { store.RecordCount = 0; @@ -147,6 +161,7 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store) delete result; exit(1); // Stop server at loading broken or non-compatible table. } + //get struct size uint32 sc=0; uint32 bo=0; @@ -159,8 +174,10 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store) else if (store.dst_format[x]==FT_BYTE) ++bb; recordsize=(store.iNumFields-sc-bo-bb)*4+sc*sizeof(char*)+bo*sizeof(bool)+bb*sizeof(char); + char** newIndex=new char*[maxi]; memset(newIndex,0,maxi*sizeof(char*)); + char * _data= new char[store.RecordCount *recordsize]; uint32 count=0; barGoLink bar( store.RecordCount ); @@ -170,6 +187,7 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store) bar.step(); char *p=(char*)&_data[recordsize*count]; newIndex[fields[0].GetUInt32()]=p; + offset=0; for(uint32 x = 0; x < store.iNumFields; x++) switch(store.src_format[x]) @@ -187,7 +205,9 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store) } ++count; }while( result->NextRow() ); + delete result; + store.pIndex = newIndex; store.MaxEntry = maxi; store.data = _data; diff --git a/src/shared/Database/SqlDelayThread.cpp b/src/shared/Database/SqlDelayThread.cpp index bca8dcd8cd6..88b6b85df70 100644 --- a/src/shared/Database/SqlDelayThread.cpp +++ b/src/shared/Database/SqlDelayThread.cpp @@ -17,17 +17,21 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Database/SqlDelayThread.h" #include "Database/SqlOperations.h" #include "DatabaseEnv.h" + SqlDelayThread::SqlDelayThread(Database* db) : m_dbEngine(db), m_running(true) { } + void SqlDelayThread::run() { #ifndef DO_POSTGRESQL mysql_thread_init(); #endif + while (m_running) { // if the running state gets turned off while sleeping @@ -40,10 +44,12 @@ void SqlDelayThread::run() delete s; } } + #ifndef DO_POSTGRESQL mysql_thread_end(); #endif } + void SqlDelayThread::Stop() { m_running = false; diff --git a/src/shared/Database/SqlDelayThread.h b/src/shared/Database/SqlDelayThread.h index 422b01ac650..3c24d3525b7 100644 --- a/src/shared/Database/SqlDelayThread.h +++ b/src/shared/Database/SqlDelayThread.h @@ -17,26 +17,34 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef __SQLDELAYTHREAD_H #define __SQLDELAYTHREAD_H + #include "ace/Thread_Mutex.h" #include "LockedQueue.h" #include "Threading.h" + class Database; class SqlOperation; + class SqlDelayThread : public ACE_Based::Runnable { typedef ACE_Based::LockedQueue<SqlOperation*, ACE_Thread_Mutex> SqlQueue; + private: SqlQueue m_sqlQueue; ///< Queue of SQL statements Database* m_dbEngine; ///< Pointer to used Database engine volatile bool m_running; + SqlDelayThread(); public: SqlDelayThread(Database* db); + ///< Put sql statement to delay queue bool Delay(SqlOperation* sql) { m_sqlQueue.add(sql); return true; } + virtual void Stop(); ///< Stop event virtual void run(); ///< Main Thread loop }; diff --git a/src/shared/Database/SqlOperations.cpp b/src/shared/Database/SqlOperations.cpp index 9ca698d733e..396f2e36bc2 100644 --- a/src/shared/Database/SqlOperations.cpp +++ b/src/shared/Database/SqlOperations.cpp @@ -17,16 +17,20 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "SqlOperations.h" #include "SqlDelayThread.h" #include "DatabaseEnv.h" #include "DatabaseImpl.h" + /// ---- ASYNC STATEMENTS / TRANSACTIONS ---- + void SqlStatement::Execute(Database *db) { /// just do it db->DirectExecute(m_sql); } + void SqlTransaction::Execute(Database *db) { if(m_queue.empty()) @@ -36,6 +40,7 @@ void SqlTransaction::Execute(Database *db) { char const *sql = m_queue.front(); m_queue.pop(); + if(!db->DirectExecute(sql)) { free((void*)const_cast<char*>(sql)); @@ -47,11 +52,14 @@ void SqlTransaction::Execute(Database *db) } return; } + free((void*)const_cast<char*>(sql)); } db->DirectExecute("COMMIT"); } + /// ---- ASYNC QUERIES ---- + void SqlQuery::Execute(Database *db) { if(!m_callback || !m_queue) @@ -61,6 +69,7 @@ void SqlQuery::Execute(Database *db) /// add the callback to the sql result queue of the thread it originated from m_queue->add(m_callback); } + void SqlResultQueue::Update() { /// execute the callbacks waiting in the synchronization queue @@ -71,16 +80,19 @@ void SqlResultQueue::Update() delete callback; } } + bool SqlQueryHolder::Execute(Trinity::IQueryCallback * callback, SqlDelayThread *thread, SqlResultQueue *queue) { if(!callback || !thread || !queue) return false; + /// delay the execution of the queries, sync them with the delay thread /// which will in turn resync on execution (via the queue) and call back SqlQueryHolderEx *holderEx = new SqlQueryHolderEx(this, callback, queue); thread->Delay(holderEx); return true; } + bool SqlQueryHolder::SetQuery(size_t index, const char *sql) { if(m_queries.size() <= index) @@ -88,16 +100,19 @@ bool SqlQueryHolder::SetQuery(size_t index, const char *sql) sLog.outError("Query index (%u) out of range (size: %u) for query: %s",index,(uint32)m_queries.size(),sql); return false; } + if(m_queries[index].first != NULL) { sLog.outError("Attempt assign query to holder index (%u) where other query stored (Old: [%s] New: [%s])", index,m_queries[index].first,sql); return false; } + /// not executed yet, just stored (it's not called a holder for nothing) m_queries[index] = SqlResultPair(strdup(sql), NULL); return true; } + bool SqlQueryHolder::SetPQuery(size_t index, const char *format, ...) { if(!format) @@ -105,18 +120,22 @@ bool SqlQueryHolder::SetPQuery(size_t index, const char *format, ...) sLog.outError("Query (index: %u) is empty.",index); return false; } + va_list ap; char szQuery [MAX_QUERY_LEN]; va_start(ap, format); int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap ); va_end(ap); + if(res==-1) { sLog.outError("SQL Query truncated (and not execute) for format: %s",format); return false; } + return SetQuery(index,szQuery); } + QueryResult* SqlQueryHolder::GetResult(size_t index) { if(index < m_queries.size()) @@ -133,12 +152,14 @@ QueryResult* SqlQueryHolder::GetResult(size_t index) else return NULL; } + void SqlQueryHolder::SetResult(size_t index, QueryResult *result) { /// store the result in the holder if(index < m_queries.size()) m_queries[index].second = result; } + SqlQueryHolder::~SqlQueryHolder() { for(size_t i = 0; i < m_queries.size(); i++) @@ -153,23 +174,28 @@ SqlQueryHolder::~SqlQueryHolder() } } } + void SqlQueryHolder::SetSize(size_t size) { /// to optimize push_back, reserve the number of queries about to be executed m_queries.resize(size); } + void SqlQueryHolderEx::Execute(Database *db) { if(!m_holder || !m_callback || !m_queue) return; + /// we can do this, we are friends std::vector<SqlQueryHolder::SqlResultPair> &queries = m_holder->m_queries; + for(size_t i = 0; i < queries.size(); i++) { /// execute all queries in the holder and pass the results char const *sql = queries[i].first; if(sql) m_holder->SetResult(i, db->Query(sql)); } + /// sync with the caller thread m_queue->add(m_callback); } diff --git a/src/shared/Database/SqlOperations.h b/src/shared/Database/SqlOperations.h index 164c7258ec3..e91d83b6611 100644 --- a/src/shared/Database/SqlOperations.h +++ b/src/shared/Database/SqlOperations.h @@ -17,16 +17,22 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #ifndef __SQLOPERATIONS_H #define __SQLOPERATIONS_H + #include "Common.h" + #include "ace/Thread_Mutex.h" #include "LockedQueue.h" #include <queue> #include "Utilities/Callback.h" + /// ---- BASE --- + class Database; class SqlDelayThread; + class SqlOperation { public: @@ -34,7 +40,9 @@ class SqlOperation virtual void Execute(Database *db) = 0; virtual ~SqlOperation() {} }; + /// ---- ASYNC STATEMENTS / TRANSACTIONS ---- + class SqlStatement : public SqlOperation { private: @@ -44,6 +52,7 @@ class SqlStatement : public SqlOperation ~SqlStatement() { void* tofree = const_cast<char*>(m_sql); free(tofree); } void Execute(Database *db); }; + class SqlTransaction : public SqlOperation { private: @@ -53,18 +62,22 @@ class SqlTransaction : public SqlOperation void DelayExecute(const char *sql) { m_queue.push(strdup(sql)); } void Execute(Database *db); }; + /// ---- ASYNC QUERIES ---- + class SqlQuery; /// contains a single async query class QueryResult; /// the result of one class SqlResultQueue; /// queue for thread sync class SqlQueryHolder; /// groups several async quries class SqlQueryHolderEx; /// points to a holder, added to the delay thread + class SqlResultQueue : public ACE_Based::LockedQueue<MaNGOS::IQueryCallback* , ACE_Thread_Mutex> { public: SqlResultQueue() {} void Update(); }; + class SqlQuery : public SqlOperation { private: @@ -77,6 +90,7 @@ class SqlQuery : public SqlOperation ~SqlQuery() { void* tofree = const_cast<char*>(m_sql); free(tofree); } void Execute(Database *db); }; + class SqlQueryHolder { friend class SqlQueryHolderEx; @@ -93,6 +107,7 @@ class SqlQueryHolder void SetResult(size_t index, QueryResult *result); bool Execute(Trinity::IQueryCallback * callback, SqlDelayThread *thread, SqlResultQueue *queue); }; + class SqlQueryHolderEx : public SqlOperation { private: |