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

/src/shared/Database/DatabaseMysql.cpp

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