diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/shared/Database/DatabaseWorkerPool.h | 10 | ||||
-rw-r--r-- | src/server/shared/Database/MySQLConnection.cpp | 11 | ||||
-rw-r--r-- | src/server/shared/Database/MySQLConnection.h | 1 | ||||
-rw-r--r-- | src/server/shared/Database/QueryResult.cpp | 139 | ||||
-rwxr-xr-x | src/server/shared/Database/QueryResult.h | 143 |
5 files changed, 303 insertions, 1 deletions
diff --git a/src/server/shared/Database/DatabaseWorkerPool.h b/src/server/shared/Database/DatabaseWorkerPool.h index d120314d230..fb4d3093b34 100644 --- a/src/server/shared/Database/DatabaseWorkerPool.h +++ b/src/server/shared/Database/DatabaseWorkerPool.h @@ -287,6 +287,16 @@ class DatabaseWorkerPool MySQLThreadBundle GetBundleMask() { return m_bundleMask; } + PreparedResultSet* Query(PreparedStatement* stmt) + { + PreparedResultSet* ret = GetConnection()->Query(stmt); + if (!ret || !ret->num_rows) + return NULL; + + ret->NextRow(); + return ret; + } + private: unsigned long escape_string(char *to, const char *from, unsigned long length) { diff --git a/src/server/shared/Database/MySQLConnection.cpp b/src/server/shared/Database/MySQLConnection.cpp index 00777d29ac8..47eeea3646a 100644 --- a/src/server/shared/Database/MySQLConnection.cpp +++ b/src/server/shared/Database/MySQLConnection.cpp @@ -335,4 +335,13 @@ void MySQLConnection::PrepareStatement(uint32 index, const char* sql) MySQLPreparedStatement* mStmt = new MySQLPreparedStatement(stmt); m_stmts[index] = mStmt; } -
\ No newline at end of file + +PreparedResultSet* MySQLConnection::Query(PreparedStatement* stmt) +{ + this->Execute(stmt); + if (mysql_more_results(m_Mysql)) + { + mysql_next_result(m_Mysql); + } + return new PreparedResultSet(stmt->m_stmt->GetSTMT()); +}
\ No newline at end of file diff --git a/src/server/shared/Database/MySQLConnection.h b/src/server/shared/Database/MySQLConnection.h index 7ed7855af7c..5eac4a38f61 100644 --- a/src/server/shared/Database/MySQLConnection.h +++ b/src/server/shared/Database/MySQLConnection.h @@ -41,6 +41,7 @@ class MySQLConnection bool Execute(const char* sql); bool Execute(PreparedStatement* stmt); QueryResult_AutoPtr Query(const char* sql); + PreparedResultSet* Query(PreparedStatement* stmt); bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount); void BeginTransaction(); diff --git a/src/server/shared/Database/QueryResult.cpp b/src/server/shared/Database/QueryResult.cpp index dfcb3a94acd..c5a081a514e 100644 --- a/src/server/shared/Database/QueryResult.cpp +++ b/src/server/shared/Database/QueryResult.cpp @@ -103,3 +103,142 @@ enum Field::DataTypes QueryResult::ConvertNativeType(enum_field_types mysqlType) return Field::DB_TYPE_UNKNOWN; } } + +void ResultBind::BindResult(uint32& num_rows) +{ + FreeBindBuffer(); + m_fieldCount = mysql_stmt_field_count(m_stmt); + if (!m_fieldCount) + return; + + m_rBind = new MYSQL_BIND[m_fieldCount]; + memset(m_rBind, 0, sizeof(MYSQL_BIND) * m_fieldCount); + + m_isNull = new my_bool[m_fieldCount]; + memset(m_isNull, 0, sizeof(my_bool) * m_fieldCount); + + m_length = new unsigned long[m_fieldCount]; + memset(m_length, 0, sizeof(unsigned long) * m_fieldCount); + + m_res = mysql_stmt_result_metadata(m_stmt); + + //- This is where we store the (entire) resultset + if (mysql_stmt_store_result(m_stmt)) + { + sLog.outSQLDriver("%s:mysql_stmt_store_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); + return; + } + + //- This is where we prepare the buffer based on metadata + uint32 i = 0; + MYSQL_FIELD* field; + while (field = mysql_fetch_field(m_res)) + { + size_t size = SizeForType(field); + if (size == 0) + size = field->max_length + 1; + + m_rBind[i].buffer_type = field->type; + m_rBind[i].buffer = new char[size]; + memset(m_rBind[i].buffer, 0, size); + m_rBind[i].buffer_length = size; + m_rBind[i].length = &m_length[i]; + m_rBind[i].is_null = &m_isNull[i]; + m_rBind[i].error = NULL;//&m_error[i]; + m_rBind[i].is_unsigned = field->flags & UNSIGNED_FLAG; + + ++i; + } + + //- This is where we bind the bind the buffer to the statement + if (mysql_stmt_bind_result(m_stmt, m_rBind)) + { + sLog.outSQLDriver("%s:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: %s", __FUNCTION__, mysql_stmt_error(m_stmt)); + return; + } + + num_rows = mysql_stmt_num_rows(m_stmt); +} + +void ResultBind::FreeBindBuffer() +{ + for (uint32 i = 0; i < m_fieldCount; ++i) + { + delete[] (char *) m_rBind[i].buffer; + m_rBind[i].buffer = NULL; + } + m_rBind = NULL; +} + +void ResultBind::CleanUp() +{ + FreeBindBuffer(); + delete[] m_isNull; + delete[] m_length; +} + +uint8 PreparedResultSet::GetUInt8(uint32 index) +{ + if (!CheckFieldIndex(index)) + return 0; + + return *reinterpret_cast<uint8*>(rbind->m_rBind[index].buffer); +} + +int8 PreparedResultSet::GetInt8(uint32 index) +{ + if (!CheckFieldIndex(index)) + return 0; + + return *reinterpret_cast<int8*>(rbind->m_rBind[index].buffer); +} + +uint16 PreparedResultSet::GetUInt16(uint32 index) +{ + if (!CheckFieldIndex(index)) + return 0; + + return *reinterpret_cast<uint16*>(rbind->m_rBind[index].buffer); +} + +int16 PreparedResultSet::GetInt16(uint32 index) +{ + if (!CheckFieldIndex(index)) + return 0; + + return *reinterpret_cast<int16*>(rbind->m_rBind[index].buffer); +} + +uint32 PreparedResultSet::GetUInt32(uint32 index) +{ + if (!CheckFieldIndex(index)) + return 0; + + return *reinterpret_cast<uint32*>(rbind->m_rBind[index].buffer); +} + +int32 PreparedResultSet::GetInt32(uint32 index) +{ + if (!CheckFieldIndex(index)) + return 0; + + return *reinterpret_cast<int32*>(rbind->m_rBind[index].buffer); +} + +float PreparedResultSet::GetFloat(uint32 index) +{ + if (!CheckFieldIndex(index)) + return 0; + + return *reinterpret_cast<float*>(rbind->m_rBind[index].buffer); +} + +std::string PreparedResultSet::GetString(uint32 index) +{ + if (!CheckFieldIndex(index)) + return std::string(""); + + const char* temp = static_cast<char const*>(rbind->m_rBind[index].buffer); + size_t len = *rbind->m_rBind[index].length; + return std::string(temp, len ); +}
\ No newline at end of file diff --git a/src/server/shared/Database/QueryResult.h b/src/server/shared/Database/QueryResult.h index 5dfb03a1a16..1911cf78dac 100755 --- a/src/server/shared/Database/QueryResult.h +++ b/src/server/shared/Database/QueryResult.h @@ -95,5 +95,148 @@ class QueryNamedResult QueryFieldNames mFieldNames; }; +class ResultBind +{ + friend class PreparedResultSet; + public: + + ResultBind(MYSQL_STMT* stmt) : m_stmt(stmt), m_fieldCount(NULL), m_isNull(NULL), m_length(NULL), m_rBind(NULL) {} + ~ResultBind() + { + if (!m_fieldCount) + return; + + CleanUp(); // Clean up buffer + mysql_stmt_free_result(m_stmt); + } + + void BindResult(uint32& num_rows); + + protected: + MYSQL_BIND* m_rBind; + MYSQL_STMT* m_stmt; + MYSQL_RES* m_res; + + void FreeBindBuffer(); + bool IsValidIndex(uint32 index) { return index < m_fieldCount; } + + private: + + void CleanUp(); + + size_t SizeForType(MYSQL_FIELD* field) + { + switch (field->type) + { + case MYSQL_TYPE_NULL: + return 0; + case MYSQL_TYPE_TINY: + return 1; + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_SHORT: + return 2; + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + case MYSQL_TYPE_FLOAT: + return 4; + case MYSQL_TYPE_DOUBLE: + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_BIT: + return 8; + + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + return sizeof(MYSQL_TIME); + + case MYSQL_TYPE_TINY_BLOB: + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: + case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_STRING: + case MYSQL_TYPE_VAR_STRING: + return field->max_length + 1; + + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return 64; + + case MYSQL_TYPE_GEOMETRY: + /* + Following types are not sent over the wire: + MYSQL_TYPE_ENUM: + MYSQL_TYPE_SET: + */ + default: + sLog.outSQLDriver("ResultBind::SizeForType(): invalid field type %u", uint32(field->type)); + return 0; + } + } + + my_bool* m_isNull; + unsigned long* m_length; + uint32 m_fieldCount; +}; + +class PreparedResultSet +{ + template<class T> friend class DatabaseWorkerPool; + public: + PreparedResultSet(MYSQL_STMT* stmt) : num_rows(0), row_position(0) + { + rbind = new ResultBind(stmt); + rbind->BindResult(num_rows); + } + ~PreparedResultSet() + { + delete rbind; + } + + operator bool() { return num_rows > 0; } + + uint8 GetUInt8(uint32 index); + int8 GetInt8(uint32 index); + uint16 GetUInt16(uint32 index); + int16 GetInt16(uint32 index); + uint32 GetUInt32(uint32 index); + int32 GetInt32(uint32 index); + float GetFloat(uint32 index); + std::string GetString(uint32 index); + + bool NextRow() + { + if (row_position >= num_rows) + return false; + + int retval = mysql_stmt_fetch( rbind->m_stmt ); + + if (!retval || retval == MYSQL_DATA_TRUNCATED) + retval = true; + + if (retval == MYSQL_NO_DATA) + retval = false; + + ++row_position; + return retval; + } + + private: + bool CheckFieldIndex(uint32 index) const + { + if (!rbind->IsValidIndex(index)) + return false; + + if (rbind->m_isNull[index]) + return false; + + return true; + } + + ResultBind* rbind; + uint32 row_position; + uint32 num_rows; +}; + #endif |