Core/DBLayer: Implement lower level classes for prepared statements with resultsets. (Implementation in upper level code not yet possible)

--HG--
branch : trunk
This commit is contained in:
Machiavelli
2010-09-11 09:17:14 +02:00
parent 053cf5fd07
commit 1dce5b78d5
5 changed files with 303 additions and 1 deletions
@@ -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)
{
+10 -1
View File
@@ -335,4 +335,13 @@ void MySQLConnection::PrepareStatement(uint32 index, const char* sql)
MySQLPreparedStatement* mStmt = new MySQLPreparedStatement(stmt);
m_stmts[index] = mStmt;
}
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());
}
@@ -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();
+139
View File
@@ -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 );
}
+143
View File
@@ -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