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