PageRenderTime 39ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/src/shared/Database/DatabaseMysql.cpp

https://bitbucket.org/KPsN/trinitycore/
C++ | 438 lines | 332 code | 73 blank | 33 comment | 73 complexity | a57ac4b5ead3deaaa567630585126a9e MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, CC-BY-SA-3.0
  1. /*
  2. * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
  3. *
  4. * Copyright (C) 2008-2010 Trinity <http://www.trinitycore.org/>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  19. */
  20. #ifndef DO_POSTGRESQL
  21. #include "Util.h"
  22. #include "Policies/SingletonImp.h"
  23. #include "Platform/Define.h"
  24. #include "Threading.h"
  25. #include "DatabaseEnv.h"
  26. #include "Database/MySQLDelayThread.h"
  27. #include "Database/SqlOperations.h"
  28. #include "Timer.h"
  29. void DatabaseMysql::ThreadStart()
  30. {
  31. mysql_thread_init();
  32. }
  33. void DatabaseMysql::ThreadEnd()
  34. {
  35. mysql_thread_end();
  36. }
  37. size_t DatabaseMysql::db_count = 0;
  38. DatabaseMysql::DatabaseMysql() : Database(), mMysql(0)
  39. {
  40. // before first connection
  41. if (db_count++ == 0)
  42. {
  43. // Mysql Library Init
  44. mysql_library_init(-1, NULL, NULL);
  45. if (!mysql_thread_safe())
  46. {
  47. sLog.outError("FATAL ERROR: Used MySQL library isn't thread-safe.");
  48. exit(1);
  49. }
  50. }
  51. }
  52. DatabaseMysql::~DatabaseMysql()
  53. {
  54. if (m_delayThread)
  55. HaltDelayThread();
  56. if (mMysql)
  57. mysql_close(mMysql);
  58. // Free Mysql library pointers for last ~DB
  59. if (--db_count == 0)
  60. mysql_library_end();
  61. }
  62. bool DatabaseMysql::Initialize(const char *infoString)
  63. {
  64. if (!Database::Initialize(infoString))
  65. return false;
  66. tranThread = NULL;
  67. MYSQL *mysqlInit;
  68. mysqlInit = mysql_init(NULL);
  69. if (!mysqlInit)
  70. {
  71. sLog.outError("Could not initialize Mysql connection");
  72. return false;
  73. }
  74. InitDelayThread();
  75. Tokens tokens = StrSplit(infoString, ";");
  76. Tokens::iterator iter;
  77. std::string host, port_or_socket, user, password, database;
  78. int port;
  79. char const* unix_socket;
  80. iter = tokens.begin();
  81. if (iter != tokens.end())
  82. host = *iter++;
  83. if (iter != tokens.end())
  84. port_or_socket = *iter++;
  85. if (iter != tokens.end())
  86. user = *iter++;
  87. if (iter != tokens.end())
  88. password = *iter++;
  89. if (iter != tokens.end())
  90. database = *iter++;
  91. mysql_options(mysqlInit, MYSQL_SET_CHARSET_NAME, "utf8");
  92. #ifdef WIN32
  93. if (host==".") // named pipe use option (Windows)
  94. {
  95. unsigned int opt = MYSQL_PROTOCOL_PIPE;
  96. mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (char const*)&opt);
  97. port = 0;
  98. unix_socket = 0;
  99. }
  100. else // generic case
  101. {
  102. port = atoi(port_or_socket.c_str());
  103. unix_socket = 0;
  104. }
  105. #else
  106. if (host==".") // socket use option (Unix/Linux)
  107. {
  108. unsigned int opt = MYSQL_PROTOCOL_SOCKET;
  109. mysql_options(mysqlInit, MYSQL_OPT_PROTOCOL, (char const*)&opt);
  110. host = "localhost";
  111. port = 0;
  112. unix_socket = port_or_socket.c_str();
  113. }
  114. else // generic case
  115. {
  116. port = atoi(port_or_socket.c_str());
  117. unix_socket = 0;
  118. }
  119. #endif
  120. mMysql = mysql_real_connect(mysqlInit, host.c_str(), user.c_str(),
  121. password.c_str(), database.c_str(), port, unix_socket, 0);
  122. if (mMysql)
  123. {
  124. sLog.outDetail("Connected to MySQL database at %s", host.c_str());
  125. sLog.outString("MySQL client library: %s", mysql_get_client_info());
  126. sLog.outString("MySQL server ver: %s ", mysql_get_server_info( mMysql));
  127. if (!mysql_autocommit(mMysql, 1))
  128. sLog.outDetail("AUTOCOMMIT SUCCESSFULLY SET TO 1");
  129. else
  130. sLog.outDetail("AUTOCOMMIT NOT SET TO 1");
  131. // set connection properties to UTF8 to properly handle locales for different
  132. // server configs - core sends data in UTF8, so MySQL must expect UTF8 too
  133. PExecute("SET NAMES `utf8`");
  134. PExecute("SET CHARACTER SET `utf8`");
  135. #if MYSQL_VERSION_ID >= 50003
  136. my_bool my_true = (my_bool)1;
  137. if (mysql_options(mMysql, MYSQL_OPT_RECONNECT, &my_true))
  138. sLog.outDetail("Failed to turn on MYSQL_OPT_RECONNECT.");
  139. else
  140. sLog.outDetail("Successfully turned on MYSQL_OPT_RECONNECT.");
  141. #else
  142. #warning "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."
  143. #endif
  144. return true;
  145. }
  146. else
  147. {
  148. sLog.outError("Could not connect to MySQL database at %s: %s\n", host.c_str(),mysql_error(mysqlInit));
  149. mysql_close(mysqlInit);
  150. return false;
  151. }
  152. }
  153. bool DatabaseMysql::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount)
  154. {
  155. if (!mMysql)
  156. return 0;
  157. {
  158. // guarded block for thread-safe mySQL request
  159. ACE_Guard<ACE_Thread_Mutex> query_connection_guard(mMutex);
  160. #ifdef TRINITY_DEBUG
  161. uint32 _s = getMSTime();
  162. #endif
  163. if (mysql_query(mMysql, sql))
  164. {
  165. sLog.outErrorDb("SQL: %s", sql);
  166. sLog.outErrorDb("query ERROR: %s", mysql_error(mMysql));
  167. return false;
  168. }
  169. else
  170. {
  171. #ifdef TRINITY_DEBUG
  172. sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql );
  173. #endif
  174. }
  175. *pResult = mysql_store_result(mMysql);
  176. *pRowCount = mysql_affected_rows(mMysql);
  177. *pFieldCount = mysql_field_count(mMysql);
  178. }
  179. if (!*pResult )
  180. return false;
  181. if (!*pRowCount)
  182. {
  183. mysql_free_result(*pResult);
  184. return false;
  185. }
  186. *pFields = mysql_fetch_fields(*pResult);
  187. return true;
  188. }
  189. QueryResult_AutoPtr DatabaseMysql::Query(const char *sql)
  190. {
  191. MYSQL_RES *result = NULL;
  192. MYSQL_FIELD *fields = NULL;
  193. uint64 rowCount = 0;
  194. uint32 fieldCount = 0;
  195. if (!_Query(sql, &result, &fields, &rowCount, &fieldCount))
  196. return QueryResult_AutoPtr(NULL);
  197. QueryResultMysql *queryResult = new QueryResultMysql(result, fields, rowCount, fieldCount);
  198. queryResult->NextRow();
  199. return QueryResult_AutoPtr(queryResult);
  200. }
  201. QueryNamedResult* DatabaseMysql::QueryNamed(const char *sql)
  202. {
  203. MYSQL_RES *result = NULL;
  204. MYSQL_FIELD *fields = NULL;
  205. uint64 rowCount = 0;
  206. uint32 fieldCount = 0;
  207. if (!_Query(sql, &result, &fields, &rowCount, &fieldCount))
  208. return NULL;
  209. QueryFieldNames names(fieldCount);
  210. for (uint32 i = 0; i < fieldCount; i++)
  211. names[i] = fields[i].name;
  212. QueryResultMysql *queryResult = new QueryResultMysql(result, fields, rowCount, fieldCount);
  213. queryResult->NextRow();
  214. return new QueryNamedResult(queryResult, names);
  215. }
  216. bool DatabaseMysql::Execute(const char *sql)
  217. {
  218. if (!mMysql)
  219. return false;
  220. // don't use queued execution if it has not been initialized
  221. if (!m_threadBody)
  222. return DirectExecute(sql);
  223. tranThread = ACE_Based::Thread::current(); // owner of this transaction
  224. TransactionQueues::iterator i = m_tranQueues.find(tranThread);
  225. if (i != m_tranQueues.end() && i->second != NULL)
  226. i->second->DelayExecute(sql); // Statement for transaction
  227. else
  228. m_threadBody->Delay(new SqlStatement(sql)); // Simple sql statement
  229. return true;
  230. }
  231. bool DatabaseMysql::DirectExecute(const char* sql)
  232. {
  233. if (!mMysql)
  234. return false;
  235. {
  236. // guarded block for thread-safe mySQL request
  237. ACE_Guard<ACE_Thread_Mutex> query_connection_guard(mMutex);
  238. #ifdef TRINITY_DEBUG
  239. uint32 _s = getMSTime();
  240. #endif
  241. if (mysql_query(mMysql, sql))
  242. {
  243. sLog.outErrorDb("SQL: %s", sql);
  244. sLog.outErrorDb("SQL ERROR: %s", mysql_error(mMysql));
  245. return false;
  246. }
  247. else
  248. {
  249. #ifdef TRINITY_DEBUG
  250. sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql);
  251. #endif
  252. }
  253. }
  254. return true;
  255. }
  256. bool DatabaseMysql::_TransactionCmd(const char *sql)
  257. {
  258. if (mysql_query(mMysql, sql))
  259. {
  260. sLog.outError("SQL: %s", sql);
  261. sLog.outError("SQL ERROR: %s", mysql_error(mMysql));
  262. return false;
  263. }
  264. else
  265. DEBUG_LOG("SQL: %s", sql);
  266. return true;
  267. }
  268. bool DatabaseMysql::BeginTransaction()
  269. {
  270. if (!mMysql)
  271. return false;
  272. // don't use queued execution if it has not been initialized
  273. if (!m_threadBody)
  274. {
  275. if (tranThread == ACE_Based::Thread::current())
  276. return false; // huh? this thread already started transaction
  277. mMutex.acquire();
  278. if (!_TransactionCmd("START TRANSACTION"))
  279. {
  280. mMutex.release(); // can't start transaction
  281. return false;
  282. }
  283. return true; // transaction started
  284. }
  285. tranThread = ACE_Based::Thread::current(); // owner of this transaction
  286. TransactionQueues::iterator i = m_tranQueues.find(tranThread);
  287. if (i != m_tranQueues.end() && i->second != NULL)
  288. // If for thread exists queue and also contains transaction
  289. // delete that transaction (not allow trans in trans)
  290. delete i->second;
  291. m_tranQueues[tranThread] = new SqlTransaction();
  292. return true;
  293. }
  294. bool DatabaseMysql::CommitTransaction()
  295. {
  296. if (!mMysql)
  297. return false;
  298. // don't use queued execution if it has not been initialized
  299. if (!m_threadBody)
  300. {
  301. if (tranThread != ACE_Based::Thread::current())
  302. return false;
  303. bool _res = _TransactionCmd("COMMIT");
  304. tranThread = NULL;
  305. mMutex.release();
  306. return _res;
  307. }
  308. tranThread = ACE_Based::Thread::current();
  309. TransactionQueues::iterator i = m_tranQueues.find(tranThread);
  310. if (i != m_tranQueues.end() && i->second != NULL)
  311. {
  312. m_threadBody->Delay(i->second);
  313. i->second = NULL;
  314. return true;
  315. }
  316. else
  317. return false;
  318. }
  319. bool DatabaseMysql::RollbackTransaction()
  320. {
  321. if (!mMysql)
  322. return false;
  323. // don't use queued execution if it has not been initialized
  324. if (!m_threadBody)
  325. {
  326. if (tranThread != ACE_Based::Thread::current())
  327. return false;
  328. bool _res = _TransactionCmd("ROLLBACK");
  329. tranThread = NULL;
  330. mMutex.release();
  331. return _res;
  332. }
  333. tranThread = ACE_Based::Thread::current();
  334. TransactionQueues::iterator i = m_tranQueues.find(tranThread);
  335. if (i != m_tranQueues.end() && i->second != NULL)
  336. {
  337. delete i->second;
  338. i->second = NULL;
  339. }
  340. return true;
  341. }
  342. unsigned long DatabaseMysql::escape_string(char *to, const char *from, unsigned long length)
  343. {
  344. if (!mMysql || !to || !from || !length)
  345. return 0;
  346. return(mysql_real_escape_string(mMysql, to, from, length));
  347. }
  348. void DatabaseMysql::InitDelayThread()
  349. {
  350. assert(!m_delayThread);
  351. //New delay thread for delay execute
  352. m_threadBody = new MySQLDelayThread(this); // will deleted at m_delayThread delete
  353. m_delayThread = new ACE_Based::Thread(m_threadBody);
  354. }
  355. void DatabaseMysql::HaltDelayThread()
  356. {
  357. if (!m_threadBody || !m_delayThread)
  358. return;
  359. m_threadBody->Stop(); //Stop event
  360. m_delayThread->wait(); //Wait for flush to DB
  361. delete m_delayThread; //This also deletes m_threadBody
  362. m_delayThread = NULL;
  363. m_threadBody = NULL;
  364. }
  365. #endif