2010-10-07 15:35:36 +02:00
/*
2012-12-31 23:15:50 +01:00
* Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
2010-10-07 15:35:36 +02:00
*
* 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-09-02 20:54:43 +02:00
*/
#include "PreparedStatement.h"
#include "MySQLConnection.h"
2010-09-02 17:19:53 -06:00
#include "Log.h"
2010-09-02 20:54:43 +02:00
PreparedStatement :: PreparedStatement ( uint32 index ) :
2010-09-06 15:23:08 +02:00
m_stmt ( NULL ),
2013-10-28 14:36:07 -04:00
m_index ( index ) { }
2010-09-02 20:54:43 +02:00
2013-10-28 14:36:07 -04:00
PreparedStatement ::~ PreparedStatement () { }
2010-09-02 20:54:43 +02:00
void PreparedStatement :: BindParameters ()
{
ASSERT ( m_stmt );
2013-01-02 14:20:20 +01:00
uint8 i = 0 ;
2010-09-02 20:54:43 +02:00
for (; i < statement_data . size (); i ++ )
{
switch ( statement_data [ i ]. type )
{
case TYPE_BOOL :
m_stmt -> setBool ( i , statement_data [ i ]. data . boolean );
break ;
case TYPE_UI8 :
2012-03-31 14:16:48 +02:00
m_stmt -> setUInt8 ( i , statement_data [ i ]. data . ui8 );
break ;
2010-09-02 20:54:43 +02:00
case TYPE_UI16 :
2012-03-31 14:16:48 +02:00
m_stmt -> setUInt16 ( i , statement_data [ i ]. data . ui16 );
break ;
2010-09-02 20:54:43 +02:00
case TYPE_UI32 :
m_stmt -> setUInt32 ( i , statement_data [ i ]. data . ui32 );
break ;
case TYPE_I8 :
2012-03-31 14:16:48 +02:00
m_stmt -> setInt8 ( i , statement_data [ i ]. data . i8 );
break ;
2010-09-02 20:54:43 +02:00
case TYPE_I16 :
2012-03-31 14:16:48 +02:00
m_stmt -> setInt16 ( i , statement_data [ i ]. data . i16 );
break ;
2010-09-02 20:54:43 +02:00
case TYPE_I32 :
m_stmt -> setInt32 ( i , statement_data [ i ]. data . i32 );
break ;
case TYPE_UI64 :
m_stmt -> setUInt64 ( i , statement_data [ i ]. data . ui64 );
break ;
case TYPE_I64 :
m_stmt -> setInt64 ( i , statement_data [ i ]. data . i64 );
break ;
case TYPE_FLOAT :
2010-09-19 15:25:50 +02:00
m_stmt -> setFloat ( i , statement_data [ i ]. data . f );
break ;
case TYPE_DOUBLE :
m_stmt -> setDouble ( i , statement_data [ i ]. data . d );
2010-09-02 20:54:43 +02:00
break ;
case TYPE_STRING :
m_stmt -> setString ( i , statement_data [ i ]. str . c_str ());
break ;
2013-01-02 12:22:50 +01:00
case TYPE_NULL :
m_stmt -> setNull ( i );
break ;
2010-09-02 20:54:43 +02:00
}
}
#ifdef _DEBUG
if ( i < m_stmt -> m_paramCount )
2013-05-13 15:07:36 +02:00
TC_LOG_WARN ( LOG_FILTER_SQL , "[WARNING]: BindParameters() for statement %u did not bind all allocated parameters" , m_index );
2010-09-02 20:54:43 +02:00
#endif
}
//- Bind to buffer
void PreparedStatement :: setBool ( const uint8 index , const bool value )
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
statement_data [ index ]. data . boolean = value ;
statement_data [ index ]. type = TYPE_BOOL ;
}
void PreparedStatement :: setUInt8 ( const uint8 index , const uint8 value )
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
2012-03-31 14:16:48 +02:00
statement_data [ index ]. data . ui8 = value ;
2010-09-02 20:54:43 +02:00
statement_data [ index ]. type = TYPE_UI8 ;
}
void PreparedStatement :: setUInt16 ( const uint8 index , const uint16 value )
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
2012-03-31 14:16:48 +02:00
statement_data [ index ]. data . ui16 = value ;
2010-09-02 20:54:43 +02:00
statement_data [ index ]. type = TYPE_UI16 ;
}
void PreparedStatement :: setUInt32 ( const uint8 index , const uint32 value )
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
statement_data [ index ]. data . ui32 = value ;
statement_data [ index ]. type = TYPE_UI32 ;
}
void PreparedStatement :: setUInt64 ( const uint8 index , const uint64 value )
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
statement_data [ index ]. data . ui64 = value ;
statement_data [ index ]. type = TYPE_UI64 ;
}
void PreparedStatement :: setInt8 ( const uint8 index , const int8 value )
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
2012-03-31 14:16:48 +02:00
statement_data [ index ]. data . i8 = value ;
2010-09-02 20:54:43 +02:00
statement_data [ index ]. type = TYPE_I8 ;
}
void PreparedStatement :: setInt16 ( const uint8 index , const int16 value )
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
2012-03-31 14:16:48 +02:00
statement_data [ index ]. data . i16 = value ;
2010-09-02 20:54:43 +02:00
statement_data [ index ]. type = TYPE_I16 ;
}
void PreparedStatement :: setInt32 ( const uint8 index , const int32 value )
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
statement_data [ index ]. data . i32 = value ;
statement_data [ index ]. type = TYPE_I32 ;
}
void PreparedStatement :: setInt64 ( const uint8 index , const int64 value )
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
statement_data [ index ]. data . i64 = value ;
statement_data [ index ]. type = TYPE_I64 ;
}
2010-09-19 15:25:50 +02:00
void PreparedStatement :: setFloat ( const uint8 index , const float value )
2010-09-02 20:54:43 +02:00
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
statement_data [ index ]. data . f = value ;
statement_data [ index ]. type = TYPE_FLOAT ;
}
2010-09-19 15:25:50 +02:00
void PreparedStatement :: setDouble ( const uint8 index , const double value )
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
statement_data [ index ]. data . d = value ;
statement_data [ index ]. type = TYPE_DOUBLE ;
}
2010-09-02 20:54:43 +02:00
void PreparedStatement :: setString ( const uint8 index , const std :: string & value )
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
statement_data [ index ]. str = value ;
statement_data [ index ]. type = TYPE_STRING ;
}
2013-01-02 12:22:50 +01:00
void PreparedStatement :: setNull ( const uint8 index )
{
if ( index >= statement_data . size ())
statement_data . resize ( index + 1 );
statement_data [ index ]. type = TYPE_NULL ;
}
2010-09-02 20:54:43 +02:00
MySQLPreparedStatement :: MySQLPreparedStatement ( MYSQL_STMT * stmt ) :
2013-05-17 20:39:53 +02:00
m_stmt ( NULL ),
2010-09-02 20:54:43 +02:00
m_Mstmt ( stmt ),
m_bind ( NULL )
{
/// Initialize variable parameters
m_paramCount = mysql_stmt_param_count ( stmt );
m_paramsSet . assign ( m_paramCount , false );
m_bind = new MYSQL_BIND [ m_paramCount ];
memset ( m_bind , 0 , sizeof ( MYSQL_BIND ) * m_paramCount );
/// "If set to 1, causes mysql_stmt_store_result() to update the metadata MYSQL_FIELD->max_length value."
2010-09-06 15:23:08 +02:00
my_bool bool_tmp = 1 ;
2010-09-02 20:54:43 +02:00
mysql_stmt_attr_set ( stmt , STMT_ATTR_UPDATE_MAX_LENGTH , & bool_tmp );
}
MySQLPreparedStatement ::~ MySQLPreparedStatement ()
{
ClearParameters ();
2012-06-30 16:07:09 +02:00
if ( m_Mstmt -> bind_result_done )
2012-03-24 22:59:23 +01:00
{
delete [] m_Mstmt -> bind -> length ;
delete [] m_Mstmt -> bind -> is_null ;
}
2010-09-02 20:54:43 +02:00
mysql_stmt_close ( m_Mstmt );
delete [] m_bind ;
}
void MySQLPreparedStatement :: ClearParameters ()
{
for ( uint32 i = 0 ; i < m_paramCount ; ++ i )
{
delete m_bind [ i ]. length ;
m_bind [ i ]. length = NULL ;
2010-09-06 17:55:12 +02:00
delete [] ( char * ) m_bind [ i ]. buffer ;
2010-09-02 20:54:43 +02:00
m_bind [ i ]. buffer = NULL ;
m_paramsSet [ i ] = false ;
}
}
2013-01-02 14:20:20 +01:00
static bool ParamenterIndexAssertFail ( uint32 stmtIndex , uint8 index , uint32 paramCount )
2013-01-02 12:22:50 +01:00
{
2013-05-13 15:07:36 +02:00
TC_LOG_ERROR ( LOG_FILTER_SQL_DRIVER , "Attempted to bind parameter %u%s on a PreparedStatement %u (statement has only %u parameters)" , uint32 ( index ) + 1 , ( index == 1 ? "st" : ( index == 2 ? "nd" : ( index == 3 ? "rd" : "nd" ))), stmtIndex , paramCount );
2013-01-02 12:22:50 +01:00
return false ;
}
2010-09-02 20:54:43 +02:00
//- Bind on mysql level
bool MySQLPreparedStatement :: CheckValidIndex ( uint8 index )
{
2013-01-02 14:20:20 +01:00
ASSERT ( index < m_paramCount || ParamenterIndexAssertFail ( m_stmt -> m_index , index , m_paramCount ));
2010-09-02 20:54:43 +02:00
if ( m_paramsSet [ index ])
2013-05-13 15:07:36 +02:00
TC_LOG_WARN ( LOG_FILTER_SQL , "[WARNING] Prepared Statement (id: %u) trying to bind value on already bound index (%u)." , m_stmt -> m_index , index );
2010-09-02 20:54:43 +02:00
return true ;
}
2011-04-29 20:47:02 +02:00
void MySQLPreparedStatement :: setBool ( const uint8 index , const bool value )
2010-09-02 20:54:43 +02:00
{
2012-03-31 14:16:48 +02:00
setUInt8 ( index , value ? 1 : 0 );
2010-09-02 20:54:43 +02:00
}
void MySQLPreparedStatement :: setUInt8 ( const uint8 index , const uint8 value )
{
2012-03-31 14:16:48 +02:00
CheckValidIndex ( index );
m_paramsSet [ index ] = true ;
MYSQL_BIND * param = & m_bind [ index ];
setValue ( param , MYSQL_TYPE_TINY , & value , sizeof ( uint8 ), true );
2010-09-02 20:54:43 +02:00
}
void MySQLPreparedStatement :: setUInt16 ( const uint8 index , const uint16 value )
{
2012-03-31 14:16:48 +02:00
CheckValidIndex ( index );
m_paramsSet [ index ] = true ;
MYSQL_BIND * param = & m_bind [ index ];
setValue ( param , MYSQL_TYPE_SHORT , & value , sizeof ( uint16 ), true );
2010-09-02 20:54:43 +02:00
}
void MySQLPreparedStatement :: setUInt32 ( const uint8 index , const uint32 value )
{
CheckValidIndex ( index );
m_paramsSet [ index ] = true ;
MYSQL_BIND * param = & m_bind [ index ];
setValue ( param , MYSQL_TYPE_LONG , & value , sizeof ( uint32 ), true );
}
void MySQLPreparedStatement :: setUInt64 ( const uint8 index , const uint64 value )
{
CheckValidIndex ( index );
m_paramsSet [ index ] = true ;
MYSQL_BIND * param = & m_bind [ index ];
setValue ( param , MYSQL_TYPE_LONGLONG , & value , sizeof ( uint64 ), true );
}
void MySQLPreparedStatement :: setInt8 ( const uint8 index , const int8 value )
{
2012-03-31 14:16:48 +02:00
CheckValidIndex ( index );
m_paramsSet [ index ] = true ;
MYSQL_BIND * param = & m_bind [ index ];
setValue ( param , MYSQL_TYPE_TINY , & value , sizeof ( int8 ), false );
2010-09-02 20:54:43 +02:00
}
void MySQLPreparedStatement :: setInt16 ( const uint8 index , const int16 value )
{
2012-03-31 14:16:48 +02:00
CheckValidIndex ( index );
m_paramsSet [ index ] = true ;
MYSQL_BIND * param = & m_bind [ index ];
setValue ( param , MYSQL_TYPE_SHORT , & value , sizeof ( int16 ), false );
2010-09-02 20:54:43 +02:00
}
void MySQLPreparedStatement :: setInt32 ( const uint8 index , const int32 value )
{
CheckValidIndex ( index );
m_paramsSet [ index ] = true ;
MYSQL_BIND * param = & m_bind [ index ];
setValue ( param , MYSQL_TYPE_LONG , & value , sizeof ( int32 ), false );
}
void MySQLPreparedStatement :: setInt64 ( const uint8 index , const int64 value )
{
CheckValidIndex ( index );
m_paramsSet [ index ] = true ;
MYSQL_BIND * param = & m_bind [ index ];
setValue ( param , MYSQL_TYPE_LONGLONG , & value , sizeof ( int64 ), false );
}
2010-09-19 15:25:50 +02:00
void MySQLPreparedStatement :: setFloat ( const uint8 index , const float value )
{
CheckValidIndex ( index );
m_paramsSet [ index ] = true ;
MYSQL_BIND * param = & m_bind [ index ];
2010-09-19 15:28:35 +02:00
setValue ( param , MYSQL_TYPE_FLOAT , & value , sizeof ( float ), ( value > 0.0f ));
2010-09-19 15:25:50 +02:00
}
2010-09-02 20:54:43 +02:00
void MySQLPreparedStatement :: setDouble ( const uint8 index , const double value )
{
CheckValidIndex ( index );
m_paramsSet [ index ] = true ;
MYSQL_BIND * param = & m_bind [ index ];
setValue ( param , MYSQL_TYPE_DOUBLE , & value , sizeof ( double ), ( value > 0.0f ));
}
void MySQLPreparedStatement :: setString ( const uint8 index , const char * value )
{
CheckValidIndex ( index );
m_paramsSet [ index ] = true ;
MYSQL_BIND * param = & m_bind [ index ];
size_t len = strlen ( value ) + 1 ;
param -> buffer_type = MYSQL_TYPE_VAR_STRING ;
2010-09-13 17:07:27 +02:00
delete [] static_cast < char *> ( param -> buffer );
2010-09-02 20:54:43 +02:00
param -> buffer = new char [ len ];
param -> buffer_length = len ;
param -> is_null_value = 0 ;
2013-01-02 12:22:50 +01:00
delete param -> length ;
2010-09-02 20:54:43 +02:00
param -> length = new unsigned long ( len - 1 );
memcpy ( param -> buffer , value , len );
}
2013-01-02 12:22:50 +01:00
void MySQLPreparedStatement :: setNull ( const uint8 index )
{
CheckValidIndex ( index );
m_paramsSet [ index ] = true ;
MYSQL_BIND * param = & m_bind [ index ];
param -> buffer_type = MYSQL_TYPE_NULL ;
delete [] static_cast < char *> ( param -> buffer );
param -> buffer = NULL ;
param -> buffer_length = 0 ;
param -> is_null_value = 1 ;
delete param -> length ;
param -> length = NULL ;
}
2010-09-02 20:54:43 +02:00
void MySQLPreparedStatement :: setValue ( MYSQL_BIND * param , enum_field_types type , const void * value , uint32 len , bool isUnsigned )
{
param -> buffer_type = type ;
2010-09-13 17:07:27 +02:00
delete [] static_cast < char *> ( param -> buffer );
2010-09-02 20:54:43 +02:00
param -> buffer = new char [ len ];
param -> buffer_length = 0 ;
param -> is_null_value = 0 ;
param -> length = NULL ; // Only != NULL for strings
param -> is_unsigned = isUnsigned ;
memcpy ( param -> buffer , value , len );
}
2013-01-02 14:20:20 +01:00
std :: string MySQLPreparedStatement :: getQueryString ( std :: string const & sqlPattern ) const
2011-01-26 00:41:55 +01:00
{
2013-01-02 12:22:50 +01:00
std :: string queryString = sqlPattern ;
2011-01-26 00:41:55 +01:00
2013-01-02 12:22:50 +01:00
size_t pos = 0 ;
2011-01-26 00:41:55 +01:00
for ( uint32 i = 0 ; i < m_stmt -> statement_data . size (); i ++ )
{
2013-01-02 12:22:50 +01:00
pos = queryString . find ( '?' , pos );
std :: stringstream ss ;
2011-01-26 00:41:55 +01:00
switch ( m_stmt -> statement_data [ i ]. type )
{
2011-05-13 18:07:12 +02:00
case TYPE_BOOL :
2013-01-02 12:22:50 +01:00
ss << uint16 ( m_stmt -> statement_data [ i ]. data . boolean );
2011-05-13 18:07:12 +02:00
break ;
case TYPE_UI8 :
2013-01-02 12:22:50 +01:00
ss << uint16 ( m_stmt -> statement_data [ i ]. data . ui8 ); // stringstream will append a character with that code instead of numeric representation
2012-03-31 14:16:48 +02:00
break ;
2011-05-13 18:07:12 +02:00
case TYPE_UI16 :
2013-01-02 12:22:50 +01:00
ss << m_stmt -> statement_data [ i ]. data . ui16 ;
2012-03-31 14:16:48 +02:00
break ;
2011-05-13 18:07:12 +02:00
case TYPE_UI32 :
2013-01-02 12:22:50 +01:00
ss << m_stmt -> statement_data [ i ]. data . ui32 ;
2011-05-13 18:07:12 +02:00
break ;
case TYPE_I8 :
2013-01-02 12:22:50 +01:00
ss << int16 ( m_stmt -> statement_data [ i ]. data . i8 ); // stringstream will append a character with that code instead of numeric representation
2012-03-31 14:16:48 +02:00
break ;
2011-05-13 18:07:12 +02:00
case TYPE_I16 :
2013-01-02 12:22:50 +01:00
ss << m_stmt -> statement_data [ i ]. data . i16 ;
2012-03-31 14:16:48 +02:00
break ;
2011-05-13 18:07:12 +02:00
case TYPE_I32 :
2013-01-02 12:22:50 +01:00
ss << m_stmt -> statement_data [ i ]. data . i32 ;
2011-05-13 18:07:12 +02:00
break ;
case TYPE_UI64 :
2013-01-02 12:22:50 +01:00
ss << m_stmt -> statement_data [ i ]. data . ui64 ;
2011-05-13 18:07:12 +02:00
break ;
case TYPE_I64 :
2013-01-02 12:22:50 +01:00
ss << m_stmt -> statement_data [ i ]. data . i64 ;
2011-05-13 18:07:12 +02:00
break ;
case TYPE_FLOAT :
2013-01-02 12:22:50 +01:00
ss << m_stmt -> statement_data [ i ]. data . f ;
2011-05-13 18:07:12 +02:00
break ;
case TYPE_DOUBLE :
2013-01-02 12:22:50 +01:00
ss << m_stmt -> statement_data [ i ]. data . d ;
2011-05-13 18:07:12 +02:00
break ;
case TYPE_STRING :
2013-01-02 12:22:50 +01:00
ss << '\'' << m_stmt -> statement_data [ i ]. str << '\'' ;
break ;
case TYPE_NULL :
ss << "NULL" ;
2011-05-13 18:07:12 +02:00
break ;
2011-01-26 00:41:55 +01:00
}
2013-01-02 12:22:50 +01:00
std :: string replaceStr = ss . str ();
queryString . replace ( pos , 1 , replaceStr );
pos += replaceStr . length ();
2011-01-26 00:41:55 +01:00
}
return queryString ;
}
2010-09-02 20:54:43 +02:00
//- Execution
PreparedStatementTask :: PreparedStatementTask ( PreparedStatement * stmt ) :
2010-12-23 16:05:43 +01:00
m_stmt ( stmt ),
2013-10-28 14:36:07 -04:00
m_has_result ( false ) { }
2010-09-02 20:54:43 +02:00
2010-12-23 16:05:43 +01:00
PreparedStatementTask :: PreparedStatementTask ( PreparedStatement * stmt , PreparedQueryResultFuture result ) :
m_stmt ( stmt ),
m_has_result ( true ),
2013-10-28 14:36:07 -04:00
m_result ( result ) { }
2010-12-23 16:05:43 +01:00
2010-09-02 20:54:43 +02:00
PreparedStatementTask ::~ PreparedStatementTask ()
{
delete m_stmt ;
}
bool PreparedStatementTask :: Execute ()
{
2010-12-23 16:05:43 +01:00
if ( m_has_result )
{
PreparedResultSet * result = m_conn -> Query ( m_stmt );
if ( ! result || ! result -> GetRowCount ())
{
2012-08-26 21:24:15 +02:00
delete result ;
2010-12-23 16:05:43 +01:00
m_result . set ( PreparedQueryResult ( NULL ));
return false ;
}
m_result . set ( PreparedQueryResult ( result ));
return true ;
}
2010-09-02 20:54:43 +02:00
return m_conn -> Execute ( m_stmt );
2010-09-02 17:19:53 -06:00
}