DatabaseNew.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  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. {
  197. // Grab number of rows and number of fields from the query
  198. uint8 num_rows = mysql_affected_rows(&mysql);
  199. uint8 num_fields = mysql_field_count(&mysql);
  200. ret = result->StoreResult(res, num_fields, num_rows);
  201. }
  202. else {
  203. LogWrite(DATABASE__ERROR, 0, "Database", "Error storing MySql query result (%d): %s\n%s", mysql_errno(&mysql), mysql_error(&mysql), buf);
  204. ret = false;
  205. }
  206. }
  207. free(buf);
  208. MMysql.releasewritelock(__FUNCTION__, __LINE__);
  209. return ret;
  210. }
  211. int32 DatabaseNew::LastInsertID()
  212. {
  213. return (int32)mysql_insert_id(&mysql);
  214. }
  215. long DatabaseNew::AffectedRows()
  216. {
  217. return mysql_affected_rows(&mysql);
  218. }
  219. char * DatabaseNew::Escape(const char *str, size_t len) {
  220. char *buf = (char *)malloc(len * 2 + 1);
  221. if (buf == NULL) {
  222. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", len * 2 + 1, __FUNCTION__, __LINE__);
  223. return NULL;
  224. }
  225. mysql_real_escape_string(&mysql, buf, str, len);
  226. return buf;
  227. }
  228. char * DatabaseNew::Escape(const char *str) {
  229. return Escape(str, strlen(str));
  230. }
  231. string DatabaseNew::EscapeStr(const char *str, size_t len) {
  232. char *buf = (char *)malloc(len * 2 + 1);
  233. string ret;
  234. if (buf == NULL) {
  235. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", len * 2 + 1, __FUNCTION__, __LINE__);
  236. return NULL;
  237. }
  238. mysql_real_escape_string(&mysql, buf, str, len);
  239. ret.append(buf);
  240. free(buf);
  241. return ret;
  242. }
  243. string DatabaseNew::EscapeStr(const char *str) {
  244. return EscapeStr(str, strlen(str));
  245. }
  246. string DatabaseNew::EscapeStr(string str) {
  247. return EscapeStr(str.c_str(), str.length());
  248. }
  249. bool DatabaseNew::QueriesFromFile(const char * file) {
  250. bool success = true;
  251. long size;
  252. char *buf;
  253. int ret;
  254. MYSQL_RES *res;
  255. FILE *f;
  256. f = fopen(file, "rb");
  257. if (f == NULL) {
  258. LogWrite(DATABASE__ERROR, 0, "Database", "Unable to open '%s' for reading: %s", file, strerror(errno));
  259. return false;
  260. }
  261. fseek(f, 0, SEEK_END);
  262. size = ftell(f);
  263. fseek(f, 0, SEEK_SET);
  264. buf = (char *)malloc(size + 1);
  265. if (buf == NULL) {
  266. fclose(f);
  267. LogWrite(DATABASE__ERROR, 0, "Database", "Out of memory trying to allocate %u bytes in %s:%u\n", size + 1, __FUNCTION__, __LINE__);
  268. return false;
  269. }
  270. if (fread(buf, sizeof(*buf), size, f) != (size_t)size) {
  271. LogWrite(DATABASE__ERROR, 0, "Database", "Failed to read from '%s': %s", file, strerror(errno));
  272. fclose(f);
  273. free(buf);
  274. return false;
  275. }
  276. buf[size] = '\0';
  277. fclose(f);
  278. mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_ON);
  279. ret = mysql_real_query(&mysql, buf, size);
  280. free(buf);
  281. if (ret != 0) {
  282. LogWrite(DATABASE__ERROR, 0, "Database", "Error running MySQL queries from file '%s' (%d): %s", file, mysql_errno(&mysql), mysql_error(&mysql));
  283. success = false;
  284. }
  285. else {
  286. //all results must be processed
  287. do {
  288. res = mysql_store_result(&mysql);
  289. if (res != NULL)
  290. mysql_free_result(res);
  291. ret = mysql_next_result(&mysql);
  292. if (ret > 0) {
  293. LogWrite(DATABASE__ERROR, 0, "Database", "Error running MySQL queries from file '%s' (%d): %s", file, mysql_errno(&mysql), mysql_error(&mysql));
  294. success = false;
  295. }
  296. } while (ret == 0);
  297. }
  298. mysql_set_server_option(&mysql, MYSQL_OPTION_MULTI_STATEMENTS_OFF);
  299. return success;
  300. }
  301. void DatabaseNew::SetIgnoredErrno(unsigned int db_errno) {
  302. vector<unsigned int>::iterator itr;
  303. for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) {
  304. if ((*itr) == db_errno)
  305. return;
  306. }
  307. ignored_errnos.push_back(db_errno);
  308. }
  309. void DatabaseNew::RemoveIgnoredErrno(unsigned int db_errno) {
  310. vector<unsigned int>::iterator itr;
  311. for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) {
  312. if ((*itr) == db_errno) {
  313. ignored_errnos.erase(itr);
  314. break;
  315. }
  316. }
  317. }
  318. bool DatabaseNew::IsIgnoredErrno(unsigned int db_errno) {
  319. vector<unsigned int>::iterator itr;
  320. for (itr = ignored_errnos.begin(); itr != ignored_errnos.end(); itr++) {
  321. if ((*itr) == db_errno)
  322. return true;
  323. }
  324. return false;
  325. }
  326. // Sends the MySQL server a keepalive
  327. void DatabaseNew::PingNewDB() {
  328. MMysql.writelock(__FUNCTION__, __LINE__);
  329. mysql_ping(&mysql);
  330. int32* errnum = new int32;
  331. *errnum = mysql_errno(&mysql);
  332. switch (*errnum)
  333. {
  334. case CR_COMMANDS_OUT_OF_SYNC:
  335. case CR_SERVER_GONE_ERROR:
  336. case CR_UNKNOWN_ERROR:
  337. {
  338. LogWrite(DATABASE__ERROR, 0, "Database", "[Database] We lost connection to the database., errno: %i", errno);
  339. break;
  340. }
  341. }
  342. safe_delete(errnum);
  343. MMysql.releasewritelock(__FUNCTION__, __LINE__);
  344. }