2025-08-06 19:00:30 -05:00

276 lines
6.0 KiB
C++

// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License
#pragma once
#include <string>
#include <vector>
#include <cstring>
#include <cstdarg>
#include <mysql.h>
#include <errmsg.h>
#include "../types.hpp"
#include "../misc_functions.hpp"
using namespace std;
class Database;
class DatabaseCore;
#ifdef WORLD
extern class WorldDatabase database;
#endif
#ifdef LOGIN
extern class LoginDatabase database;
#endif
#ifdef PARSER
extern class ParserDatabase database;
#endif
#ifdef PATCHER
extern class PatcherDatabase database;
#endif
// Simplified query class for executing SQL statements with result handling
class Query
{
public:
// Default constructor - initializes all members to safe defaults
Query()
{
result = 0;
affected_rows = 0;
last_insert_id = 0;
errnum = 0;
row = 0;
retry = true;
multiple_results = 0;
memset(errbuf, 0, sizeof(errbuf));
queryID = 0;
}
// Copy constructor for async query creation
Query(Query* queryPtr, int32 in_id)
{
result = 0;
affected_rows = 0;
last_insert_id = 0;
errnum = 0;
row = 0;
retry = true;
multiple_results = 0;
memset(errbuf, 0, sizeof(errbuf));
query = string(queryPtr->GetQuery());
in_type = queryPtr->GetQueryType();
queryID = in_id;
}
// Destructor - frees MySQL results and allocated memory
~Query()
{
if (result)
mysql_free_result(result);
result = 0;
safe_delete(affected_rows);
safe_delete(last_insert_id);
if (multiple_results) {
for (auto& res : *multiple_results) {
mysql_free_result(res);
}
safe_delete(multiple_results);
}
}
// Get the last inserted auto-increment ID
int32 GetLastInsertedID()
{
return last_insert_id ? *last_insert_id : 0;
}
// Get number of rows affected by last query
int32 GetAffectedRows()
{
return affected_rows ? *affected_rows : 0;
}
// Get MySQL result set from last query
MYSQL_RES* GetResult()
{
return result;
}
// Get error message from last failed query
char* GetError()
{
return errbuf;
}
// Get MySQL error number from last failed query
int32 GetErrorNumber()
{
return errnum;
}
// Get the SQL query string
const char* GetQuery()
{
return query.c_str();
}
// Get the type of this query (SELECT, INSERT, etc.)
QUERY_TYPE GetQueryType()
{
return in_type;
}
// Get unique identifier for this query
int32 GetQueryID()
{
return queryID;
}
// Get specific field value from current row
char* GetField(int8 field_num)
{
if (!row && result)
*row = mysql_fetch_row(result);
if (row && result && field_num < mysql_num_fields(result))
return *row[field_num];
else
return NULL;
}
// Advance to next row in result set
void NextRow()
{
if (result)
*row = mysql_fetch_row(result);
}
// Execute query with specified string and type
MYSQL_RES* RunQuery2(string in_query, QUERY_TYPE type)
{
switch (type) {
case Q_SELECT:
break;
case Q_DBMS:
case Q_REPLACE:
case Q_DELETE:
case Q_UPDATE:
safe_delete(affected_rows);
affected_rows = new int32;
break;
case Q_INSERT:
safe_delete(last_insert_id);
last_insert_id = new int32;
}
if (result) {
if (!multiple_results)
multiple_results = new vector<MYSQL_RES*>();
multiple_results->push_back(result);
}
query = in_query;
#if defined WORLD && defined _DEBUG
if (type == Q_UPDATE || type == Q_INSERT || type == Q_DELETE || type == Q_REPLACE) {
char* filteredTables[] = { " characters", " character_", " `character_", " statistics", " variables", " char_colors", " `guild", " bugs" };
bool match = false;
for (int i = 0; i < sizeof(filteredTables) / sizeof(filteredTables[0]); i++) {
if (query.find(filteredTables[i]) != std::string::npos) {
match = true;
break;
}
}
try {
if (!match) {
FILE* pFile;
pFile = fopen("sql_updates.sql", "a+");
fwrite(query.c_str(), 1, query.length(), pFile);
fwrite(";", sizeof(char), 1, pFile);
fwrite("\n", sizeof(char), 1, pFile);
fclose(pFile);
}
}
catch (...) {}
}
#endif // WORLD && _DEBUG
database.RunQuery(query.c_str(), query.length(), errbuf, &result, affected_rows, last_insert_id, &errnum, retry);
return result;
}
// Execute formatted query with variable arguments
MYSQL_RES* RunQuery2(QUERY_TYPE type, const char* format, ...)
{
va_list args;
va_start(args, format);
char* buffer;
int buf_len;
va_list argcopy;
va_copy(argcopy, args);
buf_len = vsnprintf(NULL, 0, format, argcopy) + 1;
va_end(argcopy);
buffer = new char[buf_len];
vsnprintf(buffer, buf_len, format, args);
va_end(args);
query = string(buffer);
safe_delete_array(buffer);
return RunQuery2(query.c_str(), type);
}
#ifdef WORLD
// Add formatted query to async processing queue
void AddQueryAsync(int32 queryID, Database* db, QUERY_TYPE type, const char* format, ...)
{
in_type = type;
va_list args;
va_start(args, format);
char* buffer;
int buf_len;
va_list argcopy;
va_copy(argcopy, args);
buf_len = vsnprintf(NULL, 0, format, argcopy) + 1;
va_end(argcopy);
buffer = new char[buf_len];
vsnprintf(buffer, buf_len, format, args);
va_end(args);
query = string(buffer);
Query* asyncQuery = new Query(this, queryID);
safe_delete_array(buffer);
db->AddAsyncQuery(asyncQuery);
}
// Execute this query asynchronously using provided database instance
void RunQueryAsync(Database* db)
{
db->RunQuery(query.c_str(), query.length(), errbuf, &result, affected_rows, last_insert_id, &errnum, retry);
}
#endif
private:
string query; // SQL query string
char errbuf[MYSQL_ERRMSG_SIZE]; // Error message buffer
MYSQL_RES* result; // MySQL result set
vector<MYSQL_RES*>* multiple_results; // Multiple result sets for complex queries
int32* affected_rows; // Number of rows affected
int32* last_insert_id; // Last auto-increment ID
int32 errnum; // MySQL error number
QUERY_TYPE in_type; // Type of query being executed
bool retry; // Whether to retry failed queries
MYSQL_ROW* row; // Current row data
int32 queryID; // Unique query identifier
};