DatabaseNew.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  1. /*
  2. EQ2Emulator: Everquest II Server Emulator
  3. Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
  4. This file is part of EQ2Emulator.
  5. EQ2Emulator is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. EQ2Emulator is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <string.h>
  17. #include <stdlib.h>
  18. #include <stdarg.h>
  19. #include <string.h>
  20. #include "Log.h"
  21. #include "DatabaseNew.h"
  22. #include <errmsg.h>
  23. //increase this if large queries are being run frequently to make less calls to malloc()
  24. #define QUERY_INITIAL_SIZE 512
  25. #if defined WORLD
  26. #define DB_INI "world_db.ini"
  27. #elif defined LOGIN
  28. #define DB_INI "login_db.ini"
  29. #elif defined PARSER
  30. #define DB_INI "parser_db.ini"
  31. #endif
  32. DatabaseNew::DatabaseNew() {
  33. mysql_init(&mysql);
  34. MMysql.SetName("DatabaseNew::mysql");
  35. }
  36. DatabaseNew::~DatabaseNew() {
  37. mysql_close(&mysql);
  38. #if MYSQL_VERSION_ID >= 50003
  39. mysql_library_end();
  40. #else
  41. mysql_server_end();
  42. #endif
  43. }
  44. bool DatabaseNew::Connect() {
  45. char line[256], *key, *val;
  46. char host[256], user[64], password[64], database[64];
  47. bool found_section = false;
  48. FILE *f;
  49. if ((f = fopen(DB_INI, "r")) == NULL) {
  50. LogWrite(DATABASE__ERROR, 0, "Database", "Unable to read %s\n", DB_INI);
  51. return false;
  52. }
  53. memset(host, 0, sizeof(host));
  54. memset(user, 0, sizeof(user));
  55. memset(password, 0, sizeof(password));
  56. memset(database, 0, sizeof(database));
  57. while (fgets(line, sizeof(line), f) != NULL) {
  58. if (line[0] == '#' || line[0] == '\n' || line[0] == '\r')
  59. continue;
  60. if (!found_section) {
  61. if (strncasecmp(line, "[Database]", 10) == 0)
  62. found_section = true;
  63. }
  64. else {
  65. if ((key = strtok(line, "=")) != NULL) {
  66. if ((val = strtok(NULL, "\r\n")) != NULL) {
  67. if (strncasecmp(line, "host", 4) == 0)
  68. strncpy(host, val, sizeof(host) - 1);
  69. else if (strncasecmp(line, "user", 4) == 0)
  70. strncpy(user, val, sizeof(user) - 1);
  71. else if (strncasecmp(line, "password", 8) == 0)
  72. strncpy(password, val, sizeof(password) - 1);
  73. else if (strncasecmp(line, "database", 8) == 0)
  74. strncpy(database, val, sizeof(database) - 1);
  75. }
  76. }
  77. }
  78. }
  79. fclose(f);
  80. if (host[0] == '\0') {
  81. LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'host' in '%s'\n", DB_INI);
  82. return false;
  83. }
  84. if (user[0] == '\0') {
  85. LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'user' in '%s'\n", DB_INI);
  86. return false;
  87. }
  88. if (password[0] == '\0') {
  89. LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'password' in '%s'\n", DB_INI);
  90. return false;
  91. }
  92. if (database[0] == '\0') {
  93. LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'database' in '%s'\n", DB_INI);
  94. return false;
  95. }
  96. return Connect(host, user, password, database);
  97. }
  98. bool DatabaseNew::Connect(const char *host, const char *user, const char *password, const char *database) {
  99. return Connect(host, user, password, database, 3306);
  100. }
  101. bool DatabaseNew::Connect(const char *host, const char *user, const char *password, const char *database, unsigned int port) {
  102. if (mysql_real_connect(&mysql, host, user, password, database, port, NULL, 0) == NULL) {
  103. LogWrite(DATABASE__ERROR, 0, "Database", "Unable to connect to MySQL server at %s:%u: %s\n", host, port, mysql_error(&mysql));
  104. return false;
  105. }
  106. return true;
  107. }
  108. bool DatabaseNew::Query(const char *query, ...) {
  109. char *buf;
  110. size_t size = QUERY_INITIAL_SIZE;
  111. int num_chars;
  112. va_list args;
  113. bool ret = true;
  114. MMysql.writelock(__FUNCTION__, __LINE__);
  115. while (true) {
  116. if ((buf = (char *)malloc(size)) == NULL) {
  117. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate database query of %u bytes\n", size);
  118. MMysql.releasewritelock(__FUNCTION__, __LINE__);
  119. return false;
  120. }
  121. va_start(args, query);
  122. num_chars = vsnprintf(buf, size, query, args);
  123. va_end(args);
  124. if (num_chars > -1 && (size_t)num_chars < size)
  125. break;
  126. if (num_chars > -1)
  127. size = num_chars + 1;
  128. else
  129. size *= 2;
  130. free(buf);
  131. }
  132. if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) {
  133. if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) {
  134. LogWrite(DATABASE__ERROR, 0, "Database", "Lost connection, attempting to recover and retry query...");
  135. mysql_close(&mysql);
  136. Connect();
  137. // retry attempt of previous query (1 try and we give up)
  138. if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) {
  139. ret = false;
  140. }
  141. }
  142. else if (!IsIgnoredErrno(mysql_errno(&mysql))) {
  143. LogWrite(DATABASE__ERROR, 0, "Database", "Error %i running MySQL query: %s\n%s\n", mysql_errno(&mysql), mysql_error(&mysql), buf);
  144. ret = false;
  145. }
  146. }
  147. free(buf);
  148. MMysql.releasewritelock(__FUNCTION__, __LINE__);
  149. return ret;
  150. }
  151. bool DatabaseNew::Select(DatabaseResult *result, const char *query, ...) {
  152. char *buf;
  153. size_t size = QUERY_INITIAL_SIZE;
  154. int num_chars;
  155. va_list args;
  156. MYSQL_RES *res;
  157. bool ret = true;
  158. MMysql.writelock(__FUNCTION__, __LINE__);
  159. while (true) {
  160. if ((buf = (char *)malloc(size)) == NULL) {
  161. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate database query of %u bytes\n", size);
  162. MMysql.releasewritelock(__FUNCTION__, __LINE__);
  163. return false;
  164. }
  165. va_start(args, query);
  166. num_chars = vsnprintf(buf, size, query, args);
  167. va_end(args);
  168. if (num_chars > -1 && (size_t)num_chars < size)
  169. break;
  170. if (num_chars > -1)
  171. size = num_chars + 1;
  172. else
  173. size *= 2;
  174. free(buf);
  175. }
  176. if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) {
  177. if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) {
  178. LogWrite(DATABASE__ERROR, 0, "Database", "Lost connection, attempting to recover and retry query...");
  179. mysql_close(&mysql);
  180. Connect();
  181. // retry attempt of previous query (1 try and we give up)
  182. if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) {
  183. ret = false;
  184. }
  185. }
  186. else if (!IsIgnoredErrno(mysql_errno(&mysql))) {
  187. LogWrite(DATABASE__ERROR, 0, "Database", "Error %i running MySQL query: %s\n%s\n", mysql_errno(&mysql), mysql_error(&mysql), buf);
  188. ret = false;
  189. }
  190. }
  191. if (ret && !IsIgnoredErrno(mysql_errno(&mysql))) {
  192. res = mysql_store_result(&mysql);
  193. if (res != NULL)
  194. ret = result->StoreResult(res);
  195. else {
  196. LogWrite(DATABASE__ERROR, 0, "Database", "Error storing MySql query result (%d): %s\n%s", mysql_errno(&mysql), mysql_error(&mysql), buf);
  197. ret = false;
  198. }
  199. }
  200. free(buf);
  201. MMysql.releasewritelock(__FUNCTION__, __LINE__);
  202. return ret;
  203. }
  204. int32 DatabaseNew::LastInsertID()
  205. {
  206. return (int32)mysql_insert_id(&mysql);
  207. }
  208. long DatabaseNew::AffectedRows()
  209. {
  210. return mysql_affected_rows(&mysql);
  211. }
  212. char * DatabaseNew::Escape(const char *str, size_t len) {
  213. char *buf = (char *)malloc(len * 2 + 1);
  214. if (buf == NULL) {
  215. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", len * 2 + 1, __FUNCTION__, __LINE__);
  216. return NULL;
  217. }
  218. mysql_real_escape_string(&mysql, buf, str, len);
  219. return buf;
  220. }
  221. char * DatabaseNew::Escape(const char *str) {
  222. return Escape(str, strlen(str));
  223. }
  224. string DatabaseNew::EscapeStr(const char *str, size_t len) {
  225. char *buf = (char *)malloc(len * 2 + 1);
  226. string ret;
  227. if (buf == NULL) {
  228. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", len * 2 + 1, __FUNCTION__, __LINE__);
  229. return NULL;
  230. }
  231. mysql_real_escape_string(&mysql, buf, str, len);
  232. ret.append(buf);
  233. free(buf);
  234. return ret;
  235. }
  236. string DatabaseNew::EscapeStr(const char *str) {
  237. return EscapeStr(str, strlen(str));
  238. }
  239. string DatabaseNew::EscapeStr(string str) {
  240. return EscapeStr(str.c_str(), str.length());
  241. }
  242. bool DatabaseNew::QueriesFromFile(const char * file) {
  243. bool success = true;
  244. long size;
  245. char *buf;
  246. int ret;
  247. MYSQL_RES *res;
  248. FILE *f;
  249. f = fopen(file, "rb");
  250. if (f == NULL) {
  251. LogWrite(DATABASE__ERROR, 0, "Database", "Unable to open '%s' for reading: %s", file, strerror(errno));
  252. return false;
  253. }
  254. fseek(f, 0, SEEK_END);
  255. size = ftell(f);
  256. fseek(f, 0, SEEK_SET);
  257. buf = (char *)malloc(size + 1);
  258. if (buf == NULL) {
  259. fclose(f);
  260. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", size + 1, __FUNCTION__, __LINE__);
  261. return false;
  262. }
  263. if (fread(buf, sizeof(*buf), size, f) != (size_t)size) {
  264. LogWrite(DATABASE__ERROR, 0, "Database", "Failed to read from '%s': %s", file, strerror(errno));
  265. fclose(f);
  266. free(buf);
  267. return false;
  268. }
  269. buf[size] = '\0';
  270. fclose(f);
  271. mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON);
  272. ret = mysql_real_query(&mysql, buf, size);
  273. free(buf);
  274. if (ret != 0) {
  275. LogWrite(DATABASE__ERROR, 0, "Database", "Error running MySQL queries from file '%s' (%d): %s", file, mysql_errno(&mysql), mysql_error(&mysql));
  276. success = false;
  277. }
  278. else {
  279. //all results must be processed
  280. do {
  281. res = mysql_store_result(&mysql);
  282. if (res != NULL)
  283. mysql_free_result(res);
  284. ret = mysql_next_result(&mysql);
  285. if (ret > 0) {
  286. LogWrite(DATABASE__ERROR, 0, "Database", "Error running MySQL queries from file '%s' (%d): %s", file, mysql_errno(&mysql), mysql_error(&mysql));
  287. success = false;
  288. }
  289. } while (ret == 0);
  290. }
  291. mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF);
  292. return success;
  293. }
  294. void DatabaseNew::SetIgnoredErrno(unsigned int db_errno) {
  295. vector<unsigned int>::iterator itr;
  296. for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) {
  297. if ((*itr) == db_errno)
  298. return;
  299. }
  300. ignored_errnos.push_back(db_errno);
  301. }
  302. void DatabaseNew::RemoveIgnoredErrno(unsigned int db_errno) {
  303. vector<unsigned int>::iterator itr;
  304. for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) {
  305. if ((*itr) == db_errno) {
  306. ignored_errnos.erase(itr);
  307. break;
  308. }
  309. }
  310. }
  311. bool DatabaseNew::IsIgnoredErrno(unsigned int db_errno) {
  312. vector<unsigned int>::iterator itr;
  313. for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) {
  314. if ((*itr) == db_errno)
  315. return true;
  316. }
  317. return false;
  318. }