Browse Source

Core/Database: Optimised Database::StoreResult by using a map of field names to indices instead of a char pointer array. Issue #559

Nellag 1 month ago
parent
commit
dc552fc9f6

+ 7 - 1
EQ2/source/common/DatabaseNew.cpp

@@ -233,7 +233,13 @@ bool DatabaseNew::Select(DatabaseResult *result, const char *query, ...) {
 		res = mysql_store_result(&mysql);
 
 		if (res != NULL)
-			ret = result->StoreResult(res);
+		{
+			// Grab number of rows and number of fields from the query
+			uint8 num_rows = mysql_affected_rows(&mysql);
+			uint8 num_fields = mysql_field_count(&mysql);
+
+			ret = result->StoreResult(res, num_fields, num_rows);
+		}
 		else {
 			LogWrite(DATABASE__ERROR, 0, "Database", "Error storing MySql query result (%d): %s\n%s", mysql_errno(&mysql), mysql_error(&mysql), buf);
 			ret = false;

+ 21 - 47
EQ2/source/common/DatabaseResult.cpp

@@ -28,11 +28,7 @@
 //return this instead of NULL for certain functions to prevent crashes from coding errors
 static const char *empty_str = "";
 
-DatabaseResult::DatabaseResult() {
-	result = NULL;
-	field_names = NULL;
-	num_fields = 0;
-	row = 0;
+DatabaseResult::DatabaseResult(): field_map(), result(0), num_fields(0), row(0) {
 }
 
 DatabaseResult::~DatabaseResult() {
@@ -41,60 +37,40 @@ DatabaseResult::~DatabaseResult() {
 	if (result != NULL)
 		mysql_free_result(result);
 
-	if (field_names != NULL) {
-		for (i = 0; i < num_fields; i++)
-			free(field_names[i]);
-		free(field_names);
+	if (field_map.size()) {
+		field_map.clear();
 	}
 }
 
-bool DatabaseResult::StoreResult(MYSQL_RES *res) {
-	unsigned int i, j;
-	MYSQL_FIELD *field;
+bool DatabaseResult::StoreResult(MYSQL_RES* res, uint8 field_count, uint8 row_count) {
 
 	//clear any previously stored result
 	if (result != NULL)
 		mysql_free_result(result);
 
 	//clear any field names from a previous result
-	if (field_names != NULL) {
-		for (i = 0; i < num_fields; i++)
-			free(field_names[i]);
-		free(field_names);
+	if (field_map.size()) {
+		field_map.clear();
 	}
 
-	//store the new result
 	result = res;
-	num_fields = mysql_num_fields(res);
-	
-	//allocate enough space for each field's name
-	if ((field_names = (char **)malloc(sizeof(char *) * num_fields)) == NULL) {
-		LogWrite(DATABASE__ERROR, 0, "Database Result", "Unable to allocate %u bytes when storing database result and fetching field names\nNumber of fields=%u\n", sizeof(char *) * num_fields, num_fields);
-		mysql_free_result(result);
+	num_rows = row_count;	
+	num_fields = field_count;
+
+	// No rows or fields then we don't care
+	if (!num_rows || !num_fields) {
+		mysql_free_result(res);
 		result = NULL;
-		num_fields = 0;
 		return false;
 	}
 
-	//store each field's name
-	i = 0;
-	while ((field = mysql_fetch_field(result)) != NULL) {
-		if ((field_names[i] = (char *)calloc(field->name_length + 1, sizeof(char))) == NULL) {
-			LogWrite(DATABASE__ERROR, 0, "Database Result", "Unable to allocate %u bytes when storing databse result and copying field name %s\n", field->name_length + 1, field->name);
-			mysql_free_result(result);
-			result = NULL;
-			for (j = 0; j < i; j++)
-				free(field_names[j]);
-			free(field_names);
-			field_names = NULL;
-			num_fields = 0;
-			return false;
-		}
-
-		strncpy(field_names[i], field->name, field->name_length);
-		i++;
-	}
+	
+	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;
 }
 
@@ -108,11 +84,9 @@ const char * DatabaseResult::GetFieldValue(unsigned int index) {
 }
 
 const char * DatabaseResult::GetFieldValueStr(const char *field_name) {
-	unsigned int i;
-
-	for (i = 0; i < num_fields; i++) {
-		if (strncmp(field_name, field_names[i], FIELD_NAME_MAX) == 0)
-			return row[i];
+	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);

+ 6 - 3
EQ2/source/common/DatabaseResult.h

@@ -6,13 +6,14 @@
 #include <WinSock2.h>	//#include <my_global.h> when we/if we go to winsock2 :/
 #endif
 #include <mysql.h>
+#include <map>
 
 class DatabaseResult {
 public:
 	DatabaseResult();
 	virtual ~DatabaseResult();
 
-	bool StoreResult(MYSQL_RES *res);
+	bool StoreResult(MYSQL_RES* result, uint8 field_count, uint8 row_count);
 	bool Next();
 
 	bool IsNull(unsigned int index);
@@ -40,15 +41,17 @@ public:
 	const char * GetString(unsigned int index);
 	const char * GetStringStr(const char *field_name);
 
-	unsigned int GetNumRows() {return result == NULL ? 0 : (unsigned int)mysql_num_rows(result);}
+	const unsigned int GetNumRows() { return num_rows; }
 	
 	const char * GetFieldValue(unsigned int index);
 	const char * GetFieldValueStr(const char *field_name);
 private:
 	MYSQL_RES *result;
 	MYSQL_ROW row;
-	char **field_names;
+	unsigned int num_rows;
 	unsigned int num_fields;
+
+	std::map<std::string_view,uint8> field_map;
 };
 
 #endif