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

Revision 2, 11.0 kB (checked in by yumileroy, 17 years ago)

[svn] * Proper SVN structure

Original author: Neo2003
Date: 2008-10-02 16:23:55-05:00

Line 
1/*
2 * Copyright (C) 2005-2008 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
19#ifndef DO_POSTGRESQL
20
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"
29
30void DatabaseMysql::ThreadStart()
31{
32    mysql_thread_init();
33}
34
35void DatabaseMysql::ThreadEnd()
36{
37    mysql_thread_end();
38}
39
40size_t DatabaseMysql::db_count = 0;
41
42DatabaseMysql::DatabaseMysql() : Database(), mMysql(0)
43{
44    // before first connection
45    if( db_count++ == 0 )
46    {
47        // Mysql Library Init
48        mysql_library_init(-1, NULL, NULL);
49
50        if (!mysql_thread_safe())
51        {
52            sLog.outError("FATAL ERROR: Used MySQL library isn't thread-safe.");
53            exit(1);
54        }
55    }
56}
57
58DatabaseMysql::~DatabaseMysql()
59{
60    if (m_delayThread)
61        HaltDelayThread();
62
63    if (mMysql)
64        mysql_close(mMysql);
65
66    //Free Mysql library pointers for last ~DB
67    if(--db_count == 0)
68        mysql_library_end();
69}
70
71bool DatabaseMysql::Initialize(const char *infoString)
72{
73
74    if(!Database::Initialize(infoString))
75        return false;
76
77    tranThread = NULL;
78    MYSQL *mysqlInit;
79    mysqlInit = mysql_init(NULL);
80    if (!mysqlInit)
81    {
82        sLog.outError( "Could not initialize Mysql connection" );
83        return false;
84    }
85
86    InitDelayThread();
87
88    Tokens tokens = StrSplit(infoString, ";");
89
90    Tokens::iterator iter;
91
92    std::string host, port_or_socket, user, password, database;
93    int port;
94    char const* unix_socket;
95
96    iter = tokens.begin();
97
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++;
108
109    mysql_options(mysqlInit,MYSQL_SET_CHARSET_NAME,"utf8");
110    #ifdef WIN32
111    if(host==".")                                           // named pipe use option (Windows)
112    {
113        unsigned int opt = MYSQL_PROTOCOL_PIPE;
114        mysql_options(mysqlInit,MYSQL_OPT_PROTOCOL,(char const*)&opt);
115        port = 0;
116        unix_socket = 0;
117    }
118    else                                                    // generic case
119    {
120        port = atoi(port_or_socket.c_str());
121        unix_socket = 0;
122    }
123    #else
124    if(host==".")                                           // socket use option (Unix/Linux)
125    {
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();
131    }
132    else                                                    // generic case
133    {
134        port = atoi(port_or_socket.c_str());
135        unix_socket = 0;
136    }
137    #endif
138
139    mMysql = mysql_real_connect(mysqlInit, host.c_str(), user.c_str(),
140        password.c_str(), database.c_str(), port, unix_socket, 0);
141
142    if (mMysql)
143    {
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));
148
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        /*-------------------------------------*/
162
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`");
167
168        return true;
169    }
170    else
171    {
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;
176    }
177}
178
179QueryResult* DatabaseMysql::Query(const char *sql)
180{
181    if (!mMysql)
182        return 0;
183
184    MYSQL_RES *result = 0;
185    uint64 rowCount = 0;
186    uint32 fieldCount = 0;
187
188    {
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))
195        {
196            sLog.outErrorDb( "SQL: %s", sql );
197            sLog.outErrorDb("query ERROR: %s", mysql_error(mMysql));
198            return NULL;
199        }
200        else
201        {
202            #ifdef MANGOS_DEBUG
203            sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql );
204            #endif
205        }
206
207        result = mysql_store_result(mMysql);
208
209        rowCount = mysql_affected_rows(mMysql);
210        fieldCount = mysql_field_count(mMysql);
211        // end guarded block
212    }
213
214    if (!result )
215        return NULL;
216
217    if (!rowCount)
218    {
219        mysql_free_result(result);
220        return NULL;
221    }
222
223    QueryResultMysql *queryResult = new QueryResultMysql(result, rowCount, fieldCount);
224
225    queryResult->NextRow();
226
227    return queryResult;
228}
229
230bool DatabaseMysql::Execute(const char *sql)
231{
232    if (!mMysql)
233        return false;
234
235    // don't use queued execution if it has not been initialized
236    if (!m_threadBody) return DirectExecute(sql);
237
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);
243    }
244    else
245    {
246        // Simple sql statement
247        m_threadBody->Delay(new SqlStatement(sql));
248    }
249
250    return true;
251}
252
253bool DatabaseMysql::DirectExecute(const char* sql)
254{
255    if (!mMysql)
256        return false;
257
258    {
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))
265        {
266            sLog.outErrorDb("SQL: %s", sql);
267            sLog.outErrorDb("SQL ERROR: %s", mysql_error(mMysql));
268            return false;
269        }
270        else
271        {
272            #ifdef MANGOS_DEBUG
273            sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql );
274            #endif
275        }
276        // end guarded block
277    }
278
279    return true;
280}
281
282bool DatabaseMysql::_TransactionCmd(const char *sql)
283{
284    if (mysql_query(mMysql, sql))
285    {
286        sLog.outError("SQL: %s", sql);
287        sLog.outError("SQL ERROR: %s", mysql_error(mMysql));
288        return false;
289    }
290    else
291    {
292        DEBUG_LOG("SQL: %s", sql);
293    }
294    return true;
295}
296
297bool DatabaseMysql::BeginTransaction()
298{
299    if (!mMysql)
300        return false;
301
302    // don't use queued execution if it has not been initialized
303    if (!m_threadBody)
304    {
305        if (tranThread==ZThread::ThreadImpl::current())
306            return false;                                   // huh? this thread already started transaction
307        mMutex.acquire();
308        if (!_TransactionCmd("START TRANSACTION"))
309        {
310            mMutex.release();                               // can't start transaction
311            return false;
312        }
313        return true;                                        // transaction started
314    }
315
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;
322
323    m_tranQueues[tranThread] = new SqlTransaction();
324
325    return true;
326}
327
328bool DatabaseMysql::CommitTransaction()
329{
330    if (!mMysql)
331        return false;
332
333    // don't use queued execution if it has not been initialized
334    if (!m_threadBody)
335    {
336        if (tranThread!=ZThread::ThreadImpl::current())
337            return false;
338        bool _res = _TransactionCmd("COMMIT");
339        tranThread = NULL;
340        mMutex.release();
341        return _res;
342    }
343
344    tranThread = ZThread::ThreadImpl::current();
345    TransactionQueues::iterator i = m_tranQueues.find(tranThread);
346    if (i != m_tranQueues.end() && i->second != NULL)
347    {
348        m_threadBody->Delay(i->second);
349        i->second = NULL;
350        return true;
351    }
352    else
353        return false;
354}
355
356bool DatabaseMysql::RollbackTransaction()
357{
358    if (!mMysql)
359        return false;
360
361    // don't use queued execution if it has not been initialized
362    if (!m_threadBody)
363    {
364        if (tranThread!=ZThread::ThreadImpl::current())
365            return false;
366        bool _res = _TransactionCmd("ROLLBACK");
367        tranThread = NULL;
368        mMutex.release();
369        return _res;
370    }
371
372    tranThread = ZThread::ThreadImpl::current();
373    TransactionQueues::iterator i = m_tranQueues.find(tranThread);
374    if (i != m_tranQueues.end() && i->second != NULL)
375    {
376        delete i->second;
377        i->second = NULL;
378    }
379    return true;
380}
381
382unsigned long DatabaseMysql::escape_string(char *to, const char *from, unsigned long length)
383{
384    if (!mMysql || !to || !from || !length)
385        return 0;
386
387    return(mysql_real_escape_string(mMysql, to, from, length));
388}
389
390void DatabaseMysql::InitDelayThread()
391{
392    assert(!m_delayThread);
393
394    //New delay thread for delay execute
395    m_delayThread = new ZThread::Thread(m_threadBody = new MySQLDelayThread(this));
396}
397
398void DatabaseMysql::HaltDelayThread()
399{
400    if (!m_threadBody || !m_delayThread) return;
401
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;
407}
408#endif
Note: See TracBrowser for help on using the browser.