// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License #pragma once #include #include #include #include #include #include "../types.hpp" #include "../log.hpp" using namespace std; // Return this instead of NULL for string functions to prevent crashes static const char* empty_str = ""; // Database result wrapper class for MySQL query results with type-safe accessors class DatabaseResult { public: // Constructor - initializes empty result set DatabaseResult() : field_map(), result(0), num_fields(0), row(0) { } // Destructor - frees MySQL result and clears field mappings virtual ~DatabaseResult() { if (result != NULL) mysql_free_result(result); if (field_map.size()) { field_map.clear(); } } // Store MySQL result set and build field name to index mapping bool StoreResult(MYSQL_RES* res, uint8 field_count, uint8 row_count) { if (result != NULL) mysql_free_result(result); if (field_map.size()) { field_map.clear(); } result = res; num_rows = row_count; num_fields = field_count; if (!num_rows || !num_fields) { mysql_free_result(res); result = NULL; return false; } const MYSQL_FIELD* fields = mysql_fetch_fields(result); for (uint8 i = 0; i < num_fields; ++i) { field_map.emplace(std::make_pair(std::string_view(fields[i].name), i)); } return true; } // Advance to next row in result set bool Next() { return (result != NULL && (row = mysql_fetch_row(result)) != NULL); } // Get total number of rows in result set const unsigned int GetNumRows() { return num_rows; } // Check if field value at index is NULL bool IsNull(unsigned int index) { const char* value = GetFieldValue(index); return value == NULL; } // Check if field value by name is NULL bool IsNullStr(const char* field_name) { const char* value = GetFieldValueStr(field_name); return value == NULL; } // Type-safe getters by index int8 GetInt8(unsigned int index) { const char* value = GetFieldValue(index); return value == NULL ? 0 : atoi(value); } sint8 GetSInt8(unsigned int index) { const char* value = GetFieldValue(index); return value == NULL ? 0 : atoi(value); } int16 GetInt16(unsigned int index) { const char* value = GetFieldValue(index); return value == NULL ? 0 : atoi(value); } sint16 GetSInt16(unsigned int index) { const char* value = GetFieldValue(index); return value == NULL ? 0 : atoi(value); } int32 GetInt32(unsigned int index) { const char* value = GetFieldValue(index); return value == NULL ? 0U : strtoul(value, NULL, 10); } sint32 GetSInt32(unsigned int index) { const char* value = GetFieldValue(index); return value == NULL ? 0 : atoi(value); } uint64 GetInt64(unsigned int index) { const char* value = GetFieldValue(index); return value == NULL ? 0UL : strtoull(value, NULL, 10); } sint64 GetSInt64(unsigned int index) { const char* value = GetFieldValue(index); return value == NULL ? 0L : strtoll(value, NULL, 10); } float GetFloat(unsigned int index) { const char* value = GetFieldValue(index); return value == NULL ? 0.0F : atof(value); } char GetChar(unsigned int index) { const char* value = GetFieldValue(index); return value == NULL ? '\0' : value[0]; } const char* GetString(unsigned int index) { const char* value = GetFieldValue(index); return value == NULL ? empty_str : value; } // Type-safe getters by field name int8 GetInt8Str(const char* field_name) { const char* value = GetFieldValueStr(field_name); return value == NULL ? 0 : atoi(value); } sint8 GetSInt8Str(const char* field_name) { const char* value = GetFieldValueStr(field_name); return value == NULL ? 0 : atoi(value); } int16 GetInt16Str(const char* field_name) { const char* value = GetFieldValueStr(field_name); return value == NULL ? 0 : atoi(value); } sint16 GetSInt16Str(const char* field_name) { const char* value = GetFieldValueStr(field_name); return value == NULL ? 0 : atoi(value); } int32 GetInt32Str(const char* field_name) { const char* value = GetFieldValueStr(field_name); return value == NULL ? 0U : strtoul(value, NULL, 10); } sint32 GetSInt32Str(const char* field_name) { const char* value = GetFieldValueStr(field_name); return value == NULL ? 0 : atoi(value); } uint64 GetInt64Str(const char* field_name) { const char* value = GetFieldValueStr(field_name); return value == NULL ? 0UL : strtoull(value, NULL, 10); } sint64 GetSInt64Str(const char* field_name) { const char* value = GetFieldValueStr(field_name); return value == NULL ? 0L : strtoll(value, NULL, 10); } float GetFloatStr(const char* field_name) { const char* value = GetFieldValueStr(field_name); return value == NULL ? 0.0F : atof(value); } char GetCharStr(const char* field_name) { const char* value = GetFieldValueStr(field_name); return value == NULL ? '\0' : value[0]; } const char* GetStringStr(const char* field_name) { const char* value = GetFieldValueStr(field_name); return value == NULL ? empty_str : value; } // Get raw field value by index with bounds checking const char* GetFieldValue(unsigned int index) { if (index >= num_fields) { LogWrite(DATABASE__ERROR, 0, "Database Result", "Attempt to access field at index %u but there %s only %u field%s", index, num_fields == 1 ? "is" : "are", num_fields, num_fields == 1 ? "" : "s"); return NULL; } return row[index]; } // Get raw field value by name with field mapping lookup const char* GetFieldValueStr(const char* field_name) { const auto& map_iterator = field_map.find(std::string_view(field_name)); if (map_iterator != field_map.end()) { return row[map_iterator->second]; } LogWrite(DATABASE__ERROR, 0, "Database Result", "Unknown field name '%s'", field_name); return NULL; } private: MYSQL_RES* result; // MySQL result set from query MYSQL_ROW row; // Current row data unsigned int num_rows; // Total number of rows in result unsigned int num_fields; // Total number of fields per row std::map field_map; // Field name to index mapping for fast lookups };