DatabaseNew.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  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. int timeout = 10;
  35. mysql_options(&mysql, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);
  36. MMysql.SetName("DatabaseNew::mysql");
  37. }
  38. DatabaseNew::~DatabaseNew() {
  39. mysql_close(&mysql);
  40. #if MYSQL_VERSION_ID >= 50003
  41. mysql_library_end();
  42. #else
  43. mysql_server_end();
  44. #endif
  45. }
  46. bool DatabaseNew::Connect() {
  47. char line[256], *key, *val;
  48. char host[256], user[64], password[64], database[64];
  49. bool found_section = false;
  50. FILE *f;
  51. if ((f = fopen(DB_INI, "r")) == NULL) {
  52. LogWrite(DATABASE__ERROR, 0, "Database", "Unable to read %s\n", DB_INI);
  53. return false;
  54. }
  55. memset(host, 0, sizeof(host));
  56. memset(user, 0, sizeof(user));
  57. memset(password, 0, sizeof(password));
  58. memset(database, 0, sizeof(database));
  59. while (fgets(line, sizeof(line), f) != NULL) {
  60. if (line[0] == '#' || line[0] == '\n' || line[0] == '\r')
  61. continue;
  62. if (!found_section) {
  63. if (strncasecmp(line, "[Database]", 10) == 0)
  64. found_section = true;
  65. }
  66. else {
  67. if ((key = strtok(line, "=")) != NULL) {
  68. if ((val = strtok(NULL, "\r\n")) != NULL) {
  69. if (strncasecmp(line, "host", 4) == 0)
  70. strncpy(host, val, sizeof(host) - 1);
  71. else if (strncasecmp(line, "user", 4) == 0)
  72. strncpy(user, val, sizeof(user) - 1);
  73. else if (strncasecmp(line, "password", 8) == 0)
  74. strncpy(password, val, sizeof(password) - 1);
  75. else if (strncasecmp(line, "database", 8) == 0)
  76. strncpy(database, val, sizeof(database) - 1);
  77. }
  78. }
  79. }
  80. }
  81. fclose(f);
  82. if (host[0] == '\0') {
  83. LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'host' in '%s'\n", DB_INI);
  84. return false;
  85. }
  86. if (user[0] == '\0') {
  87. LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'user' in '%s'\n", DB_INI);
  88. return false;
  89. }
  90. if (password[0] == '\0') {
  91. LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'password' in '%s'\n", DB_INI);
  92. return false;
  93. }
  94. if (database[0] == '\0') {
  95. LogWrite(DATABASE__ERROR, 0, "Database", "Unknown 'database' in '%s'\n", DB_INI);
  96. return false;
  97. }
  98. return Connect(host, user, password, database);
  99. }
  100. bool DatabaseNew::Connect(const char *host, const char *user, const char *password, const char *database) {
  101. return Connect(host, user, password, database, 3306);
  102. }
  103. bool DatabaseNew::Connect(const char *host, const char *user, const char *password, const char *database, unsigned int port) {
  104. if (mysql_real_connect(&mysql, host, user, password, database, port, NULL, 0) == NULL) {
  105. LogWrite(DATABASE__ERROR, 0, "Database", "Unable to connect to MySQL server at %s:%u: %s\n", host, port, mysql_error(&mysql));
  106. return false;
  107. }
  108. return true;
  109. }
  110. bool DatabaseNew::Query(const char *query, ...) {
  111. char *buf;
  112. size_t size = QUERY_INITIAL_SIZE;
  113. int num_chars;
  114. va_list args;
  115. bool ret = true;
  116. MMysql.writelock(__FUNCTION__, __LINE__);
  117. while (true) {
  118. if ((buf = (char *)malloc(size)) == NULL) {
  119. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate database query of %u bytes\n", size);
  120. MMysql.releasewritelock(__FUNCTION__, __LINE__);
  121. return false;
  122. }
  123. va_start(args, query);
  124. num_chars = vsnprintf(buf, size, query, args);
  125. va_end(args);
  126. if (num_chars > -1 && (size_t)num_chars < size)
  127. break;
  128. if (num_chars > -1)
  129. size = num_chars + 1;
  130. else
  131. size *= 2;
  132. free(buf);
  133. }
  134. if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) {
  135. if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) {
  136. LogWrite(DATABASE__ERROR, 0, "Database", "Lost connection, attempting to recover and retry query...");
  137. Connect();
  138. // retry attempt of previous query (1 try and we give up)
  139. if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) {
  140. ret = false;
  141. }
  142. }
  143. else if (!IsIgnoredErrno(mysql_errno(&mysql))) {
  144. LogWrite(DATABASE__ERROR, 0, "Database", "Error %i running MySQL query: %s\n%s\n", mysql_errno(&mysql), mysql_error(&mysql), buf);
  145. ret = false;
  146. }
  147. }
  148. free(buf);
  149. MMysql.releasewritelock(__FUNCTION__, __LINE__);
  150. return ret;
  151. }
  152. bool DatabaseNew::Select(DatabaseResult *result, const char *query, ...) {
  153. char *buf;
  154. size_t size = QUERY_INITIAL_SIZE;
  155. int num_chars;
  156. va_list args;
  157. MYSQL_RES *res;
  158. bool ret = true;
  159. MMysql.writelock(__FUNCTION__, __LINE__);
  160. while (true) {
  161. if ((buf = (char *)malloc(size)) == NULL) {
  162. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate database query of %u bytes\n", size);
  163. MMysql.releasewritelock(__FUNCTION__, __LINE__);
  164. return false;
  165. }
  166. va_start(args, query);
  167. num_chars = vsnprintf(buf, size, query, args);
  168. va_end(args);
  169. if (num_chars > -1 && (size_t)num_chars < size)
  170. break;
  171. if (num_chars > -1)
  172. size = num_chars + 1;
  173. else
  174. size *= 2;
  175. free(buf);
  176. }
  177. if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) {
  178. if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) {
  179. LogWrite(DATABASE__ERROR, 0, "Database", "Lost connection, attempting to recover and retry query...");
  180. mysql_close(&mysql);
  181. Connect();
  182. // retry attempt of previous query (1 try and we give up)
  183. if (mysql_real_query(&mysql, buf, (unsigned long)num_chars) != 0) {
  184. ret = false;
  185. }
  186. }
  187. else if (!IsIgnoredErrno(mysql_errno(&mysql))) {
  188. LogWrite(DATABASE__ERROR, 0, "Database", "Error %i running MySQL query: %s\n%s\n", mysql_errno(&mysql), mysql_error(&mysql), buf);
  189. ret = false;
  190. }
  191. }
  192. if (ret && !IsIgnoredErrno(mysql_errno(&mysql))) {
  193. res = mysql_store_result(&mysql);
  194. if (res != NULL)
  195. ret = result->StoreResult(res);
  196. else {
  197. LogWrite(DATABASE__ERROR, 0, "Database", "Error storing MySql query result (%d): %s\n%s", mysql_errno(&mysql), mysql_error(&mysql), buf);
  198. ret = false;
  199. }
  200. }
  201. free(buf);
  202. MMysql.releasewritelock(__FUNCTION__, __LINE__);
  203. return ret;
  204. }
  205. int32 DatabaseNew::LastInsertID()
  206. {
  207. return (int32)mysql_insert_id(&mysql);
  208. }
  209. long DatabaseNew::AffectedRows()
  210. {
  211. return mysql_affected_rows(&mysql);
  212. }
  213. char * DatabaseNew::Escape(const char *str, size_t len) {
  214. char *buf = (char *)malloc(len * 2 + 1);
  215. if (buf == NULL) {
  216. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", len * 2 + 1, __FUNCTION__, __LINE__);
  217. return NULL;
  218. }
  219. mysql_real_escape_string(&mysql, buf, str, len);
  220. return buf;
  221. }
  222. char * DatabaseNew::Escape(const char *str) {
  223. return Escape(str, strlen(str));
  224. }
  225. string DatabaseNew::EscapeStr(const char *str, size_t len) {
  226. char *buf = (char *)malloc(len * 2 + 1);
  227. string ret;
  228. if (buf == NULL) {
  229. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", len * 2 + 1, __FUNCTION__, __LINE__);
  230. return NULL;
  231. }
  232. mysql_real_escape_string(&mysql, buf, str, len);
  233. ret.append(buf);
  234. free(buf);
  235. return ret;
  236. }
  237. string DatabaseNew::EscapeStr(const char *str) {
  238. return EscapeStr(str, strlen(str));
  239. }
  240. string DatabaseNew::EscapeStr(string str) {
  241. return EscapeStr(str.c_str(), str.length());
  242. }
  243. bool DatabaseNew::QueriesFromFile(const char * file) {
  244. bool success = true;
  245. long size;
  246. char *buf;
  247. int ret;
  248. MYSQL_RES *res;
  249. FILE *f;
  250. f = fopen(file, "rb");
  251. if (f == NULL) {
  252. LogWrite(DATABASE__ERROR, 0, "Database", "Unable to open '%s' for reading: %s", file, strerror(errno));
  253. return false;
  254. }
  255. fseek(f, 0, SEEK_END);
  256. size = ftell(f);
  257. fseek(f, 0, SEEK_SET);
  258. buf = (char *)malloc(size + 1);
  259. if (buf == NULL) {
  260. fclose(f);
  261. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", size + 1, __FUNCTION__, __LINE__);
  262. return false;
  263. }
  264. if (fread(buf, sizeof(*buf), size, f) != (size_t)size) {
  265. LogWrite(DATABASE__ERROR, 0, "Database", "Failed to read from '%s': %s", file, strerror(errno));
  266. fclose(f);
  267. free(buf);
  268. return false;
  269. }
  270. buf[size] = '\0';
  271. fclose(f);
  272. mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON);
  273. ret = mysql_real_query(&mysql, buf, size);
  274. free(buf);
  275. if (ret != 0) {
  276. LogWrite(DATABASE__ERROR, 0, "Database", "Error running MySQL queries from file '%s' (%d): %s", file, mysql_errno(&mysql), mysql_error(&mysql));
  277. success = false;
  278. }
  279. else {
  280. //all results must be processed
  281. do {
  282. res = mysql_store_result(&mysql);
  283. if (res != NULL)
  284. mysql_free_result(res);
  285. ret = mysql_next_result(&mysql);
  286. if (ret > 0) {
  287. LogWrite(DATABASE__ERROR, 0, "Database", "Error running MySQL queries from file '%s' (%d): %s", file, mysql_errno(&mysql), mysql_error(&mysql));
  288. success = false;
  289. }
  290. } while (ret == 0);
  291. }
  292. mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF);
  293. return success;
  294. }
  295. void DatabaseNew::SetIgnoredErrno(unsigned int db_errno) {
  296. vector<unsigned int>::iterator itr;
  297. for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) {
  298. if ((*itr) == db_errno)
  299. return;
  300. }
  301. ignored_errnos.push_back(db_errno);
  302. }
  303. void DatabaseNew::RemoveIgnoredErrno(unsigned int db_errno) {
  304. vector<unsigned int>::iterator itr;
  305. for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) {
  306. if ((*itr) == db_errno) {
  307. ignored_errnos.erase(itr);
  308. break;
  309. }
  310. }
  311. }
  312. bool DatabaseNew::IsIgnoredErrno(unsigned int db_errno) {
  313. vector<unsigned int>::iterator itr;
  314. for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) {
  315. if ((*itr) == db_errno)
  316. return true;
  317. }
  318. return false;
  319. }
  320. // Sends the MySQL server a keepalive
  321. void DatabaseNew::PingNewDB() {
  322. MMysql.writelock(__FUNCTION__, __LINE__);
  323. mysql_ping(&mysql);
  324. int32* errnum = new int32;
  325. *errnum = mysql_errno(&mysql);
  326. switch (*errnum)
  327. {
  328. case CR_COMMANDS_OUT_OF_SYNC:
  329. case CR_SERVER_GONE_ERROR:
  330. case CR_UNKNOWN_ERROR:
  331. {
  332. LogWrite(DATABASE__ERROR, 0, "Database", "[Database] We lost connection to the database., errno: %i", errno);
  333. break;
  334. }
  335. }
  336. safe_delete(errnum);
  337. MMysql.releasewritelock(__FUNCTION__, __LINE__);
  338. }