AzerothCore 3.3.5a
OpenSource WoW Emulator
Loading...
Searching...
No Matches
PreparedResultSet Class Reference

#include "QueryResult.h"

Public Member Functions

 PreparedResultSet (MySQLStmt *stmt, MySQLResult *result, uint64 rowCount, uint32 fieldCount)
 
 ~PreparedResultSet ()
 
bool NextRow ()
 
uint64 GetRowCount () const
 
uint32 GetFieldCount () const
 
FieldFetch () const
 
Field const & operator[] (std::size_t index) const
 
template<typename... Ts>
std::tuple< Ts... > FetchTuple ()
 
auto begin ()
 

Static Public Member Functions

static auto end ()
 

Protected Attributes

std::vector< QueryResultFieldMetadatam_fieldMetadata
 
std::vector< Fieldm_rows
 
uint64 m_rowCount
 
uint64 m_rowPosition
 
uint32 m_fieldCount
 

Private Member Functions

void CleanUp ()
 
bool _NextRow ()
 
void AssertRows (std::size_t sizeRows)
 
 PreparedResultSet (PreparedResultSet const &right)=delete
 
PreparedResultSetoperator= (PreparedResultSet const &right)=delete
 

Private Attributes

MySQLBindm_rBind
 
MySQLStmtm_stmt
 
MySQLResultm_metadataResult
 Field metadata, returned by mysql_stmt_result_metadata.
 

Detailed Description

Constructor & Destructor Documentation

◆ PreparedResultSet() [1/2]

PreparedResultSet::PreparedResultSet ( MySQLStmt stmt,
MySQLResult result,
uint64  rowCount,
uint32  fieldCount 
)
Todo:
: remove Field::GetCString and use std::string_view in C++17

All data is buffered, let go of mysql c api structures

