aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/shared/Database/DatabaseWorkerPool.h10
-rw-r--r--src/server/shared/Database/MySQLConnection.cpp11
-rw-r--r--src/server/shared/Database/MySQLConnection.h1
-rw-r--r--src/server/shared/Database/QueryResult.cpp139
-rwxr-xr-xsrc/server/shared/Database/QueryResult.h143
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