PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/tags/0.8.01/src/shared/Database/DatabaseMysql.cpp

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