248 :
249 m_rowCount(rowCount),
250 m_rowPosition(0),
251 m_fieldCount(fieldCount),
252 m_rBind(nullptr),
253 m_stmt(stmt),
254 m_metadataResult(result)
255{
256 if (!m_metadataResult)
257 return;
258
259 if (m_stmt->bind_result_done)
260 {
261 delete[] m_stmt->bind->length;
262 delete[] m_stmt->bind->is_null;
263 }
264
266
267 //- for future readers wondering where this is freed - mysql_stmt_bind_result moves pointers to these
268 // from m_rBind to m_stmt->bind and it is later freed by the `if (m_stmt->bind_result_done)` block just above here
269 // MYSQL_STMT lifetime is equal to connection lifetime
270 MySQLBool* m_isNull = new MySQLBool[m_fieldCount];
271 unsigned long* m_length = new unsigned long[m_fieldCount];
272
273 memset(m_isNull, 0, sizeof(MySQLBool) * m_fieldCount);
274 memset(m_rBind, 0, sizeof(MySQLBind) * m_fieldCount);
275 memset(m_length, 0, sizeof(unsigned long) * m_fieldCount);
276
277 //- This is where we store the (entire) resultset
278 if (mysql_stmt_store_result(m_stmt))
279 {
280 LOG_WARN("sql.sql", "{}:mysql_stmt_store_result, cannot bind result from MySQL server. Error: {}", __FUNCTION__, mysql_stmt_error(m_stmt));
281 delete[] m_rBind;
282 delete[] m_isNull;
283 delete[] m_length;
284 return;
285 }
286
287 m_rowCount = mysql_stmt_num_rows(m_stmt);
288
289 //- This is where we prepare the buffer based on metadata
290 MySQLField* field = reinterpret_cast<MySQLField*>(mysql_fetch_fields(m_metadataResult));
292 std::size_t rowSize = 0;
293
294 for (uint32 i = 0; i < m_fieldCount; ++i)
295 {
296 uint32 size = SizeForType(&field[i]);
297 rowSize += size;
298
300
301 m_rBind[i].buffer_type = field[i].type;
302 m_rBind[i].buffer_length = size;
303 m_rBind[i].length = &m_length[i];
304 m_rBind[i].is_null = &m_isNull[i];
305 m_rBind[i].error = nullptr;
306 m_rBind[i].is_unsigned = field[i].flags & UNSIGNED_FLAG;
307 }
308
309 char* dataBuffer = new char[rowSize * m_rowCount];
310 for (uint32 i = 0, offset = 0; i < m_fieldCount; ++i)
311 {
312 m_rBind[i].buffer = dataBuffer + offset;
313 offset += m_rBind[i].buffer_length;
314 }
315
316 //- This is where we bind the bind the buffer to the statement
317 if (mysql_stmt_bind_result(m_stmt, m_rBind))
318 {
319 LOG_WARN("sql.sql", "{}:mysql_stmt_bind_result, cannot bind result from MySQL server. Error: {}", __FUNCTION__, mysql_stmt_error(m_stmt));
320 mysql_stmt_free_result(m_stmt);
321 CleanUp();
322 delete[] m_isNull;
323 delete[] m_length;
324 return;
325 }
326
328
329 while (_NextRow())
330 {
331 for (uint32 fIndex = 0; fIndex < m_fieldCount; ++fIndex)
332 {
333 m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetMetadata(&m_fieldMetadata[fIndex]);
334
335 unsigned long buffer_length = m_rBind[fIndex].buffer_length;
336 unsigned long fetched_length = *m_rBind[fIndex].length;
337 if (!*m_rBind[fIndex].is_null)
338 {
339 void* buffer = m_stmt->bind[fIndex].buffer;
340 switch (m_rBind[fIndex].buffer_type)
341 {
342 case MYSQL_TYPE_TINY_BLOB:
343 case MYSQL_TYPE_MEDIUM_BLOB:
344 case MYSQL_TYPE_LONG_BLOB:
345 case MYSQL_TYPE_BLOB:
346 case MYSQL_TYPE_STRING:
347 case MYSQL_TYPE_VAR_STRING:
348 // warning - the string will not be null-terminated if there is no space for it in the buffer
349 // when mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED
350 // we cannot blindly null-terminate the data either as it may be retrieved as binary blob and not specifically a string
351 // in this case using Field::GetCString will result in garbage
353 if (fetched_length < buffer_length)
354 *((char*)buffer + fetched_length) = '\0';
355 break;
356 default:
357 break;
358 }
359
360 m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue((char const*)buffer, fetched_length);
361
362 // move buffer pointer to next part
363 m_stmt->bind[fIndex].buffer = (char*)buffer + rowSize;
364 }
365 else
366 {
367 m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue(nullptr, *m_rBind[fIndex].length);
368 }
369 }
370
372 }
373
374 m_rowPosition = 0;
375
377 mysql_stmt_free_result(m_stmt);
378}
std::uint32_t uint32
Definition: Define.h:108
#define LOG_WARN(filterType__,...)
Definition: Log.h:163
std::remove_pointer_t< decltype(std::declval< MYSQL_BIND >().is_null)> MySQLBool
Definition: MySQLHacks.h:32
void InitializeDatabaseFieldMetadata(QueryResultFieldMetadata *meta, MySQLField const *field, uint32 fieldIndex)
Definition: QueryResult.cpp:155
static uint32 SizeForType(MYSQL_FIELD *field)
Definition: QueryResult.cpp:27
Definition: MySQLHacks.h:26
Definition: MySQLHacks.h:27
std::vector< QueryResultFieldMetadata > m_fieldMetadata
Definition: QueryResult.h:131
MySQLStmt * m_stmt
Definition: QueryResult.h:139
uint64 m_rowPosition
Definition: QueryResult.h:134
void CleanUp()
Definition: QueryResult.cpp:419
uint64 m_rowCount
Definition: QueryResult.h:133
bool _NextRow()
Definition: QueryResult.cpp:395
MySQLBind * m_rBind
Definition: QueryResult.h:138
uint32 m_fieldCount
Definition: QueryResult.h:135
std::vector< Field > m_rows
Definition: QueryResult.h:132
MySQLResult * m_metadataResult
Field metadata, returned by mysql_stmt_result_metadata.
Definition: QueryResult.h:140

References _NextRow(), CleanUp(), LOG_WARN, m_fieldCount, m_fieldMetadata, m_metadataResult, m_rBind, m_rowCount, m_rowPosition, m_rows, and m_stmt.

◆ ~PreparedResultSet()

PreparedResultSet::~PreparedResultSet ( )
381{
382 CleanUp();
383}

References CleanUp().

◆ PreparedResultSet() [2/2]

PreparedResultSet::PreparedResultSet ( PreparedResultSet const &  right)
privatedelete

Member Function Documentation

◆ _NextRow()

bool PreparedResultSet::_NextRow ( )
private

Only called in low-level code, namely the constructor Will iterate over every row of data and buffer it

