2010-10-07 15:35:36 +02:00
/*
* Copyright (C) 2008-2010 TrinityCore <http://www.trinitycore.org/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
2010-08-18 02:25:52 +02:00
*/
2010-08-21 09:53:04 +02:00
#include "Common.h"
2010-08-26 16:40:28 +02:00
#ifdef _WIN32
#include <winsock2.h>
#endif
2010-08-18 07:17:04 +02:00
#include <mysql.h>
2010-08-21 09:53:04 +02:00
2010-09-03 00:04:14 +02:00
#include "MySQLConnection.h"
2010-09-03 00:44:27 +02:00
#include "MySQLThreading.h"
2010-08-18 07:17:04 +02:00
#include "QueryResult.h"
#include "SQLOperation.h"
2010-09-02 20:54:43 +02:00
#include "PreparedStatement.h"
2010-08-18 02:25:52 +02:00
#include "DatabaseWorker.h"
2010-08-18 07:17:04 +02:00
#include "Timer.h"
2010-11-19 13:13:07 +01:00
#include "Log.h"
2010-08-18 02:25:52 +02:00
2010-09-30 20:32:09 +02:00
MySQLConnection :: MySQLConnection ( MySQLConnectionInfo & connInfo ) :
2010-08-21 20:08:47 +02:00
m_queue ( NULL ),
2010-12-13 09:18:49 +01:00
m_worker ( NULL ),
2010-10-01 09:10:07 +02:00
m_Mysql ( NULL ),
m_connectionInfo ( connInfo )
2010-08-18 02:25:52 +02:00
{
}
2010-12-13 09:18:49 +01:00
MySQLConnection :: MySQLConnection ( ACE_Activation_Queue * queue , MySQLConnectionInfo & connInfo ) :
m_queue ( queue ),
m_Mysql ( NULL ),
m_connectionInfo ( connInfo )
{
m_worker = new DatabaseWorker ( m_queue , this );
}
2010-08-18 02:25:52 +02:00
MySQLConnection ::~ MySQLConnection ()
{
2010-09-27 00:20:56 +02:00
ASSERT ( m_Mysql ); /// MySQL context must be present at this point
sLog . outSQLDriver ( "MySQLConnection::~MySQLConnection()" );
2010-09-02 20:54:43 +02:00
for ( size_t i = 0 ; i < m_stmts . size (); ++ i )
delete m_stmts [ i ];
2010-08-19 15:47:29 +02:00
mysql_close ( m_Mysql );
2010-09-27 00:20:56 +02:00
Unlock (); /// Unlock while we die, how ironic
}
void MySQLConnection :: Close ()
{
/// Only close us if we're not operating
delete this ;
2010-08-18 02:25:52 +02:00
}
2010-09-29 15:29:57 +02:00
bool MySQLConnection :: Open ()
2010-08-18 02:25:52 +02:00
{
MYSQL * mysqlInit ;
mysqlInit = mysql_init ( NULL );
if ( ! mysqlInit )
{
2010-09-29 15:29:57 +02:00
sLog . outError ( "Could not initialize Mysql connection to database `%s`" , m_connectionInfo . database . c_str ());
2010-08-18 02:25:52 +02:00
return false ;
}
int port ;
2010-09-29 14:59:05 +02:00
char const * unix_socket ;
2010-08-18 02:25:52 +02:00
mysql_options ( mysqlInit , MYSQL_SET_CHARSET_NAME , "utf8" );
#ifdef _WIN32
2010-09-29 15:29:57 +02:00
if ( m_connectionInfo . host == "." ) // named pipe use option (Windows)
2010-08-18 02:25:52 +02:00
{
unsigned int opt = MYSQL_PROTOCOL_PIPE ;
mysql_options ( mysqlInit , MYSQL_OPT_PROTOCOL , ( char const * ) & opt );
port = 0 ;
unix_socket = 0 ;
}
else // generic case
{
2010-09-29 15:29:57 +02:00
port = atoi ( m_connectionInfo . port_or_socket . c_str ());
2010-08-18 02:25:52 +02:00
unix_socket = 0 ;
}
#else
2010-09-29 15:29:57 +02:00
if ( m_connectionInfo . host == "." ) // socket use option (Unix/Linux)
2010-08-18 02:25:52 +02:00
{
unsigned int opt = MYSQL_PROTOCOL_SOCKET ;
mysql_options ( mysqlInit , MYSQL_OPT_PROTOCOL , ( char const * ) & opt );
2010-09-30 20:32:09 +02:00
m_connectionInfo . host = "localhost" ;
2010-08-18 02:25:52 +02:00
port = 0 ;
2010-09-29 15:29:57 +02:00
unix_socket = m_connectionInfo . port_or_socket . c_str ();
2010-08-18 02:25:52 +02:00
}
else // generic case
{
2010-09-29 15:29:57 +02:00
port = atoi ( m_connectionInfo . port_or_socket . c_str ());
2010-08-18 02:25:52 +02:00
unix_socket = 0 ;
}
#endif
2010-09-29 15:29:57 +02:00
m_Mysql = mysql_real_connect ( mysqlInit , m_connectionInfo . host . c_str (), m_connectionInfo . user . c_str (),
m_connectionInfo . password . c_str (), m_connectionInfo . database . c_str (), port , unix_socket , 0 );
2010-08-18 02:25:52 +02:00
if ( m_Mysql )
{
2010-08-26 21:50:54 +02:00
sLog . outSQLDriver ( "MySQL client library: %s" , mysql_get_client_info ());
2010-09-12 15:57:36 +02:00
sLog . outSQLDriver ( "MySQL server ver: %s " , mysql_get_server_info ( m_Mysql ));
if ( mysql_get_server_version ( m_Mysql ) != mysql_get_client_version ())
sLog . outSQLDriver ( "[WARNING] MySQL client/server version mismatch; may conflict with behaviour of prepared statements." );
2010-08-18 02:25:52 +02:00
2010-09-29 15:29:57 +02:00
sLog . outDetail ( "Connected to MySQL database at %s" , m_connectionInfo . host . c_str ());
2010-08-18 02:25:52 +02:00
if ( ! mysql_autocommit ( m_Mysql , 1 ))
2010-08-26 21:50:54 +02:00
sLog . outSQLDriver ( "AUTOCOMMIT SUCCESSFULLY SET TO 1" );
2010-08-18 02:25:52 +02:00
else
2010-08-26 21:50:54 +02:00
sLog . outSQLDriver ( "AUTOCOMMIT NOT SET TO 1" );
2010-08-18 02:25:52 +02:00
// set connection properties to UTF8 to properly handle locales for different
// server configs - core sends data in UTF8, so MySQL must expect UTF8 too
Execute ( "SET NAMES `utf8`" );
Execute ( "SET CHARACTER SET `utf8`" );
#if MYSQL_VERSION_ID >= 50003
my_bool my_true = ( my_bool ) 1 ;
if ( mysql_options ( m_Mysql , MYSQL_OPT_RECONNECT , & my_true ))
2010-08-26 21:50:54 +02:00
sLog . outSQLDriver ( "Failed to turn on MYSQL_OPT_RECONNECT." );
2010-08-18 02:25:52 +02:00
else
2010-08-26 21:50:54 +02:00
sLog . outSQLDriver ( "Successfully turned on MYSQL_OPT_RECONNECT." );
2010-08-18 02:25:52 +02:00
#else
#warning "Your mySQL client lib version does not support reconnecting after a timeout.\nIf this causes you any trouble we advice you to upgrade your mySQL client libs to at least mySQL 5.0.13 to resolve this problem."
#endif
return true ;
}
else
{
2010-09-29 15:29:57 +02:00
sLog . outError ( "Could not connect to MySQL database at %s: %s \n " , m_connectionInfo . host . c_str (), mysql_error ( mysqlInit ));
2010-08-18 02:25:52 +02:00
mysql_close ( mysqlInit );
return false ;
}
}
bool MySQLConnection :: Execute ( const char * sql )
{
if ( ! m_Mysql )
return false ;
{
2010-11-22 10:10:46 +01:00
uint32 _s = 0 ;
2010-11-19 13:13:07 +01:00
if ( sLog . GetSQLDriverQueryLogging ())
_s = getMSTime ();
2010-08-18 02:25:52 +02:00
if ( mysql_query ( m_Mysql , sql ))
{
2010-08-26 21:50:54 +02:00
sLog . outSQLDriver ( "SQL: %s" , sql );
sLog . outSQLDriver ( "SQL ERROR: %s" , mysql_error ( m_Mysql ));
2010-08-18 02:25:52 +02:00
return false ;
}
2010-11-19 13:13:07 +01:00
else if ( sLog . GetSQLDriverQueryLogging ())
2010-08-18 02:25:52 +02:00
{
2010-08-26 21:50:54 +02:00
sLog . outSQLDriver ( "[%u ms] SQL: %s" , getMSTimeDiff ( _s , getMSTime ()), sql );
2010-08-18 02:25:52 +02:00
}
}
return true ;
}
2010-09-02 20:54:43 +02:00
bool MySQLConnection :: Execute ( PreparedStatement * stmt )
{
if ( ! m_Mysql )
return false ;
uint32 index = stmt -> m_index ;
{
2010-09-05 10:34:46 +02:00
MySQLPreparedStatement * m_mStmt = GetPreparedStatement ( index );
ASSERT ( m_mStmt ); // Can only be null if preparation failed, server side error or bad query
m_mStmt -> m_stmt = stmt ; // Cross reference them for debug output
stmt -> m_stmt = m_mStmt ; // TODO: Cleaner way
2010-09-12 01:40:27 +02:00
2010-09-02 20:54:43 +02:00
stmt -> BindParameters ();
MYSQL_STMT * msql_STMT = m_mStmt -> GetSTMT ();
MYSQL_BIND * msql_BIND = m_mStmt -> GetBind ();
2010-11-22 10:10:46 +01:00
uint32 _s = 0 ;
2010-11-19 13:13:07 +01:00
if ( sLog . GetSQLDriverQueryLogging ())
_s = getMSTime ();
2010-09-02 20:54:43 +02:00
if ( mysql_stmt_bind_param ( msql_STMT , msql_BIND ))
{
2010-10-07 14:00:52 +02:00
sLog . outSQLDriver ( "[ERROR]: PreparedStatement (id: %u, database: `%s`) error binding params: %s" ,
2010-09-29 15:29:57 +02:00
index , m_connectionInfo . database . c_str (), mysql_stmt_error ( msql_STMT ));
2010-09-02 20:54:43 +02:00
m_mStmt -> ClearParameters ();
return false ;
}
if ( mysql_stmt_execute ( msql_STMT ))
{
2010-10-07 14:00:52 +02:00
sLog . outSQLDriver ( "[ERROR]: PreparedStatement (id: %u, database: `%s`) error executing: %s" ,
2010-09-29 15:29:57 +02:00
index , m_connectionInfo . database . c_str (), mysql_stmt_error ( msql_STMT ));
2010-09-02 20:54:43 +02:00
m_mStmt -> ClearParameters ();
return false ;
}
2010-11-19 13:13:07 +01:00
if ( sLog . GetSQLDriverQueryLogging ())
sLog . outSQLDriver ( "[%u ms] Prepared SQL: %u on database `%s`" ,
getMSTimeDiff ( _s , getMSTime ()), index , m_connectionInfo . database . c_str ());
2010-09-24 22:16:21 +02:00
m_mStmt -> ClearParameters ();
return true ;
}
}
2010-09-28 14:03:35 +02:00
bool MySQLConnection :: _Query ( PreparedStatement * stmt , MYSQL_RES ** pResult , uint64 * pRowCount , uint32 * pFieldCount )
2010-09-24 22:16:21 +02:00
{
if ( ! m_Mysql )
return false ;
uint32 index = stmt -> m_index ;
{
MySQLPreparedStatement * m_mStmt = GetPreparedStatement ( index );
ASSERT ( m_mStmt ); // Can only be null if preparation failed, server side error or bad query
m_mStmt -> m_stmt = stmt ; // Cross reference them for debug output
stmt -> m_stmt = m_mStmt ; // TODO: Cleaner way
stmt -> BindParameters ();
MYSQL_STMT * msql_STMT = m_mStmt -> GetSTMT ();
MYSQL_BIND * msql_BIND = m_mStmt -> GetBind ();
2010-11-22 10:10:46 +01:00
uint32 _s = 0 ;
2010-11-19 13:13:07 +01:00
if ( sLog . GetSQLDriverQueryLogging ())
_s = getMSTime ();
2010-09-24 22:16:21 +02:00
if ( mysql_stmt_bind_param ( msql_STMT , msql_BIND ))
2010-09-02 20:54:43 +02:00
{
2010-10-07 14:00:52 +02:00
sLog . outSQLDriver ( "[ERROR]: PreparedStatement (id: %u, database: `%s`) error binding params: %s" ,
2010-09-29 15:29:57 +02:00
index , m_connectionInfo . database . c_str (), mysql_stmt_error ( msql_STMT ));
2010-09-24 22:16:21 +02:00
m_mStmt -> ClearParameters ();
return false ;
}
if ( mysql_stmt_execute ( msql_STMT ))
{
2010-10-07 14:00:52 +02:00
sLog . outSQLDriver ( "[ERROR]: PreparedStatement (id: %u, database: `%s`) error executing: %s" ,
2010-09-29 15:29:57 +02:00
index , m_connectionInfo . database . c_str (), mysql_stmt_error ( msql_STMT ));
2010-09-02 20:54:43 +02:00
m_mStmt -> ClearParameters ();
2010-09-24 22:16:21 +02:00
return false ;
2010-09-02 20:54:43 +02:00
}
2010-09-24 22:16:21 +02:00
2010-11-19 13:13:07 +01:00
if ( sLog . GetSQLDriverQueryLogging ())
sLog . outSQLDriver ( "[%u ms] Prepared SQL: %u on database `%s`" ,
getMSTimeDiff ( _s , getMSTime ()), index , m_connectionInfo . database . c_str ());
2010-09-24 22:16:21 +02:00
m_mStmt -> ClearParameters ();
* pResult = mysql_stmt_result_metadata ( msql_STMT );
2010-09-28 14:03:35 +02:00
* pRowCount = mysql_stmt_num_rows ( msql_STMT );
2010-09-24 22:16:21 +02:00
* pFieldCount = mysql_stmt_field_count ( msql_STMT );
return true ;
2010-09-02 20:54:43 +02:00
}
}
2010-09-19 12:16:29 +02:00
ResultSet * MySQLConnection :: Query ( const char * sql )
2010-08-18 02:25:52 +02:00
{
if ( ! sql )
2010-09-19 12:16:29 +02:00
return NULL ;
2010-08-18 02:25:52 +02:00
MYSQL_RES * result = NULL ;
MYSQL_FIELD * fields = NULL ;
uint64 rowCount = 0 ;
uint32 fieldCount = 0 ;
if ( ! _Query ( sql , & result , & fields , & rowCount , & fieldCount ))
2010-09-19 12:16:29 +02:00
return NULL ;
2010-08-18 02:25:52 +02:00
2010-09-19 12:16:29 +02:00
return new ResultSet ( result , fields , rowCount , fieldCount );
2010-08-18 02:25:52 +02:00
}
bool MySQLConnection :: _Query ( const char * sql , MYSQL_RES ** pResult , MYSQL_FIELD ** pFields , uint64 * pRowCount , uint32 * pFieldCount )
{
if ( ! m_Mysql )
return false ;
{
2010-11-22 10:10:46 +01:00
uint32 _s = 0 ;
2010-11-19 13:13:07 +01:00
if ( sLog . GetSQLDriverQueryLogging ())
_s = getMSTime ();
2010-08-18 02:25:52 +02:00
if ( mysql_query ( m_Mysql , sql ))
{
2010-08-26 21:50:54 +02:00
sLog . outSQLDriver ( "SQL: %s" , sql );
sLog . outSQLDriver ( "query ERROR: %s" , mysql_error ( m_Mysql ));
2010-08-18 02:25:52 +02:00
return false ;
}
2010-11-19 13:13:07 +01:00
else if ( sLog . GetSQLDriverQueryLogging ())
2010-08-18 02:25:52 +02:00
{
2010-08-26 21:50:54 +02:00
sLog . outSQLDriver ( "[%u ms] SQL: %s" , getMSTimeDiff ( _s , getMSTime ()), sql );
2010-08-18 02:25:52 +02:00
}
* pResult = mysql_store_result ( m_Mysql );
* pRowCount = mysql_affected_rows ( m_Mysql );
* pFieldCount = mysql_field_count ( m_Mysql );
}
if ( !* pResult )
return false ;
if ( !* pRowCount )
{
mysql_free_result ( * pResult );
return false ;
}
* pFields = mysql_fetch_fields ( * pResult );
return true ;
}
void MySQLConnection :: BeginTransaction ()
{
Execute ( "START TRANSACTION" );
}
void MySQLConnection :: RollbackTransaction ()
{
Execute ( "ROLLBACK" );
}
void MySQLConnection :: CommitTransaction ()
{
Execute ( "COMMIT" );
2010-08-18 07:17:04 +02:00
}
2010-09-02 20:54:43 +02:00
MySQLPreparedStatement * MySQLConnection :: GetPreparedStatement ( uint32 index )
{
2010-09-03 17:30:23 +02:00
ASSERT ( index < m_stmts . size ());
2010-09-02 20:54:43 +02:00
return m_stmts [ index ];
}
void MySQLConnection :: PrepareStatement ( uint32 index , const char * sql )
{
MYSQL_STMT * stmt = mysql_stmt_init ( m_Mysql );
if ( ! stmt )
{
sLog . outSQLDriver ( "[ERROR]: In mysql_stmt_init() id: %u, sql: \" %s \" " , index , sql );
sLog . outSQLDriver ( "[ERROR]: %s" , mysql_error ( m_Mysql ));
2010-09-20 20:53:32 +02:00
exit ( 1 );
2010-09-02 20:54:43 +02:00
}
if ( mysql_stmt_prepare ( stmt , sql , static_cast < unsigned long > ( strlen ( sql ))))
{
2010-09-19 10:36:48 +02:00
sLog . outSQLDriver ( "[ERROR]: In mysql_stmt_prepare() id: %u, sql: \" %s \" " , index , sql );
2010-09-20 20:53:32 +02:00
sLog . outSQLDriver ( "[ERROR]: %s" , mysql_stmt_error ( stmt ));
mysql_stmt_close ( stmt );
exit ( 1 );
2010-09-02 20:54:43 +02:00
}
MySQLPreparedStatement * mStmt = new MySQLPreparedStatement ( stmt );
m_stmts [ index ] = mStmt ;
}
2010-09-11 09:17:14 +02:00
PreparedResultSet * MySQLConnection :: Query ( PreparedStatement * stmt )
{
2010-09-24 22:16:21 +02:00
MYSQL_RES * result = NULL ;
uint64 rowCount = 0 ;
uint32 fieldCount = 0 ;
2010-09-28 14:03:35 +02:00
if ( ! _Query ( stmt , & result , & rowCount , & fieldCount ))
2010-09-24 22:16:21 +02:00
return NULL ;
2010-09-11 09:17:14 +02:00
if ( mysql_more_results ( m_Mysql ))
{
mysql_next_result ( m_Mysql );
}
2010-09-28 14:03:35 +02:00
return new PreparedResultSet ( stmt -> m_stmt -> GetSTMT (), result , rowCount , fieldCount );
2010-09-17 20:36:29 +02:00
}