diff options
author | Machiavelli <none@none> | 2010-09-02 20:54:43 +0200 |
---|---|---|
committer | Machiavelli <none@none> | 2010-09-02 20:54:43 +0200 |
commit | 1198591bacb3a80bbe0f57c8fc7d8b883b1a5ecd (patch) | |
tree | afed62f085f6271d6cb5ede9647475d6b502ec82 /src | |
parent | cd182a1e8fea18c8c054f586bd369daa5bf085ba (diff) |
Core/DBLayer:
- Add basic prepared statement interface (without implementation and thus without testing, without resultset support).
- Based on raczman/Albator´s work on TrinityCore3
Build:
- Add sLog define ¨hack¨ in DatbaseWorkerPool.h to fix some more sLog errors temporarily
FYI: Builds on windows
--HG--
branch : trunk
Diffstat (limited to 'src')
-rw-r--r-- | src/server/shared/Database/DatabaseWorkerPool.h | 14 | ||||
-rw-r--r-- | src/server/shared/Database/MySQLConnection.cpp | 78 | ||||
-rw-r--r-- | src/server/shared/Database/MySQLConnection.h | 6 | ||||
-rw-r--r-- | src/server/shared/Database/PreparedStatement.cpp | 325 | ||||
-rw-r--r-- | src/server/shared/Database/PreparedStatement.h | 147 |
5 files changed, 570 insertions, 0 deletions
diff --git a/src/server/shared/Database/DatabaseWorkerPool.h b/src/server/shared/Database/DatabaseWorkerPool.h index 641344c1f7b..6f16235c200 100644 --- a/src/server/shared/Database/DatabaseWorkerPool.h +++ b/src/server/shared/Database/DatabaseWorkerPool.h @@ -31,6 +31,9 @@ #include "MySQLConnection.h" #include "DatabaseWorker.h" +// TODO: Fixme +#define sLog (*ACE_Singleton<Log, ACE_Thread_Mutex>::instance()) + enum MySQLThreadBundle { MYSQL_BUNDLE_NONE = 0x00, //- Each task will run their own MySQL connection @@ -257,6 +260,17 @@ class DatabaseWorkerPool Enqueue(new TransactionTask(transaction)); } + PreparedStatement* GetPreparedStatement(uint32 index) + { + return new PreparedStatement(index); + } + + void Execute(PreparedStatement* stmt) + { + PreparedStatementTask* task = new PreparedStatementTask(stmt); + Enqueue(task); + } + void escape_string(std::string& str) { if (str.empty()) diff --git a/src/server/shared/Database/MySQLConnection.cpp b/src/server/shared/Database/MySQLConnection.cpp index 91084af71b8..e1149fa16c1 100644 --- a/src/server/shared/Database/MySQLConnection.cpp +++ b/src/server/shared/Database/MySQLConnection.cpp @@ -27,6 +27,7 @@ #include "DatabaseEnv.h" #include "QueryResult.h" #include "SQLOperation.h" +#include "PreparedStatement.h" #include "MySQLConnection.h" #include "DatabaseWorker.h" #include "Log.h" @@ -49,6 +50,9 @@ m_Mysql(NULL) MySQLConnection::~MySQLConnection() { + for (size_t i = 0; i < m_stmts.size(); ++i) + delete m_stmts[i]; + MySQL::Thread_End(); mysql_close(m_Mysql); } @@ -181,6 +185,52 @@ bool MySQLConnection::Execute(const char* sql) return true; } +bool MySQLConnection::Execute(PreparedStatement* stmt) +{ + if (!m_Mysql) + return false; + + uint32 index = stmt->m_index; + MySQLPreparedStatement* m_mStmt = GetPreparedStatement(index); + m_mStmt->m_stmt = stmt; // Cross reference them for debug output + stmt->m_stmt = m_mStmt; + + { + // guarded block for thread-safe mySQL request + ACE_Guard<ACE_Thread_Mutex> query_connection_guard(m_Mutex); + + stmt->BindParameters(); + + MYSQL_STMT* msql_STMT = m_mStmt->GetSTMT(); + MYSQL_BIND* msql_BIND = m_mStmt->GetBind(); + + #ifdef TRINITY_DEBUG + uint32 _s = getMSTime(); + #endif + if (mysql_stmt_bind_param(msql_STMT, msql_BIND)) + { + sLog.outSQLDriver("[ERROR]: PreparedStatement (id: %u) error binding params: %s", index, mysql_stmt_error(msql_STMT)); + m_mStmt->ClearParameters(); + return false; + } + + if (mysql_stmt_execute(msql_STMT)) + { + sLog.outSQLDriver("[ERROR]: PreparedStatement (id: %u) error executing: %s", index, mysql_stmt_error(msql_STMT)); + m_mStmt->ClearParameters(); + return false; + } + else + { + #ifdef TRINITY_DEBUG + sLog.outSQLDriver("[%u ms] Prepared SQL: %u", getMSTimeDiff(_s, getMSTime()), index); + #endif + m_mStmt->ClearParameters(); + return true; + } + } +} + QueryResult_AutoPtr MySQLConnection::Query(const char* sql) { if (!sql) @@ -257,3 +307,31 @@ void MySQLConnection::CommitTransaction() { Execute("COMMIT"); } + +MySQLPreparedStatement* MySQLConnection::GetPreparedStatement(uint32 index) +{ + return m_stmts[index]; +} + +void MySQLConnection::PrepareStatement(uint32 index, const char* sql) +{ + MYSQL_STMT * stmt = mysql_stmt_init(m_Mysql); + if (!stmt) + { + sLog.outSQLDriver("[ERROR]: In mysql_stmt_init() id: %u, sql: \"%s\"", index, sql); + sLog.outSQLDriver("[ERROR]: %s", mysql_error(m_Mysql)); + return; + } + + if (mysql_stmt_prepare(stmt, sql, static_cast<unsigned long>(strlen(sql)))) + { + mysql_stmt_close(stmt); + sLog.outSQLDriver("[ERROR]: In mysql_stmt_close() id: %u, sql: \"%s\"", index, sql); + sLog.outSQLDriver("[ERROR]: %s", mysql_error(m_Mysql)); + return; + } + + MySQLPreparedStatement* mStmt = new MySQLPreparedStatement(stmt); + m_stmts[index] = mStmt; +} +
\ No newline at end of file diff --git a/src/server/shared/Database/MySQLConnection.h b/src/server/shared/Database/MySQLConnection.h index 5e68e9d7516..c0f792b1191 100644 --- a/src/server/shared/Database/MySQLConnection.h +++ b/src/server/shared/Database/MySQLConnection.h @@ -20,6 +20,8 @@ #define _MYSQLCONNECTION_H class DatabaseWorker; +class PreparedStatement; +class MySQLPreparedStatement; class MySQLConnection { @@ -34,6 +36,7 @@ class MySQLConnection public: bool Execute(const char* sql); + bool Execute(PreparedStatement* stmt); QueryResult_AutoPtr Query(const char* sql); bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount); @@ -45,12 +48,15 @@ class MySQLConnection protected: MYSQL* GetHandle() { return m_Mysql; } + MySQLPreparedStatement* GetPreparedStatement(uint32 index); + void PrepareStatement(uint32 index, const char* sql); private: ACE_Activation_Queue* m_queue; //! Queue shared with other asynchroneous connections. DatabaseWorker* m_worker; //! Core worker task. MYSQL * m_Mysql; //! MySQL Handle. ACE_Thread_Mutex m_Mutex; + std::vector<MySQLPreparedStatement*> m_stmts; //! PreparedStatements storage }; #endif
\ No newline at end of file diff --git a/src/server/shared/Database/PreparedStatement.cpp b/src/server/shared/Database/PreparedStatement.cpp new file mode 100644 index 00000000000..327caf71ab7 --- /dev/null +++ b/src/server/shared/Database/PreparedStatement.cpp @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "PreparedStatement.h" +#include "MySQLConnection.h" + +PreparedStatement::PreparedStatement(uint32 index) : +m_index(index), +m_stmt(NULL) +{ +} + +PreparedStatement::~PreparedStatement() +{ +} + +void PreparedStatement::BindParameters() +{ + ASSERT (m_stmt); + + uint32 i = 0; + for (; i < statement_data.size(); i++) + { + switch (statement_data[i].type) + { + case TYPE_BOOL: + m_stmt->setBool(i, statement_data[i].data.boolean); + break; + case TYPE_UI8: + case TYPE_UI16: + case TYPE_UI32: + m_stmt->setUInt32(i, statement_data[i].data.ui32); + break; + case TYPE_I8: + case TYPE_I16: + case TYPE_I32: + m_stmt->setInt32(i, statement_data[i].data.i32); + break; + case TYPE_UI64: + m_stmt->setUInt64(i, statement_data[i].data.ui64); + break; + case TYPE_I64: + m_stmt->setInt64(i, statement_data[i].data.i64); + break; + case TYPE_FLOAT: + m_stmt->setDouble(i, statement_data[i].data.f); + break; + case TYPE_STRING: + m_stmt->setString(i, statement_data[i].str.c_str()); + break; + } + } + #ifdef _DEBUG + if (i < m_stmt->m_paramCount) + sLog.outSQLDriver("[WARNING]: BindParameters() for statement %u did not bind all allocated parameters", m_index); + #endif +} + +//- Bind to buffer +void PreparedStatement::setBool(const uint8 index, const bool value) +{ + if (index >= statement_data.size()) + statement_data.resize(index+1); + + statement_data[index].data.boolean = value; + statement_data[index].type = TYPE_BOOL; +} + +void PreparedStatement::setUInt8(const uint8 index, const uint8 value) +{ + if (index >= statement_data.size()) + statement_data.resize(index+1); + + statement_data[index].data.ui32 = value; + statement_data[index].type = TYPE_UI8; +} + +void PreparedStatement::setUInt16(const uint8 index, const uint16 value) +{ + if (index >= statement_data.size()) + statement_data.resize(index+1); + + statement_data[index].data.ui32 = value; + statement_data[index].type = TYPE_UI16; +} + +void PreparedStatement::setUInt32(const uint8 index, const uint32 value) +{ + if (index >= statement_data.size()) + statement_data.resize(index+1); + + statement_data[index].data.ui32 = value; + statement_data[index].type = TYPE_UI32; +} + +void PreparedStatement::setUInt64(const uint8 index, const uint64 value) +{ + if (index >= statement_data.size()) + statement_data.resize(index+1); + + statement_data[index].data.ui64 = value; + statement_data[index].type = TYPE_UI64; +} + +void PreparedStatement::setInt8(const uint8 index, const int8 value) +{ + if (index >= statement_data.size()) + statement_data.resize(index+1); + + statement_data[index].data.i32 = value; + statement_data[index].type = TYPE_I8; +} + +void PreparedStatement::setInt16(const uint8 index, const int16 value) +{ + if (index >= statement_data.size()) + statement_data.resize(index+1); + + statement_data[index].data.i32 = value; + statement_data[index].type = TYPE_I16; +} + +void PreparedStatement::setInt32(const uint8 index, const int32 value) +{ + if (index >= statement_data.size()) + statement_data.resize(index+1); + + statement_data[index].data.i32 = value; + statement_data[index].type = TYPE_I32; +} + +void PreparedStatement::setInt64(const uint8 index, const int64 value) +{ + if (index >= statement_data.size()) + statement_data.resize(index+1); + + statement_data[index].data.i64 = value; + statement_data[index].type = TYPE_I64; +} + +void PreparedStatement::setDouble(const uint8 index, const double value) +{ + if (index >= statement_data.size()) + statement_data.resize(index+1); + + statement_data[index].data.f = value; + statement_data[index].type = TYPE_FLOAT; +} + +void PreparedStatement::setString(const uint8 index, const std::string& value) +{ + if (index >= statement_data.size()) + statement_data.resize(index+1); + + statement_data[index].str = value; + statement_data[index].type = TYPE_STRING; +} + +MySQLPreparedStatement::MySQLPreparedStatement(MYSQL_STMT* stmt) : +m_Mstmt(stmt), +m_bind(NULL) +{ + /// Initialize variable parameters + m_paramCount = mysql_stmt_param_count(stmt); + m_paramsSet.assign(m_paramCount, false); + m_bind = new MYSQL_BIND[m_paramCount]; + memset(m_bind, 0, sizeof(MYSQL_BIND)*m_paramCount); + + /// "If set to 1, causes mysql_stmt_store_result() to update the metadata MYSQL_FIELD->max_length value." + my_bool bool_tmp = 1; + mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, &bool_tmp); +} + +MySQLPreparedStatement::~MySQLPreparedStatement() +{ + ClearParameters(); + mysql_stmt_close(m_Mstmt); + delete[] m_bind; +} + +void MySQLPreparedStatement::ClearParameters() +{ + for (uint32 i=0; i < m_paramCount; ++i) + { + delete m_bind[i].length; + m_bind[i].length = NULL; + delete[] m_bind[i].buffer; + m_bind[i].buffer = NULL; + m_paramsSet[i] = false; + } +} + +//- Bind on mysql level +bool MySQLPreparedStatement::CheckValidIndex(uint8 index) +{ + if (index >= m_paramCount) + return false; + + if (m_paramsSet[index]) + sLog.outSQLDriver("[WARNING] Prepared Statement (id: %u) trying to bind value on already bound index (%u).", m_stmt->m_index, index); + return true; +} + +void MySQLPreparedStatement::setBool(const uint8 index,const bool value) +{ + setUInt32(index, value); +} + +void MySQLPreparedStatement::setUInt8(const uint8 index, const uint8 value) +{ + setUInt32(index, value); +} + +void MySQLPreparedStatement::setUInt16(const uint8 index, const uint16 value) +{ + setUInt32(index, value); +} + +void MySQLPreparedStatement::setUInt32(const uint8 index, const uint32 value) +{ + CheckValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + setValue(param, MYSQL_TYPE_LONG, &value, sizeof(uint32), true); +} + +void MySQLPreparedStatement::setUInt64(const uint8 index, const uint64 value) +{ + CheckValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + setValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(uint64), true); +} + +void MySQLPreparedStatement::setInt8(const uint8 index, const int8 value) +{ + setInt32(index, value); +} + +void MySQLPreparedStatement::setInt16(const uint8 index, const int16 value) +{ + setInt32(index, value); +} + +void MySQLPreparedStatement::setInt32(const uint8 index, const int32 value) +{ + CheckValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + setValue(param, MYSQL_TYPE_LONG, &value, sizeof(int32), false); +} + +void MySQLPreparedStatement::setInt64(const uint8 index, const int64 value) +{ + CheckValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + setValue(param, MYSQL_TYPE_LONGLONG, &value, sizeof(int64), false); +} + +void MySQLPreparedStatement::setDouble(const uint8 index, const double value) +{ + CheckValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + setValue(param, MYSQL_TYPE_DOUBLE, &value, sizeof(double), (value > 0.0f)); +} + +void MySQLPreparedStatement::setString(const uint8 index, const char* value) +{ + CheckValidIndex(index); + m_paramsSet[index] = true; + MYSQL_BIND* param = &m_bind[index]; + size_t len = strlen(value) + 1; + param->buffer_type = MYSQL_TYPE_VAR_STRING; + param->buffer = new char[len]; + param->buffer_length = len; + param->is_null_value = 0; + param->length = new unsigned long(len-1); + + memcpy(param->buffer, value, len); +} + +void MySQLPreparedStatement::setValue(MYSQL_BIND* param, enum_field_types type, const void* value, uint32 len, bool isUnsigned) +{ + param->buffer_type = type; + param->buffer = new char[len]; + param->buffer_length = 0; + param->is_null_value = 0; + param->length = NULL; // Only != NULL for strings + param->is_unsigned = isUnsigned; + + memcpy(param->buffer, value, len); +} + +//- Execution +PreparedStatementTask::PreparedStatementTask(PreparedStatement* stmt) : +m_stmt(stmt) +{ +} + +PreparedStatementTask::~PreparedStatementTask() +{ + delete m_stmt; +} + +bool PreparedStatementTask::Execute() +{ + return m_conn->Execute(m_stmt); +}
\ No newline at end of file diff --git a/src/server/shared/Database/PreparedStatement.h b/src/server/shared/Database/PreparedStatement.h new file mode 100644 index 00000000000..580f4ee14a0 --- /dev/null +++ b/src/server/shared/Database/PreparedStatement.h @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _PREPAREDSTATEMENT_H +#define _PREPAREDSTATEMENT_H + +#include "SQLOperation.h" + +//- Union for data buffer (upper-level bind -> queue -> lower-level bind) +union PreparedStatementDataUnion +{ + bool boolean; + uint8 ui8; + int8 i8; + uint16 ui16; + int16 i16; + uint32 ui32; + int32 i32; + uint64 ui64; + int64 i64; + double f; // TODO: Maybe split it up in double and float types +}; + +//- This enum helps us differ data held in above union +enum PreparedStatementValueType +{ + TYPE_BOOL, + TYPE_UI8, + TYPE_UI16, + TYPE_UI32, + TYPE_UI64, + TYPE_I8, + TYPE_I16, + TYPE_I32, + TYPE_I64, + TYPE_FLOAT, + TYPE_STRING +}; + +struct PreparedStatementData +{ + PreparedStatementDataUnion data; + PreparedStatementValueType type; + std::string str; +}; + +//- Upper-level class that is used in code +class PreparedStatement +{ + friend class PreparedStatementTask; + friend class MySQLPreparedStatement; + friend class MySQLConnection; + + public: + explicit PreparedStatement(uint32 index); + ~PreparedStatement(); + + void setBool(const uint8 index,const bool value); + void setUInt8(const uint8 index, const uint8 value); + void setUInt16(const uint8 index, const uint16 value); + void setUInt32(const uint8 index, const uint32 value); + void setUInt64(const uint8 index, const uint64 value); + void setInt8(const uint8 index, const int8 value); + void setInt16(const uint8 index, const int16 value); + void setInt32(const uint8 index, const int32 value); + void setInt64(const uint8 index, const int64 value); + void setDouble(const uint8 index, const double value); + void setString(const uint8 index, const std::string& value); + + protected: + void BindParameters(); + + protected: + MySQLPreparedStatement* m_stmt; + uint32 m_index; + std::vector<PreparedStatementData> statement_data; //- Buffer of parameters, not tied to MySQL in any way yet +}; + +//- Class of which the instances are unique per MySQLConnection +//- access to these class objects is only done when a prepared statement task +//- is executed. +class MySQLPreparedStatement +{ + friend class MySQLConnection; + friend class PreparedStatement; + + public: + MySQLPreparedStatement(MYSQL_STMT* stmt); + ~MySQLPreparedStatement(); + + void setBool(const uint8 index,const bool value); + void setUInt8(const uint8 index, const uint8 value); + void setUInt16(const uint8 index, const uint16 value); + void setUInt32(const uint8 index, const uint32 value); + void setUInt64(const uint8 index, const uint64 value); + void setInt8(const uint8 index, const int8 value); + void setInt16(const uint8 index, const int16 value); + void setInt32(const uint8 index, const int32 value); + void setInt64(const uint8 index, const int64 value); + void setDouble(const uint8 index, const double value); + void setString(const uint8 index, const char* value); + + protected: + MYSQL_STMT* GetSTMT() { return m_Mstmt; } + MYSQL_BIND* GetBind() { return m_bind; } + PreparedStatement* m_stmt; + void ClearParameters(); + bool CheckValidIndex(uint8 index); + + private: + void setValue(MYSQL_BIND* param, enum_field_types type, const void* value, uint32 len, bool isUnsigned); + + private: + MYSQL_STMT* m_Mstmt; + uint32 m_paramCount; + std::vector<bool> m_paramsSet; + MYSQL_BIND* m_bind; +}; + +//- Lower-level class, enqueuable operation +class PreparedStatementTask : public SQLOperation +{ + public: + PreparedStatementTask(PreparedStatement* stmt); + ~PreparedStatementTask(); + + bool Execute(); + + protected: + PreparedStatement* m_stmt; +}; +#endif
\ No newline at end of file |