396{
400 return false;
401
402 int retval = mysql_stmt_fetch(m_stmt);
403 return retval == 0 || retval == MYSQL_DATA_TRUNCATED;
404}

References m_rowCount, m_rowPosition, and m_stmt.

Referenced by PreparedResultSet().

◆ AssertRows()

void PreparedResultSet::AssertRows ( std::size_t  sizeRows)
private
433{
435 ASSERT(sizeRows == m_fieldCount, "> Tuple size != count fields");
436}
#define ASSERT
Definition: Errors.h:68

References ASSERT, m_fieldCount, m_rowCount, and m_rowPosition.

◆ begin()

auto PreparedResultSet::begin ( )
inline
127{ return ResultIterator<PreparedResultSet>(this); }
Definition: QueryResult.h:29

◆ CleanUp()

void PreparedResultSet::CleanUp ( )
private
420{
422 mysql_free_result(m_metadataResult);
423
424 if (m_rBind)
425 {
426 delete[](char*)m_rBind->buffer;
427 delete[] m_rBind;
428 m_rBind = nullptr;
429 }
430}

References m_metadataResult, and m_rBind.

Referenced by PreparedResultSet(), and ~PreparedResultSet().

◆ end()

static auto PreparedResultSet::end ( )
inlinestatic
128{ return ResultIterator<PreparedResultSet>(nullptr); }

◆ Fetch()

Field * PreparedResultSet::Fetch ( ) const
407{
409 return const_cast<Field*>(&m_rows[uint32(m_rowPosition) * m_fieldCount]);
410}
Class used to access individual fields of database query result.
Definition: Field.h:99

References ASSERT, m_fieldCount, m_rowCount, m_rowPosition, and m_rows.

◆ FetchTuple()

template<typename... Ts>
std::tuple< Ts... > PreparedResultSet::FetchTuple ( )
inline
113 {
114 AssertRows(sizeof...(Ts));
115
116 std::tuple<Ts...> theTuple = {};
117
118 std::apply([this](Ts&... args)
119 {
120 uint8 index{ 0 };
121 ((args = m_rows[uint32(m_rowPosition) * m_fieldCount + index].Get<Ts>(), index++), ...);
122 }, theTuple);
123
124 return theTuple;
125 }
std::uint8_t uint8
Definition: Define.h:110
void AssertRows(std::size_t sizeRows)
Definition: QueryResult.cpp:432

◆ GetFieldCount()

uint32 PreparedResultSet::GetFieldCount ( ) const
inline
106{ return m_fieldCount; }

◆ GetRowCount()

uint64 PreparedResultSet::GetRowCount ( ) const
inline

◆ NextRow()

bool PreparedResultSet::NextRow ( )

Only updates the m_rowPosition so upper level code knows in which element of the rows vector to look

386{
389 if (++m_rowPosition >= m_rowCount)
390 return false;
391
392 return true;
393}

References m_rowCount, and m_rowPosition.

◆ operator=()

PreparedResultSet & PreparedResultSet::operator= ( PreparedResultSet const &  right)
privatedelete

◆ operator[]()

Field const & PreparedResultSet::operator[] ( std::size_t  index) const
413{
415 ASSERT(index < m_fieldCount);
416 return m_rows[uint32(m_rowPosition) * m_fieldCount + index];
417}

References ASSERT, m_fieldCount, m_rowCount, m_rowPosition, and m_rows.

Member Data Documentation

◆ m_fieldCount

uint32 PreparedResultSet::m_fieldCount
protected

◆ m_fieldMetadata

std::vector<QueryResultFieldMetadata> PreparedResultSet::m_fieldMetadata
protected

Referenced by PreparedResultSet().

◆ m_metadataResult

MySQLResult* PreparedResultSet::m_metadataResult
private

Field metadata, returned by mysql_stmt_result_metadata.

Referenced by CleanUp(), and PreparedResultSet().

◆ m_rBind

MySQLBind* PreparedResultSet::m_rBind
private

Referenced by CleanUp(), and PreparedResultSet().

◆ m_rowCount

uint64 PreparedResultSet::m_rowCount
protected

◆ m_rowPosition

uint64 PreparedResultSet::m_rowPosition
protected

◆ m_rows

std::vector<Field> PreparedResultSet::m_rows
protected

◆ m_stmt

MySQLStmt* PreparedResultSet::m_stmt
private

Referenced by _NextRow(), and PreparedResultSet().