root/trunk/src/shared/Database/DatabaseMysql.cpp @ 102

Revision 102, 11.1 kB (checked in by yumileroy, 17 years ago)

[svn] Fixed copyright notices to comply with GPL.

Original author: w12x
Date: 2008-10-23 03:29:52-05:00

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