// Copyright (C) 2007 EQ2EMulator Development Team - GPL v3 License #pragma once #include #include #include #include #include #include #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(); 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* 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 };