eq2go/old/common/database/database_result.hpp
2025-08-06 19:00:30 -05:00

249 lines
6.1 KiB
C++

// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License
#pragma once
#include <map>
#include <string>
#include <string_view>
#include <cstdlib>
#include <mysql.h>
#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<std::string_view, uint8> field_map; // Field name to index mapping for fast lookups
};