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
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"
30 void DatabaseMysql::ThreadStart()
35 void DatabaseMysql::ThreadEnd()
40 size_t DatabaseMysql::db_count
= 0;
42 DatabaseMysql::DatabaseMysql() : Database(), mMysql(0)
44 // before first connection
48 mysql_library_init(-1, NULL
, NULL
);
50 if (!mysql_thread_safe())
52 sLog
.outError("FATAL ERROR: Used MySQL library isn't thread-safe.");
58 DatabaseMysql::~DatabaseMysql()
66 //Free Mysql library pointers for last ~DB
71 bool DatabaseMysql::Initialize(const char *infoString
)
74 if(!Database::Initialize(infoString
))
79 mysqlInit
= mysql_init(NULL
);
82 sLog
.outError( "Could not initialize Mysql connection" );
88 Tokens tokens
= StrSplit(infoString
, ";");
90 Tokens::iterator iter
;
92 std::string host
, port_or_socket
, user
, password
, database
;
94 char const* unix_socket
;
96 iter
= tokens
.begin();
98 if(iter
!= tokens
.end())
100 if(iter
!= tokens
.end())
101 port_or_socket
= *iter
++;
102 if(iter
!= tokens
.end())
104 if(iter
!= tokens
.end())
106 if(iter
!= tokens
.end())
109 mysql_options(mysqlInit
,MYSQL_SET_CHARSET_NAME
,"utf8");
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
);
120 port
= atoi(port_or_socket
.c_str());
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
);
130 unix_socket
= port_or_socket
.c_str();
134 port
= atoi(port_or_socket
.c_str());
139 mMysql
= mysql_real_connect(mysqlInit
, host
.c_str(), user
.c_str(),
140 password
.c_str(), database
.c_str(), port
, unix_socket
, 0);
144 sLog
.outDetail( "Connected to MySQL database at %s",
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
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");
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`");
172 sLog
.outError( "Could not connect to MySQL database at %s: %s\n",
173 host
.c_str(),mysql_error(mysqlInit
));
174 mysql_close(mysqlInit
);
179 QueryResult
* DatabaseMysql::Query(const char *sql
)
184 MYSQL_RES
*result
= 0;
186 uint32 fieldCount
= 0;
189 // guarded block for thread-safe mySQL request
190 ZThread::Guard
<ZThread::FastMutex
> query_connection_guard(mMutex
);
192 uint32 _s
= getMSTime();
194 if(mysql_query(mMysql
, sql
))
196 sLog
.outErrorDb( "SQL: %s", sql
);
197 sLog
.outErrorDb("query ERROR: %s", mysql_error(mMysql
));
203 sLog
.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s
,getMSTime()), sql
);
207 result
= mysql_store_result(mMysql
);
209 rowCount
= mysql_affected_rows(mMysql
);
210 fieldCount
= mysql_field_count(mMysql
);
219 mysql_free_result(result
);
223 QueryResultMysql
*queryResult
= new QueryResultMysql(result
, rowCount
, fieldCount
);
225 queryResult
->NextRow();
230 bool DatabaseMysql::Execute(const char *sql
)
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
);
246 // Simple sql statement
247 m_threadBody
->Delay(new SqlStatement(sql
));
253 bool DatabaseMysql::DirectExecute(const char* sql
)
259 // guarded block for thread-safe mySQL request
260 ZThread::Guard
<ZThread::FastMutex
> query_connection_guard(mMutex
);
262 uint32 _s
= getMSTime();
264 if(mysql_query(mMysql
, sql
))
266 sLog
.outErrorDb("SQL: %s", sql
);
267 sLog
.outErrorDb("SQL ERROR: %s", mysql_error(mMysql
));
273 sLog
.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s
,getMSTime()), sql
);
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
));
292 DEBUG_LOG("SQL: %s", sql
);
297 bool DatabaseMysql::BeginTransaction()
302 // don't use queued execution if it has not been initialized
305 if (tranThread
==ZThread::ThreadImpl::current())
306 return false; // huh? this thread already started transaction
308 if (!_TransactionCmd("START TRANSACTION"))
310 mMutex
.release(); // can't start transaction
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)
323 m_tranQueues
[tranThread
] = new SqlTransaction();
328 bool DatabaseMysql::CommitTransaction()
333 // don't use queued execution if it has not been initialized
336 if (tranThread
!=ZThread::ThreadImpl::current())
338 bool _res
= _TransactionCmd("COMMIT");
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
);
356 bool DatabaseMysql::RollbackTransaction()
361 // don't use queued execution if it has not been initialized
364 if (tranThread
!=ZThread::ThreadImpl::current())
366 bool _res
= _TransactionCmd("ROLLBACK");
372 tranThread
= ZThread::ThreadImpl::current();
373 TransactionQueues::iterator i
= m_tranQueues
.find(tranThread
);
374 if (i
!= m_tranQueues
.end() && i
->second
!= NULL
)
382 unsigned long DatabaseMysql::escape_string(char *to
, const char *from
, unsigned long length
)
384 if (!mMysql
|| !to
|| !from
|| !length
)
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
;