diff options
Diffstat (limited to 'src/server/database/Database/QueryResult.cpp')
| -rw-r--r-- | src/server/database/Database/QueryResult.cpp | 452 |
1 files changed, 232 insertions, 220 deletions
diff --git a/src/server/database/Database/QueryResult.cpp b/src/server/database/Database/QueryResult.cpp index 15aabbe7e1..06005a34d2 100644 --- a/src/server/database/Database/QueryResult.cpp +++ b/src/server/database/Database/QueryResult.cpp @@ -24,168 +24,234 @@ namespace { -static uint32 SizeForType(MYSQL_FIELD* field) -{ - switch (field->type) + static uint32 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: + LOG_WARN("sql.sql", "SQL::SizeForType(): invalid field type {}", uint32(field->type)); + return 0; + } + } + + DatabaseFieldTypes MysqlTypeToFieldType(enum_field_types 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: - LOG_WARN("sql.sql", "SQL::SizeForType(): invalid field type {}", uint32(field->type)); - return 0; + switch (type) + { + case MYSQL_TYPE_NULL: + return DatabaseFieldTypes::Null; + case MYSQL_TYPE_TINY: + return DatabaseFieldTypes::Int8; + case MYSQL_TYPE_YEAR: + case MYSQL_TYPE_SHORT: + return DatabaseFieldTypes::Int16; + case MYSQL_TYPE_INT24: + case MYSQL_TYPE_LONG: + return DatabaseFieldTypes::Int32; + case MYSQL_TYPE_LONGLONG: + case MYSQL_TYPE_BIT: + return DatabaseFieldTypes::Int64; + case MYSQL_TYPE_FLOAT: + return DatabaseFieldTypes::Float; + case MYSQL_TYPE_DOUBLE: + return DatabaseFieldTypes::Double; + case MYSQL_TYPE_DECIMAL: + case MYSQL_TYPE_NEWDECIMAL: + return DatabaseFieldTypes::Decimal; + case MYSQL_TYPE_TIMESTAMP: + case MYSQL_TYPE_DATE: + case MYSQL_TYPE_TIME: + case MYSQL_TYPE_DATETIME: + return DatabaseFieldTypes::Date; + 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 DatabaseFieldTypes::Binary; + default: + LOG_WARN("sql.sql", "MysqlTypeToFieldType(): invalid field type {}", uint32(type)); + break; + } + + return DatabaseFieldTypes::Null; + } + + static std::string FieldTypeToString(enum_field_types type) + { + switch (type) + { + case MYSQL_TYPE_BIT: return "BIT"; + case MYSQL_TYPE_BLOB: return "BLOB"; + case MYSQL_TYPE_DATE: return "DATE"; + case MYSQL_TYPE_DATETIME: return "DATETIME"; + case MYSQL_TYPE_NEWDECIMAL: return "NEWDECIMAL"; + case MYSQL_TYPE_DECIMAL: return "DECIMAL"; + case MYSQL_TYPE_DOUBLE: return "DOUBLE"; + case MYSQL_TYPE_ENUM: return "ENUM"; + case MYSQL_TYPE_FLOAT: return "FLOAT"; + case MYSQL_TYPE_GEOMETRY: return "GEOMETRY"; + case MYSQL_TYPE_INT24: return "INT24"; + case MYSQL_TYPE_LONG: return "LONG"; + case MYSQL_TYPE_LONGLONG: return "LONGLONG"; + case MYSQL_TYPE_LONG_BLOB: return "LONG_BLOB"; + case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB"; + case MYSQL_TYPE_NEWDATE: return "NEWDATE"; + case MYSQL_TYPE_NULL: return "NULL"; + case MYSQL_TYPE_SET: return "SET"; + case MYSQL_TYPE_SHORT: return "SHORT"; + case MYSQL_TYPE_STRING: return "STRING"; + case MYSQL_TYPE_TIME: return "TIME"; + case MYSQL_TYPE_TIMESTAMP: return "TIMESTAMP"; + case MYSQL_TYPE_TINY: return "TINY"; + case MYSQL_TYPE_TINY_BLOB: return "TINY_BLOB"; + case MYSQL_TYPE_VAR_STRING: return "VAR_STRING"; + case MYSQL_TYPE_YEAR: return "YEAR"; + default: return "-Unknown-"; + } + } + + void InitializeDatabaseFieldMetadata(QueryResultFieldMetadata* meta, MySQLField const* field, uint32 fieldIndex) + { + meta->TableName = field->org_table; + meta->TableAlias = field->table; + meta->Name = field->org_name; + meta->Alias = field->name; + meta->TypeName = FieldTypeToString(field->type); + meta->Index = fieldIndex; + meta->Type = MysqlTypeToFieldType(field->type); } } -DatabaseFieldTypes MysqlTypeToFieldType(enum_field_types type) +ResultSet::ResultSet(MySQLResult* result, MySQLField* fields, uint64 rowCount, uint32 fieldCount) : + _rowCount(rowCount), + _fieldCount(fieldCount), + _result(result), + _fields(fields) { - switch (type) + _fieldMetadata.resize(_fieldCount); + _currentRow = new Field[_fieldCount]; + + for (uint32 i = 0; i < _fieldCount; i++) { - case MYSQL_TYPE_NULL: - return DatabaseFieldTypes::Null; - case MYSQL_TYPE_TINY: - return DatabaseFieldTypes::Int8; - case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_SHORT: - return DatabaseFieldTypes::Int16; - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_LONG: - return DatabaseFieldTypes::Int32; - case MYSQL_TYPE_LONGLONG: - case MYSQL_TYPE_BIT: - return DatabaseFieldTypes::Int64; - case MYSQL_TYPE_FLOAT: - return DatabaseFieldTypes::Float; - case MYSQL_TYPE_DOUBLE: - return DatabaseFieldTypes::Double; - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_NEWDECIMAL: - return DatabaseFieldTypes::Decimal; - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_DATETIME: - return DatabaseFieldTypes::Date; - 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 DatabaseFieldTypes::Binary; - default: - LOG_WARN("sql.sql", "MysqlTypeToFieldType(): invalid field type {}", uint32(type)); - break; + InitializeDatabaseFieldMetadata(&_fieldMetadata[i], &_fields[i], i); + _currentRow[i].SetMetadata(&_fieldMetadata[i]); } +} - return DatabaseFieldTypes::Null; +ResultSet::~ResultSet() +{ + CleanUp(); } -static char const* FieldTypeToString(enum_field_types type) +bool ResultSet::NextRow() { - switch (type) + MYSQL_ROW row; + + if (!_result) + return false; + + row = mysql_fetch_row(_result); + if (!row) { - case MYSQL_TYPE_BIT: return "BIT"; - case MYSQL_TYPE_BLOB: return "BLOB"; - case MYSQL_TYPE_DATE: return "DATE"; - case MYSQL_TYPE_DATETIME: return "DATETIME"; - case MYSQL_TYPE_NEWDECIMAL: return "NEWDECIMAL"; - case MYSQL_TYPE_DECIMAL: return "DECIMAL"; - case MYSQL_TYPE_DOUBLE: return "DOUBLE"; - case MYSQL_TYPE_ENUM: return "ENUM"; - case MYSQL_TYPE_FLOAT: return "FLOAT"; - case MYSQL_TYPE_GEOMETRY: return "GEOMETRY"; - case MYSQL_TYPE_INT24: return "INT24"; - case MYSQL_TYPE_LONG: return "LONG"; - case MYSQL_TYPE_LONGLONG: return "LONGLONG"; - case MYSQL_TYPE_LONG_BLOB: return "LONG_BLOB"; - case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB"; - case MYSQL_TYPE_NEWDATE: return "NEWDATE"; - case MYSQL_TYPE_NULL: return "NULL"; - case MYSQL_TYPE_SET: return "SET"; - case MYSQL_TYPE_SHORT: return "SHORT"; - case MYSQL_TYPE_STRING: return "STRING"; - case MYSQL_TYPE_TIME: return "TIME"; - case MYSQL_TYPE_TIMESTAMP: return "TIMESTAMP"; - case MYSQL_TYPE_TINY: return "TINY"; - case MYSQL_TYPE_TINY_BLOB: return "TINY_BLOB"; - case MYSQL_TYPE_VAR_STRING: return "VAR_STRING"; - case MYSQL_TYPE_YEAR: return "YEAR"; - default: return "-Unknown-"; + CleanUp(); + return false; + } + + unsigned long* lengths = mysql_fetch_lengths(_result); + if (!lengths) + { + LOG_WARN("sql.sql", "{}:mysql_fetch_lengths, cannot retrieve value lengths. Error {}.", __FUNCTION__, mysql_error(_result->handle)); + CleanUp(); + return false; } + + for (uint32 i = 0; i < _fieldCount; i++) + _currentRow[i].SetStructuredValue(row[i], lengths[i]); + + return true; } -void InitializeDatabaseFieldMetadata(QueryResultFieldMetadata* meta, MySQLField const* field, uint32 fieldIndex) +std::string ResultSet::GetFieldName(uint32 index) const { - meta->TableName = field->org_table; - meta->TableAlias = field->table; - meta->Name = field->org_name; - meta->Alias = field->name; - meta->TypeName = FieldTypeToString(field->type); - meta->Index = fieldIndex; - meta->Type = MysqlTypeToFieldType(field->type); -} + ASSERT(index < _fieldCount); + return _fields[index].name; } -ResultSet::ResultSet(MySQLResult* result, MySQLField* fields, uint64 rowCount, uint32 fieldCount) : -_rowCount(rowCount), -_fieldCount(fieldCount), -_result(result), -_fields(fields) +void ResultSet::CleanUp() { - _fieldMetadata.resize(_fieldCount); - _currentRow = new Field[_fieldCount]; - for (uint32 i = 0; i < _fieldCount; i++) + if (_currentRow) { - InitializeDatabaseFieldMetadata(&_fieldMetadata[i], &_fields[i], i); - _currentRow[i].SetMetadata(&_fieldMetadata[i]); + delete[] _currentRow; + _currentRow = nullptr; } + + if (_result) + { + mysql_free_result(_result); + _result = nullptr; + } +} + +Field const& ResultSet::operator[](std::size_t index) const +{ + ASSERT(index < _fieldCount); + return _currentRow[index]; +} + +void ResultSet::AssertRows(std::size_t sizeRows) +{ + ASSERT(sizeRows == _fieldCount); } PreparedResultSet::PreparedResultSet(MySQLStmt* stmt, MySQLResult* result, uint64 rowCount, uint32 fieldCount) : -m_rowCount(rowCount), -m_rowPosition(0), -m_fieldCount(fieldCount), -m_rBind(nullptr), -m_stmt(stmt), -m_metadataResult(result) + m_rowCount(rowCount), + m_rowPosition(0), + m_fieldCount(fieldCount), + m_rBind(nullptr), + m_stmt(stmt), + m_metadataResult(result) { if (!m_metadataResult) return; @@ -224,6 +290,7 @@ m_metadataResult(result) MySQLField* field = reinterpret_cast<MySQLField*>(mysql_fetch_fields(m_metadataResult)); m_fieldMetadata.resize(m_fieldCount); std::size_t rowSize = 0; + for (uint32 i = 0; i < m_fieldCount; ++i) { uint32 size = SizeForType(&field[i]); @@ -258,6 +325,7 @@ m_metadataResult(result) } m_rows.resize(uint32(m_rowCount) * m_fieldCount); + while (_NextRow()) { for (uint32 fIndex = 0; fIndex < m_fieldCount; ++fIndex) @@ -271,90 +339,49 @@ m_metadataResult(result) void* buffer = m_stmt->bind[fIndex].buffer; switch (m_rBind[fIndex].buffer_type) { - 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: - // warning - the string will not be null-terminated if there is no space for it in the buffer - // when mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED - // we cannot blindly null-terminate the data either as it may be retrieved as binary blob and not specifically a string - // in this case using Field::GetCString will result in garbage - // TODO: remove Field::GetCString and use std::string_view in C++17 - if (fetched_length < buffer_length) - *((char*)buffer + fetched_length) = '\0'; - break; - default: - break; + 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: + // warning - the string will not be null-terminated if there is no space for it in the buffer + // when mysql_stmt_fetch returned MYSQL_DATA_TRUNCATED + // we cannot blindly null-terminate the data either as it may be retrieved as binary blob and not specifically a string + // in this case using Field::GetCString will result in garbage + // TODO: remove Field::GetCString and use std::string_view in C++17 + if (fetched_length < buffer_length) + *((char*)buffer + fetched_length) = '\0'; + break; + default: + break; } - m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue( - (char const*)buffer, - fetched_length); + m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue((char const*)buffer, fetched_length); // move buffer pointer to next part m_stmt->bind[fIndex].buffer = (char*)buffer + rowSize; } else { - m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue( - nullptr, - *m_rBind[fIndex].length); + m_rows[uint32(m_rowPosition) * m_fieldCount + fIndex].SetByteValue(nullptr, *m_rBind[fIndex].length); } } + m_rowPosition++; } + m_rowPosition = 0; /// All data is buffered, let go of mysql c api structures mysql_stmt_free_result(m_stmt); } -ResultSet::~ResultSet() -{ - CleanUp(); -} - PreparedResultSet::~PreparedResultSet() { CleanUp(); } -bool ResultSet::NextRow() -{ - MYSQL_ROW row; - - if (!_result) - return false; - - row = mysql_fetch_row(_result); - if (!row) - { - CleanUp(); - return false; - } - - unsigned long* lengths = mysql_fetch_lengths(_result); - if (!lengths) - { - LOG_WARN("sql.sql", "{}:mysql_fetch_lengths, cannot retrieve value lengths. Error {}.", __FUNCTION__, mysql_error(_result->handle)); - CleanUp(); - return false; - } - - for (uint32 i = 0; i < _fieldCount; i++) - _currentRow[i].SetStructuredValue(row[i], lengths[i]); - - return true; -} - -std::string ResultSet::GetFieldName(uint32 index) const -{ - ASSERT(index < _fieldCount); - return _fields[index].name; -} - bool PreparedResultSet::NextRow() { /// Only updates the m_rowPosition so upper level code knows in which element @@ -376,27 +403,6 @@ bool PreparedResultSet::_NextRow() return retval == 0 || retval == MYSQL_DATA_TRUNCATED; } -void ResultSet::CleanUp() -{ - if (_currentRow) - { - delete [] _currentRow; - _currentRow = nullptr; - } - - if (_result) - { - mysql_free_result(_result); - _result = nullptr; - } -} - -Field const& ResultSet::operator[](std::size_t index) const -{ - ASSERT(index < _fieldCount); - return _currentRow[index]; -} - Field* PreparedResultSet::Fetch() const { ASSERT(m_rowPosition < m_rowCount); @@ -422,3 +428,9 @@ void PreparedResultSet::CleanUp() m_rBind = nullptr; } } + +void PreparedResultSet::AssertRows(std::size_t sizeRows) +{ + ASSERT(m_rowPosition < m_rowCount); + ASSERT(sizeRows == m_fieldCount, "> Tuple size != count fields"); +} |
