PageRenderTime 73ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/shared/Database/Database.cpp

https://bitbucket.org/oneb1t/crocoduckcore/
C++ | 602 lines | 464 code | 101 blank | 37 comment | 88 complexity | fd36b9f45e16763485cf53a41621e01b MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. /*
  2. * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
  3. *
  4. * Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
  5. *
  6. * Copyright (C) 2010 Oregon <http://www.oregoncore.com/>
  7. *
  8. * This program is free software; you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation; either version 2 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but WITHOUT
  14. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15. * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
  16. * more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include "DatabaseEnv.h"
  22. #include "Config/Config.h"
  23. #include "Common.h"
  24. #include "../../game/UpdateFields.h"
  25. #include "Util.h"
  26. #include "Policies/SingletonImp.h"
  27. #include "Platform/Define.h"
  28. #include "Threading.h"
  29. #include "Database/SqlDelayThread.h"
  30. #include "Database/SqlOperations.h"
  31. #include "Timer.h"
  32. #include <ctime>
  33. #include <iostream>
  34. #include <fstream>
  35. size_t Database::db_count = 0;
  36. Database::Database() : mMysql(NULL)
  37. {
  38. // before first connection
  39. if (db_count++ == 0)
  40. {
  41. // Mysql Library Init
  42. mysql_library_init(-1, NULL, NULL);
  43. if (!mysql_thread_safe())
  44. {
  45. sLog.outError("FATAL ERROR: Used MySQL library isn't thread-safe.");
  46. exit(1);
  47. }
  48. }
  49. }
  50. Database::~Database()
  51. {
  52. if (m_delayThread)
  53. HaltDelayThread();
  54. if (mMysql)
  55. mysql_close(mMysql);
  56. // Free Mysql library pointers for last ~DB
  57. if (--db_count == 0)
  58. mysql_library_end();
  59. }
  60. bool Database::Initialize(const char *infoString)
  61. {
  62. // Enable logging of SQL commands (usally only GM commands)
  63. // (See method: PExecuteLog)
  64. m_logSQL = sConfig.GetBoolDefault("LogSQL", false);
  65. m_logsDir = sConfig.GetStringDefault("LogsDir","");
  66. if (!m_logsDir.empty())
  67. {
  68. if ((m_logsDir.at(m_logsDir.length()-1)!='/') && (m_logsDir.at(m_logsDir.length()-1)!='\\'))
  69. m_logsDir.append("/");
  70. }
  71. tranThread = NULL;
  72. MYSQL *mysqlInit;
  73. mysqlInit = mysql_init(NULL);
  74. if (!mysqlInit)
  75. {
  76. sLog.outError("Could not initialize Mysql connection");
  77. return false;
  78. }
  79. InitDelayThread();
  80. Tokens tokens = StrSplit(infoString, ";");
  81. Tokens::iterator iter;
  82. std::string host, port_or_socket, user, password, database;
  83. int port;
  84. char const* unix_socket;
  85. iter = tokens.begin();
  86. if (iter != tokens.end())
  87. host = *iter++;
  88. if (iter != tokens.end())
  89. port_or_socket = *iter++;
  90. if (iter != tokens.end())
  91. user = *iter++;
  92. if (iter != tokens.end())
  93. password = *iter++;
  94. if (iter != tokens.end())
  95. database = *iter++;
  96. mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8");
  97. #ifdef _WIN32
  98. if (host==".") // named pipe use option (Windows)
  99. {
  100. unsigned int opt = MYSQL_PROTOCOL_PIPE;
  101. mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (char const*)&opt);
  102. port = 0;
  103. unix_socket = 0;
  104. }
  105. else // generic case
  106. {
  107. port = atoi(port_or_socket.c_str());
  108. unix_socket = 0;
  109. }
  110. #else
  111. if (host==".") // socket use option (Unix/Linux)
  112. {
  113. unsigned int opt = MYSQL_PROTOCOL_SOCKET;
  114. mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (char const*)&opt);
  115. host = "localhost";
  116. port = 0;
  117. unix_socket = port_or_socket.c_str();
  118. }
  119. else // generic case
  120. {
  121. port = atoi(port_or_socket.c_str());
  122. unix_socket = 0;
  123. }
  124. #endif
  125. mMysql = mysql_real_connect(mysqlInit, host.c_str(), user.c_str(),
  126. password.c_str(), database.c_str(), port, unix_socket, 0);
  127. if (mMysql)
  128. {
  129. sLog.outDetail("Connected to MySQL database at %s", host.c_str());
  130. sLog.outString("MySQL client library: %s", mysql_get_client_info());
  131. sLog.outString("MySQL server ver: %s ", mysql_get_server_info( mMysql));
  132. if (!mysql_autocommit(mMysql, 1))
  133. sLog.outDetail("AUTOCOMMIT SUCCESSFULLY SET TO 1");
  134. else
  135. sLog.outDetail("AUTOCOMMIT NOT SET TO 1");
  136. // set connection properties to UTF8 to properly handle locales for different
  137. // server configs - core sends data in UTF8, so MySQL must expect UTF8 too
  138. PExecute("SET NAMES `utf8`");
  139. PExecute("SET CHARACTER SET `utf8`");
  140. #if MYSQL_VERSION_ID >= 50003
  141. my_bool my_true = (my_bool)1;
  142. if (mysql_options(mMysql, MYSQL_OPT_RECONNECT, &my_true))
  143. sLog.outDetail("Failed to turn on MYSQL_OPT_RECONNECT.");
  144. else
  145. sLog.outDetail("Successfully turned on MYSQL_OPT_RECONNECT.");
  146. #else
  147. sLog.outDetail("Your mySQL client lib version does not support reconnecting after a timeout.\nIf this causes you any trouble we advice you to upgrade your mySQL client libs to at least mySQL 5.0.13 to resolve this problem.");
  148. #endif
  149. return true;
  150. }
  151. else
  152. {
  153. sLog.outError("Could not connect to MySQL database at %s: %s\n", host.c_str(),mysql_error(mysqlInit));
  154. mysql_close(mysqlInit);
  155. return false;
  156. }
  157. }
  158. void Database::ThreadStart()
  159. {
  160. mysql_thread_init();
  161. }
  162. void Database::ThreadEnd()
  163. {
  164. mysql_thread_end();
  165. }
  166. void Database::escape_string(std::string& str)
  167. {
  168. if (str.empty())
  169. return;
  170. char* buf = new char[str.size()*2+1];
  171. escape_string(buf,str.c_str(),str.size());
  172. str = buf;
  173. delete[] buf;
  174. }
  175. unsigned long Database::escape_string(char *to, const char *from, unsigned long length)
  176. {
  177. if (!mMysql || !to || !from || !length)
  178. return 0;
  179. return(mysql_real_escape_string(mMysql, to, from, length));
  180. }
  181. bool Database::PExecuteLog(const char * format,...)
  182. {
  183. if (!format)
  184. return false;
  185. va_list ap;
  186. char szQuery [MAX_QUERY_LEN];
  187. va_start(ap, format);
  188. int res = vsnprintf(szQuery, MAX_QUERY_LEN, format, ap);
  189. va_end(ap);
  190. if (res==-1)
  191. {
  192. sLog.outError("SQL Query truncated (and not execute) for format: %s",format);
  193. return false;
  194. }
  195. if (m_logSQL)
  196. {
  197. time_t curr;
  198. tm local;
  199. time(&curr); // get current time_t value
  200. local=*(localtime(&curr)); // dereference and assign
  201. char fName[128];
  202. sprintf(fName, "%04d-%02d-%02d_logSQL.sql", local.tm_year+1900, local.tm_mon+1, local.tm_mday);
  203. FILE* log_file;
  204. std::string logsDir_fname = m_logsDir+fName;
  205. log_file = fopen(logsDir_fname.c_str(), "a");
  206. if (log_file)
  207. {
  208. fprintf(log_file, "%s;\n", szQuery);
  209. fclose(log_file);
  210. }
  211. else
  212. {
  213. // The file could not be opened
  214. sLog.outError("SQL-Logging is disabled - Log file for the SQL commands could not be openend: %s",fName);
  215. }
  216. }
  217. return Execute(szQuery);
  218. }
  219. void Database::SetResultQueue(SqlResultQueue * queue)
  220. {
  221. m_queryQueues[ACE_Based::Thread::current()] = queue;
  222. }
  223. bool Database::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount)
  224. {
  225. if (!mMysql)
  226. return 0;
  227. {
  228. // guarded block for thread-safe mySQL request
  229. ACE_Guard<ACE_Thread_Mutex> query_connection_guard(mMutex);
  230. #ifdef TRINITY_DEBUG
  231. uint32 _s = getMSTime();
  232. #endif
  233. if (mysql_query(mMysql, sql))
  234. {
  235. sLog.outErrorDb("SQL: %s", sql);
  236. sLog.outErrorDb("query ERROR: %s", mysql_error(mMysql));
  237. return false;
  238. }
  239. else
  240. {
  241. #ifdef TRINITY_DEBUG
  242. sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql );
  243. #endif
  244. }
  245. *pResult = mysql_store_result(mMysql);
  246. *pRowCount = mysql_affected_rows(mMysql);
  247. *pFieldCount = mysql_field_count(mMysql);
  248. }
  249. if (!*pResult )
  250. return false;
  251. if (!*pRowCount)
  252. {
  253. mysql_free_result(*pResult);
  254. return false;
  255. }
  256. *pFields = mysql_fetch_fields(*pResult);
  257. return true;
  258. }
  259. QueryResult_AutoPtr Database::Query(const char *sql)
  260. {
  261. MYSQL_RES *result = NULL;
  262. MYSQL_FIELD *fields = NULL;
  263. uint64 rowCount = 0;
  264. uint32 fieldCount = 0;
  265. if (!_Query(sql, &result, &fields, &rowCount, &fieldCount))
  266. return QueryResult_AutoPtr(NULL);
  267. QueryResult *queryResult = new QueryResult(result, fields, rowCount, fieldCount);
  268. queryResult->NextRow();
  269. return QueryResult_AutoPtr(queryResult);
  270. }
  271. QueryResult_AutoPtr Database::PQuery(const char *format,...)
  272. {
  273. if (!format)
  274. return QueryResult_AutoPtr(NULL);
  275. va_list ap;
  276. char szQuery [MAX_QUERY_LEN];
  277. va_start(ap, format);
  278. int res = vsnprintf(szQuery, MAX_QUERY_LEN, format, ap);
  279. va_end(ap);
  280. if (res==-1)
  281. {
  282. sLog.outError("SQL Query truncated (and not execute) for format: %s",format);
  283. return QueryResult_AutoPtr(NULL);
  284. }
  285. return Query(szQuery);
  286. }
  287. QueryNamedResult* Database::QueryNamed(const char *sql)
  288. {
  289. MYSQL_RES *result = NULL;
  290. MYSQL_FIELD *fields = NULL;
  291. uint64 rowCount = 0;
  292. uint32 fieldCount = 0;
  293. if (!_Query(sql, &result, &fields, &rowCount, &fieldCount))
  294. return NULL;
  295. QueryFieldNames names(fieldCount);
  296. for (uint32 i = 0; i < fieldCount; i++)
  297. names[i] = fields[i].name;
  298. QueryResult *queryResult = new QueryResult(result, fields, rowCount, fieldCount);
  299. queryResult->NextRow();
  300. return new QueryNamedResult(queryResult, names);
  301. }
  302. QueryNamedResult* Database::PQueryNamed(const char *format,...)
  303. {
  304. if (!format)
  305. return NULL;
  306. va_list ap;
  307. char szQuery [MAX_QUERY_LEN];
  308. va_start(ap, format);
  309. int res = vsnprintf(szQuery, MAX_QUERY_LEN, format, ap);
  310. va_end(ap);
  311. if (res==-1)
  312. {
  313. sLog.outError("SQL Query truncated (and not execute) for format: %s",format);
  314. return false;
  315. }
  316. return QueryNamed(szQuery);
  317. }
  318. bool Database::Execute(const char *sql)
  319. {
  320. if (!mMysql)
  321. return false;
  322. // don't use queued execution if it has not been initialized
  323. if (!m_threadBody)
  324. return DirectExecute(sql);
  325. nMutex.acquire();
  326. tranThread = ACE_Based::Thread::current(); // owner of this transaction
  327. TransactionQueues::iterator i = m_tranQueues.find(tranThread);
  328. if (i != m_tranQueues.end() && i->second != NULL)
  329. i->second->DelayExecute(sql); // Statement for transaction
  330. else
  331. m_threadBody->Delay(new SqlStatement(sql)); // Simple sql statement
  332. nMutex.release();
  333. return true;
  334. }
  335. bool Database::PExecute(const char * format,...)
  336. {
  337. if (!format)
  338. return false;
  339. va_list ap;
  340. char szQuery [MAX_QUERY_LEN];
  341. va_start(ap, format);
  342. int res = vsnprintf(szQuery, MAX_QUERY_LEN, format, ap);
  343. va_end(ap);
  344. if (res==-1)
  345. {
  346. sLog.outError("SQL Query truncated (and not execute) for format: %s",format);
  347. return false;
  348. }
  349. return Execute(szQuery);
  350. }
  351. bool Database::DirectExecute(const char* sql)
  352. {
  353. if (!mMysql)
  354. return false;
  355. {
  356. // guarded block for thread-safe mySQL request
  357. ACE_Guard<ACE_Thread_Mutex> query_connection_guard(mMutex);
  358. #ifdef OREGON_DEBUG
  359. uint32 _s = getMSTime();
  360. #endif
  361. if (mysql_query(mMysql, sql))
  362. {
  363. sLog.outErrorDb("SQL: %s", sql);
  364. sLog.outErrorDb("SQL ERROR: %s", mysql_error(mMysql));
  365. return false;
  366. }
  367. else
  368. {
  369. #ifdef OREGON_DEBUG
  370. sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql);
  371. #endif
  372. }
  373. }
  374. return true;
  375. }
  376. bool Database::DirectPExecute(const char * format,...)
  377. {
  378. if (!format)
  379. return false;
  380. va_list ap;
  381. char szQuery [MAX_QUERY_LEN];
  382. va_start(ap, format);
  383. int res = vsnprintf(szQuery, MAX_QUERY_LEN, format, ap);
  384. va_end(ap);
  385. if (res==-1)
  386. {
  387. sLog.outError("SQL Query truncated (and not execute) for format: %s",format);
  388. return false;
  389. }
  390. return DirectExecute(szQuery);
  391. }
  392. bool Database::_TransactionCmd(const char *sql)
  393. {
  394. if (mysql_query(mMysql, sql))
  395. {
  396. sLog.outError("SQL: %s", sql);
  397. sLog.outError("SQL ERROR: %s", mysql_error(mMysql));
  398. return false;
  399. }
  400. else
  401. DEBUG_LOG("SQL: %s", sql);
  402. return true;
  403. }
  404. bool Database::BeginTransaction()
  405. {
  406. if (!mMysql)
  407. return false;
  408. // don't use queued execution if it has not been initialized
  409. if (!m_threadBody)
  410. {
  411. if (tranThread == ACE_Based::Thread::current())
  412. return false; // huh? this thread already started transaction
  413. mMutex.acquire();
  414. if (!_TransactionCmd("START TRANSACTION"))
  415. {
  416. mMutex.release(); // can't start transaction
  417. return false;
  418. }
  419. return true; // transaction started
  420. }
  421. nMutex.acquire();
  422. tranThread = ACE_Based::Thread::current(); // owner of this transaction
  423. TransactionQueues::iterator i = m_tranQueues.find(tranThread);
  424. if (i != m_tranQueues.end() && i->second != NULL)
  425. // If for thread exists queue and also contains transaction
  426. // delete that transaction (not allow trans in trans)
  427. delete i->second;
  428. m_tranQueues[tranThread] = new SqlTransaction();
  429. nMutex.release();
  430. return true;
  431. }
  432. bool Database::CommitTransaction()
  433. {
  434. if (!mMysql)
  435. return false;
  436. bool _res = false;
  437. // don't use queued execution if it has not been initialized
  438. if (!m_threadBody)
  439. {
  440. if (tranThread != ACE_Based::Thread::current())
  441. return false;
  442. _res = _TransactionCmd("COMMIT");
  443. tranThread = NULL;
  444. mMutex.release();
  445. return _res;
  446. }
  447. nMutex.acquire();
  448. tranThread = ACE_Based::Thread::current();
  449. TransactionQueues::iterator i = m_tranQueues.find(tranThread);
  450. if (i != m_tranQueues.end() && i->second != NULL)
  451. {
  452. m_threadBody->Delay(i->second);
  453. m_tranQueues.erase(i);
  454. _res = true;
  455. }
  456. nMutex.release();
  457. return _res;
  458. }
  459. bool Database::RollbackTransaction()
  460. {
  461. if (!mMysql)
  462. return false;
  463. // don't use queued execution if it has not been initialized
  464. if (!m_threadBody)
  465. {
  466. if (tranThread != ACE_Based::Thread::current())
  467. return false;
  468. bool _res = _TransactionCmd("ROLLBACK");
  469. tranThread = NULL;
  470. mMutex.release();
  471. return _res;
  472. }
  473. nMutex.acquire();
  474. tranThread = ACE_Based::Thread::current();
  475. TransactionQueues::iterator i = m_tranQueues.find(tranThread);
  476. if (i != m_tranQueues.end() && i->second != NULL)
  477. {
  478. delete i->second;
  479. i->second = NULL;
  480. m_tranQueues.erase(i);
  481. }
  482. nMutex.release();
  483. return true;
  484. }
  485. void Database::InitDelayThread()
  486. {
  487. assert(!m_delayThread);
  488. //New delay thread for delay execute
  489. m_threadBody = new SqlDelayThread(this); // will deleted at m_delayThread delete
  490. m_delayThread = new ACE_Based::Thread(m_threadBody);
  491. }
  492. void Database::HaltDelayThread()
  493. {
  494. if (!m_threadBody || !m_delayThread)
  495. return;
  496. m_threadBody->Stop(); //Stop event
  497. m_delayThread->wait(); //Wait for flush to DB
  498. delete m_delayThread; //This also deletes m_threadBody
  499. m_delayThread = NULL;
  500. m_threadBody = NULL;
  501. }