276 lines
6.0 KiB
C++
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
|
|
}; |