Implemented ACHIEVEMENT_FLAG_REALM_FIRST_REACH
[AHbot.git] / src / shared / Database / DatabaseMysql.cpp
blobe8a944feb0c2dcbe496e5c16195b1826f0faa25f
1 /*
2 * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/>
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.
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.
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
19 #ifndef DO_POSTGRESQL
21 #include "Util.h"
22 #include "Policies/SingletonImp.h"
23 #include "Platform/Define.h"
24 #include "../src/zthread/ThreadImpl.h"
25 #include "DatabaseEnv.h"
26 #include "Database/MySQLDelayThread.h"
27 #include "Database/SqlOperations.h"
28 #include "Timer.h"
30 void DatabaseMysql::ThreadStart()
32 mysql_thread_init();
35 void DatabaseMysql::ThreadEnd()
37 mysql_thread_end();
40 size_t DatabaseMysql::db_count = 0;
42 DatabaseMysql::DatabaseMysql() : Database(), mMysql(0)
44 // before first connection
45 if( db_count++ == 0 )
47 // Mysql Library Init
48 mysql_library_init(-1, NULL, NULL);
50 if (!mysql_thread_safe())
52 sLog.outError("FATAL ERROR: Used MySQL library isn't thread-safe.");
53 exit(1);
58 DatabaseMysql::~DatabaseMysql()
60 if (m_delayThread)
61 HaltDelayThread();
63 if (mMysql)
64 mysql_close(mMysql);
66 //Free Mysql library pointers for last ~DB
67 if(--db_count == 0)
68 mysql_library_end();
71 bool DatabaseMysql::Initialize(const char *infoString)
74 if(!Database::Initialize(infoString))
75 return false;
77 tranThread = NULL;
78 MYSQL *mysqlInit;
79 mysqlInit = mysql_init(NULL);
80 if (!mysqlInit)
82 sLog.outError( "Could not initialize Mysql connection" );
83 return false;
86 InitDelayThread();
88 Tokens tokens = StrSplit(infoString, ";");
90 Tokens::iterator iter;
92 std::string host, port_or_socket, user, password, database;
93 int port;
94 char const* unix_socket;
96 iter = tokens.begin();
98 if(iter != tokens.end())
99 host = *iter++;
100 if(iter != tokens.end())
101 port_or_socket = *iter++;
102 if(iter != tokens.end())
103 user = *iter++;
104 if(iter != tokens.end())
105 password = *iter++;
106 if(iter != tokens.end())
107 database = *iter++;
109 mysql_options(mysqlInit,MYSQL_SET_CHARSET_NAME,"utf8");
110 #ifdef WIN32
111 if(host==".") // named pipe use option (Windows)
113 unsigned int opt = MYSQL_PROTOCOL_PIPE;
114 mysql_options(mysqlInit,MYSQL_OPT_PROTOCOL,(char const*)&opt);
115 port = 0;
116 unix_socket = 0;
118 else // generic case
120 port = atoi(port_or_socket.c_str());
121 unix_socket = 0;
123 #else
124 if(host==".") // socket use option (Unix/Linux)
126 unsigned int opt = MYSQL_PROTOCOL_SOCKET;
127 mysql_options(mysqlInit,MYSQL_OPT_PROTOCOL,(char const*)&opt);
128 host = "localhost";
129 port = 0;
130 unix_socket = port_or_socket.c_str();
132 else // generic case
134 port = atoi(port_or_socket.c_str());
135 unix_socket = 0;
137 #endif
139 mMysql = mysql_real_connect(mysqlInit, host.c_str(), user.c_str(),
140 password.c_str(), database.c_str(), port, unix_socket, 0);
142 if (mMysql)
144 sLog.outDetail( "Connected to MySQL database at %s",
145 host.c_str());
146 sLog.outString( "MySQL client library: %s", mysql_get_client_info());
147 sLog.outString( "MySQL server ver: %s ", mysql_get_server_info( mMysql));
149 /*----------SET AUTOCOMMIT ON---------*/
150 // It seems mysql 5.0.x have enabled this feature
151 // by default. In crash case you can lose data!!!
152 // So better to turn this off
153 // ---
154 // This is wrong since mangos use transactions,
155 // autocommit is turned of during it.
156 // Setting it to on makes atomic updates work
157 if (!mysql_autocommit(mMysql, 1))
158 sLog.outDetail("AUTOCOMMIT SUCCESSFULLY SET TO 1");
159 else
160 sLog.outDetail("AUTOCOMMIT NOT SET TO 1");
161 /*-------------------------------------*/
163 // set connection properties to UTF8 to properly handle locales for different
164 // server configs - core sends data in UTF8, so MySQL must expect UTF8 too
165 PExecute("SET NAMES `utf8`");
166 PExecute("SET CHARACTER SET `utf8`");
168 return true;
170 else
172 sLog.outError( "Could not connect to MySQL database at %s: %s\n",
173 host.c_str(),mysql_error(mysqlInit));
174 mysql_close(mysqlInit);
175 return false;
179 QueryResult* DatabaseMysql::Query(const char *sql)
181 if (!mMysql)
182 return 0;
184 MYSQL_RES *result = 0;
185 uint64 rowCount = 0;
186 uint32 fieldCount = 0;
189 // guarded block for thread-safe mySQL request
190 ZThread::Guard<ZThread::FastMutex> query_connection_guard(mMutex);
191 #ifdef MANGOS_DEBUG
192 uint32 _s = getMSTime();
193 #endif
194 if(mysql_query(mMysql, sql))
196 sLog.outErrorDb( "SQL: %s", sql );
197 sLog.outErrorDb("query ERROR: %s", mysql_error(mMysql));
198 return NULL;
200 else
202 #ifdef MANGOS_DEBUG
203 sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql );
204 #endif
207 result = mysql_store_result(mMysql);
209 rowCount = mysql_affected_rows(mMysql);
210 fieldCount = mysql_field_count(mMysql);
211 // end guarded block
214 if (!result )
215 return NULL;
217 if (!rowCount)
219 mysql_free_result(result);
220 return NULL;
223 QueryResultMysql *queryResult = new QueryResultMysql(result, rowCount, fieldCount);
225 queryResult->NextRow();
227 return queryResult;
230 bool DatabaseMysql::Execute(const char *sql)
232 if (!mMysql)
233 return false;
235 // don't use queued execution if it has not been initialized
236 if (!m_threadBody) return DirectExecute(sql);
238 tranThread = ZThread::ThreadImpl::current(); // owner of this transaction
239 TransactionQueues::iterator i = m_tranQueues.find(tranThread);
240 if (i != m_tranQueues.end() && i->second != NULL)
241 { // Statement for transaction
242 i->second->DelayExecute(sql);
244 else
246 // Simple sql statement
247 m_threadBody->Delay(new SqlStatement(sql));
250 return true;
253 bool DatabaseMysql::DirectExecute(const char* sql)
255 if (!mMysql)
256 return false;
259 // guarded block for thread-safe mySQL request
260 ZThread::Guard<ZThread::FastMutex> query_connection_guard(mMutex);
261 #ifdef MANGOS_DEBUG
262 uint32 _s = getMSTime();
263 #endif
264 if(mysql_query(mMysql, sql))
266 sLog.outErrorDb("SQL: %s", sql);
267 sLog.outErrorDb("SQL ERROR: %s", mysql_error(mMysql));
268 return false;
270 else
272 #ifdef MANGOS_DEBUG
273 sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql );
274 #endif
276 // end guarded block
279 return true;
282 bool DatabaseMysql::_TransactionCmd(const char *sql)
284 if (mysql_query(mMysql, sql))
286 sLog.outError("SQL: %s", sql);
287 sLog.outError("SQL ERROR: %s", mysql_error(mMysql));
288 return false;
290 else
292 DEBUG_LOG("SQL: %s", sql);
294 return true;
297 bool DatabaseMysql::BeginTransaction()
299 if (!mMysql)
300 return false;
302 // don't use queued execution if it has not been initialized
303 if (!m_threadBody)
305 if (tranThread==ZThread::ThreadImpl::current())
306 return false; // huh? this thread already started transaction
307 mMutex.acquire();
308 if (!_TransactionCmd("START TRANSACTION"))
310 mMutex.release(); // can't start transaction
311 return false;
313 return true; // transaction started
316 tranThread = ZThread::ThreadImpl::current(); // owner of this transaction
317 TransactionQueues::iterator i = m_tranQueues.find(tranThread);
318 if (i != m_tranQueues.end() && i->second != NULL)
319 // If for thread exists queue and also contains transaction
320 // delete that transaction (not allow trans in trans)
321 delete i->second;
323 m_tranQueues[tranThread] = new SqlTransaction();
325 return true;
328 bool DatabaseMysql::CommitTransaction()
330 if (!mMysql)
331 return false;
333 // don't use queued execution if it has not been initialized
334 if (!m_threadBody)
336 if (tranThread!=ZThread::ThreadImpl::current())
337 return false;
338 bool _res = _TransactionCmd("COMMIT");
339 tranThread = NULL;
340 mMutex.release();
341 return _res;
344 tranThread = ZThread::ThreadImpl::current();
345 TransactionQueues::iterator i = m_tranQueues.find(tranThread);
346 if (i != m_tranQueues.end() && i->second != NULL)
348 m_threadBody->Delay(i->second);
349 i->second = NULL;
350 return true;
352 else
353 return false;
356 bool DatabaseMysql::RollbackTransaction()
358 if (!mMysql)
359 return false;
361 // don't use queued execution if it has not been initialized
362 if (!m_threadBody)
364 if (tranThread!=ZThread::ThreadImpl::current())
365 return false;
366 bool _res = _TransactionCmd("ROLLBACK");
367 tranThread = NULL;
368 mMutex.release();
369 return _res;
372 tranThread = ZThread::ThreadImpl::current();
373 TransactionQueues::iterator i = m_tranQueues.find(tranThread);
374 if (i != m_tranQueues.end() && i->second != NULL)
376 delete i->second;
377 i->second = NULL;
379 return true;
382 unsigned long DatabaseMysql::escape_string(char *to, const char *from, unsigned long length)
384 if (!mMysql || !to || !from || !length)
385 return 0;
387 return(mysql_real_escape_string(mMysql, to, from, length));
390 void DatabaseMysql::InitDelayThread()
392 assert(!m_delayThread);
394 //New delay thread for delay execute
395 m_delayThread = new ZThread::Thread(m_threadBody = new MySQLDelayThread(this));
398 void DatabaseMysql::HaltDelayThread()
400 if (!m_threadBody || !m_delayThread) return;
402 m_threadBody->Stop(); //Stop event
403 m_delayThread->wait(); //Wait for flush to DB
404 delete m_delayThread; //This also deletes m_threadBody
405 m_delayThread = NULL;
406 m_threadBody = NULL;
408 #endif