PageRenderTime 62ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 1ms

/src/shared/Database/DatabaseMysql.cpp

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