mirror of
https://github.com/mangosfour/server.git
synced 2025-12-23 07:37:01 +00:00
[11284] Implement prepared statements for INSERT+DELETE+UPDATE SQL requests. Should improve player save performance + lower MySQL server CPU usage.
Note: PostgreSQL does not have prepared statements implemented using native APIs. Huge thanks to Undergarun, kero99 and Vinolentus. Signed-off-by: Ambal <pogrebniak@gala.net>
This commit is contained in:
parent
d9374d936f
commit
40ef9cbf2f
24 changed files with 1823 additions and 488 deletions
|
|
@ -23,10 +23,77 @@
|
|||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
#define MIN_CONNECTION_POOL_SIZE 1
|
||||
#define MAX_CONNECTION_POOL_SIZE 16
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SqlPreparedStatement * SqlConnection::CreateStatement( const std::string& fmt )
|
||||
{
|
||||
return new SqlPlainPreparedStatement(fmt, *this);
|
||||
}
|
||||
|
||||
void SqlConnection::FreePreparedStatements()
|
||||
{
|
||||
SqlConnection::Lock guard(this);
|
||||
|
||||
size_t nStmts = m_holder.size();
|
||||
for (size_t i = 0; i < nStmts; ++i)
|
||||
delete m_holder[i];
|
||||
|
||||
m_holder.clear();
|
||||
}
|
||||
|
||||
SqlPreparedStatement * SqlConnection::GetStmt( int nIndex )
|
||||
{
|
||||
if(nIndex < 0)
|
||||
return NULL;
|
||||
|
||||
//resize stmt container
|
||||
if(m_holder.size() <= nIndex)
|
||||
m_holder.resize(nIndex + 1, NULL);
|
||||
|
||||
SqlPreparedStatement * pStmt = NULL;
|
||||
|
||||
//create stmt if needed
|
||||
if(m_holder[nIndex] == NULL)
|
||||
{
|
||||
//obtain SQL request string
|
||||
std::string fmt = m_db.GetStmtString(nIndex);
|
||||
MANGOS_ASSERT(fmt.length());
|
||||
//allocate SQlPreparedStatement object
|
||||
pStmt = CreateStatement(fmt);
|
||||
//prepare statement
|
||||
if(!pStmt->prepare())
|
||||
{
|
||||
MANGOS_ASSERT(false && "Unable to prepare SQL statement");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//save statement in internal registry
|
||||
m_holder[nIndex] = pStmt;
|
||||
}
|
||||
else
|
||||
pStmt = m_holder[nIndex];
|
||||
|
||||
return pStmt;
|
||||
}
|
||||
|
||||
bool SqlConnection::ExecuteStmt(int nIndex, const SqlStmtParameters& id )
|
||||
{
|
||||
if(nIndex == -1)
|
||||
return false;
|
||||
|
||||
//get prepared statement object
|
||||
SqlPreparedStatement * pStmt = GetStmt(nIndex);
|
||||
//bind parameters
|
||||
pStmt->bind(id);
|
||||
//execute statement
|
||||
return pStmt->execute();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
Database::~Database()
|
||||
{
|
||||
StopServer();
|
||||
|
|
@ -274,7 +341,7 @@ bool Database::Execute(const char *sql)
|
|||
if(pTrans)
|
||||
{
|
||||
//add SQL request to trans queue
|
||||
pTrans->DelayExecute(sql);
|
||||
pTrans->DelayExecute(new SqlPlainRequest(sql));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -283,7 +350,7 @@ bool Database::Execute(const char *sql)
|
|||
return DirectExecute(sql);
|
||||
|
||||
// Simple sql statement
|
||||
m_threadBody->Delay(new SqlStatement(sql));
|
||||
m_threadBody->Delay(new SqlPlainRequest(sql));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -477,6 +544,85 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Database::ExecuteStmt( const SqlStatementID& id, SqlStmtParameters * params )
|
||||
{
|
||||
if (!m_pAsyncConn)
|
||||
return false;
|
||||
|
||||
SqlTransaction * pTrans = m_TransStorage->get();
|
||||
if(pTrans)
|
||||
{
|
||||
//add SQL request to trans queue
|
||||
pTrans->DelayExecute(new SqlPreparedRequest(id.ID(), params));
|
||||
}
|
||||
else
|
||||
{
|
||||
//if async execution is not available
|
||||
if(!m_bAllowAsyncTransactions)
|
||||
return DirectExecuteStmt(id, params);
|
||||
|
||||
// Simple sql statement
|
||||
m_threadBody->Delay(new SqlPreparedRequest(id.ID(), params));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Database::DirectExecuteStmt( const SqlStatementID& id, SqlStmtParameters * params )
|
||||
{
|
||||
MANGOS_ASSERT(params);
|
||||
std::auto_ptr<SqlStmtParameters> p(params);
|
||||
//execute statement
|
||||
SqlConnection::Lock _guard(getAsyncConnection());
|
||||
return _guard->ExecuteStmt(id.ID(), *params);
|
||||
}
|
||||
|
||||
SqlStatement Database::CreateStatement(SqlStatementID& index, const char * fmt )
|
||||
{
|
||||
int nId = -1;
|
||||
//check if statement ID is initialized
|
||||
if(!index.initialized())
|
||||
{
|
||||
//convert to lower register
|
||||
std::string szFmt(fmt);
|
||||
//count input parameters
|
||||
int nParams = std::count(szFmt.begin(), szFmt.end(), '?');
|
||||
//find existing or add a new record in registry
|
||||
LOCK_GUARD _guard(m_stmtGuard);
|
||||
PreparedStmtRegistry::const_iterator iter = m_stmtRegistry.find(szFmt);
|
||||
if(iter == m_stmtRegistry.end())
|
||||
{
|
||||
nId = ++m_iStmtIndex;
|
||||
m_stmtRegistry[szFmt] = nId;
|
||||
}
|
||||
else
|
||||
nId = iter->second;
|
||||
|
||||
//save initialized statement index info
|
||||
index.init(nId, nParams);
|
||||
}
|
||||
|
||||
return SqlStatement(index, *this);
|
||||
}
|
||||
|
||||
std::string Database::GetStmtString(const int stmtId) const
|
||||
{
|
||||
LOCK_GUARD _guard(m_stmtGuard);
|
||||
|
||||
if(stmtId == -1 || stmtId > m_iStmtIndex)
|
||||
return std::string();
|
||||
|
||||
PreparedStmtRegistry::const_iterator iter_last = m_stmtRegistry.end();
|
||||
for(PreparedStmtRegistry::const_iterator iter = m_stmtRegistry.begin(); iter != iter_last; ++iter)
|
||||
{
|
||||
if(iter->second == stmtId)
|
||||
return iter->first;
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
//HELPER CLASSES AND FUNCTIONS
|
||||
Database::TransHelper::~TransHelper()
|
||||
{
|
||||
reset();
|
||||
|
|
|
|||
|
|
@ -26,10 +26,14 @@
|
|||
#include "Policies/ThreadingModel.h"
|
||||
#include <ace/TSS_T.h>
|
||||
#include <ace/Atomic_Op.h>
|
||||
#include "SqlPreparedStatement.h"
|
||||
|
||||
class SqlTransaction;
|
||||
class SqlResultQueue;
|
||||
class SqlQueryHolder;
|
||||
class SqlStmtParameters;
|
||||
class SqlParamBinder;
|
||||
class Database;
|
||||
|
||||
#define MAX_QUERY_LEN 32*1024
|
||||
|
||||
|
|
@ -57,6 +61,9 @@ class MANGOS_DLL_SPEC SqlConnection
|
|||
// can't rollback without transaction support
|
||||
virtual bool RollbackTransaction() { return true; }
|
||||
|
||||
//methods to work with prepared statements
|
||||
bool ExecuteStmt(int nIndex, const SqlStmtParameters& id);
|
||||
|
||||
//SqlConnection object lock
|
||||
class Lock
|
||||
{
|
||||
|
|
@ -70,9 +77,27 @@ class MANGOS_DLL_SPEC SqlConnection
|
|||
SqlConnection * const m_pConn;
|
||||
};
|
||||
|
||||
//get DB object
|
||||
Database& DB() { return m_db; }
|
||||
|
||||
protected:
|
||||
SqlConnection(Database& db) : m_db(db) {}
|
||||
|
||||
virtual SqlPreparedStatement * CreateStatement(const std::string& fmt);
|
||||
//allocate prepared statement and return statement ID
|
||||
SqlPreparedStatement * GetStmt(int nIndex);
|
||||
|
||||
Database& m_db;
|
||||
|
||||
//free prepared statements objects
|
||||
void FreePreparedStatements();
|
||||
|
||||
private:
|
||||
typedef ACE_Recursive_Thread_Mutex LOCK_TYPE;
|
||||
LOCK_TYPE m_mutex;
|
||||
|
||||
typedef std::vector<SqlPreparedStatement * > StmtHolder;
|
||||
StmtHolder m_holder;
|
||||
};
|
||||
|
||||
class MANGOS_DLL_SPEC Database
|
||||
|
|
@ -165,6 +190,13 @@ class MANGOS_DLL_SPEC Database
|
|||
//for sync transaction execution
|
||||
bool CommitTransactionDirect();
|
||||
|
||||
//PREPARED STATEMENT API
|
||||
|
||||
//allocate index for prepared statement with SQL request 'fmt'
|
||||
SqlStatement CreateStatement(SqlStatementID& index, const char * fmt);
|
||||
//get prepared statement format string
|
||||
std::string GetStmtString(const int stmtId) const;
|
||||
|
||||
operator bool () const { return m_pQueryConnections.size() && m_pAsyncConn != 0; }
|
||||
|
||||
//escape string generation
|
||||
|
|
@ -191,7 +223,7 @@ class MANGOS_DLL_SPEC Database
|
|||
|
||||
protected:
|
||||
Database() : m_pAsyncConn(NULL), m_pResultQueue(NULL), m_threadBody(NULL), m_delayThread(NULL),
|
||||
m_logSQL(false), m_pingIntervallms(0), m_nQueryConnPoolSize(1), m_bAllowAsyncTransactions(false)
|
||||
m_logSQL(false), m_pingIntervallms(0), m_nQueryConnPoolSize(1), m_bAllowAsyncTransactions(false), m_iStmtIndex(-1)
|
||||
{
|
||||
m_nQueryCounter = -1;
|
||||
}
|
||||
|
|
@ -235,6 +267,12 @@ class MANGOS_DLL_SPEC Database
|
|||
//for now return one single connection for async requests
|
||||
SqlConnection * getAsyncConnection() const { return m_pAsyncConn; }
|
||||
|
||||
friend class SqlStatement;
|
||||
//PREPARED STATEMENT API
|
||||
//query function for prepared statements
|
||||
bool ExecuteStmt(const SqlStatementID& id, SqlStmtParameters * params);
|
||||
bool DirectExecuteStmt(const SqlStatementID& id, SqlStmtParameters * params);
|
||||
|
||||
//connection helper counters
|
||||
int m_nQueryConnPoolSize; //current size of query connection pool
|
||||
ACE_Atomic_Op<ACE_Thread_Mutex, long> m_nQueryCounter; //counter for connection selection
|
||||
|
|
@ -252,6 +290,17 @@ class MANGOS_DLL_SPEC Database
|
|||
|
||||
bool m_bAllowAsyncTransactions; ///< flag which specifies if async transactions are enabled
|
||||
|
||||
//PREPARED STATEMENT REGISTRY
|
||||
typedef ACE_Thread_Mutex LOCK_TYPE;
|
||||
typedef ACE_Guard<LOCK_TYPE> LOCK_GUARD;
|
||||
|
||||
mutable LOCK_TYPE m_stmtGuard;
|
||||
|
||||
typedef UNORDERED_MAP<std::string, int> PreparedStmtRegistry;
|
||||
PreparedStmtRegistry m_stmtRegistry; ///<
|
||||
|
||||
int m_iStmtIndex;
|
||||
|
||||
private:
|
||||
|
||||
bool m_logSQL;
|
||||
|
|
|
|||
|
|
@ -65,11 +65,12 @@ DatabaseMysql::~DatabaseMysql()
|
|||
|
||||
SqlConnection * DatabaseMysql::CreateConnection()
|
||||
{
|
||||
return new MySQLConnection();
|
||||
return new MySQLConnection(*this);
|
||||
}
|
||||
|
||||
MySQLConnection::~MySQLConnection()
|
||||
{
|
||||
FreePreparedStatements();
|
||||
mysql_close(mMysql);
|
||||
}
|
||||
|
||||
|
|
@ -309,4 +310,194 @@ unsigned long MySQLConnection::escape_string(char *to, const char *from, unsigne
|
|||
return(mysql_real_escape_string(mMysql, to, from, length));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SqlPreparedStatement * MySQLConnection::CreateStatement( const std::string& fmt )
|
||||
{
|
||||
return new MySqlPreparedStatement(fmt, *this, mMysql);
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
MySqlPreparedStatement::MySqlPreparedStatement( const std::string& fmt, SqlConnection& conn, MYSQL * mysql ) : SqlPreparedStatement(fmt, conn),
|
||||
m_pMySQLConn(mysql), m_stmt(NULL), m_pInputArgs(NULL), m_pResult(NULL), m_pResultMetadata(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
MySqlPreparedStatement::~MySqlPreparedStatement()
|
||||
{
|
||||
RemoveBinds();
|
||||
}
|
||||
|
||||
bool MySqlPreparedStatement::prepare()
|
||||
{
|
||||
if(isPrepared())
|
||||
return true;
|
||||
|
||||
//remove old binds
|
||||
RemoveBinds();
|
||||
|
||||
//create statement object
|
||||
m_stmt = mysql_stmt_init(m_pMySQLConn);
|
||||
if (!m_stmt)
|
||||
{
|
||||
sLog.outError("SQL: mysql_stmt_init()() failed ");
|
||||
return false;
|
||||
}
|
||||
|
||||
//prepare statement
|
||||
if (mysql_stmt_prepare(m_stmt, m_szFmt.c_str(), m_szFmt.length()))
|
||||
{
|
||||
sLog.outError("SQL: mysql_stmt_prepare() failed for '%s'", m_szFmt.c_str());
|
||||
sLog.outError("SQL ERROR: %s", mysql_stmt_error(m_stmt));
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the parameter count from the statement */
|
||||
m_nParams = mysql_stmt_param_count(m_stmt);
|
||||
|
||||
/* Fetch result set meta information */
|
||||
m_pResultMetadata = mysql_stmt_result_metadata(m_stmt);
|
||||
//if we do not have result metadata
|
||||
if (!m_pResultMetadata && strnicmp(m_szFmt.c_str(), "select", 6) == 0)
|
||||
{
|
||||
sLog.outError("SQL: no meta information for '%s'", m_szFmt.c_str());
|
||||
sLog.outError("SQL ERROR: %s", mysql_stmt_error(m_stmt));
|
||||
return false;
|
||||
}
|
||||
|
||||
//bind input buffers
|
||||
if(m_nParams)
|
||||
{
|
||||
m_pInputArgs = new MYSQL_BIND[m_nParams];
|
||||
memset(m_pInputArgs, 0, sizeof(MYSQL_BIND) * m_nParams);
|
||||
}
|
||||
|
||||
//check if we have a statement which returns result sets
|
||||
if(m_pResultMetadata)
|
||||
{
|
||||
//our statement is query
|
||||
m_bIsQuery = true;
|
||||
/* Get total columns in the query */
|
||||
m_nColumns = mysql_num_fields(m_pResultMetadata);
|
||||
|
||||
//bind output buffers
|
||||
}
|
||||
|
||||
m_bPrepared = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MySqlPreparedStatement::bind( const SqlStmtParameters& holder )
|
||||
{
|
||||
if(!isPrepared())
|
||||
{
|
||||
MANGOS_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
//finalize adding params
|
||||
if(!m_pInputArgs)
|
||||
return;
|
||||
|
||||
//verify if we bound all needed input parameters
|
||||
if(m_nParams != holder.boundParams())
|
||||
{
|
||||
MANGOS_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
int nIndex = 0;
|
||||
SqlStmtParameters::ParameterContainer const& _args = holder.params();
|
||||
|
||||
SqlStmtParameters::ParameterContainer::const_iterator iter_last = _args.end();
|
||||
for (SqlStmtParameters::ParameterContainer::const_iterator iter = _args.begin(); iter != iter_last; ++iter)
|
||||
{
|
||||
//bind parameter
|
||||
addParam(nIndex++, (*iter));
|
||||
}
|
||||
|
||||
//bind input arguments
|
||||
if(mysql_stmt_bind_param(m_stmt, m_pInputArgs))
|
||||
{
|
||||
sLog.outError("SQL ERROR: mysql_stmt_bind_param() failed\n");
|
||||
sLog.outError("SQL ERROR: %s", mysql_stmt_error(m_stmt));
|
||||
}
|
||||
}
|
||||
|
||||
void MySqlPreparedStatement::addParam( int nIndex, const SqlStmtFieldData& data )
|
||||
{
|
||||
MANGOS_ASSERT(m_pInputArgs);
|
||||
MANGOS_ASSERT(nIndex < m_nParams);
|
||||
|
||||
MYSQL_BIND& pData = m_pInputArgs[nIndex];
|
||||
|
||||
my_bool bUnsigned = 0;
|
||||
enum_field_types dataType = ToMySQLType(data, bUnsigned);
|
||||
|
||||
//setup MYSQL_BIND structure
|
||||
pData.buffer_type = dataType;
|
||||
pData.is_unsigned = bUnsigned;
|
||||
pData.buffer = data.buff();
|
||||
pData.length = 0;
|
||||
pData.buffer_length = data.type() == FIELD_STRING ? data.size() : 0;
|
||||
}
|
||||
|
||||
void MySqlPreparedStatement::RemoveBinds()
|
||||
{
|
||||
if(!m_stmt)
|
||||
return;
|
||||
|
||||
delete [] m_pInputArgs;
|
||||
delete [] m_pResult;
|
||||
|
||||
mysql_free_result(m_pResultMetadata);
|
||||
mysql_stmt_close(m_stmt);
|
||||
|
||||
m_stmt = NULL;
|
||||
m_pResultMetadata = NULL;
|
||||
m_pResult = NULL;
|
||||
m_pInputArgs = NULL;
|
||||
|
||||
m_bPrepared = false;
|
||||
}
|
||||
|
||||
bool MySqlPreparedStatement::execute()
|
||||
{
|
||||
if(!isPrepared())
|
||||
return false;
|
||||
|
||||
if(mysql_stmt_execute(m_stmt))
|
||||
{
|
||||
sLog.outError("SQL: cannot execute '%s'", m_szFmt.c_str());
|
||||
sLog.outError("SQL ERROR: %s", mysql_stmt_error(m_stmt));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum_field_types MySqlPreparedStatement::ToMySQLType( const SqlStmtFieldData &data, my_bool &bUnsigned )
|
||||
{
|
||||
bUnsigned = 0;
|
||||
enum_field_types dataType = MYSQL_TYPE_NULL;
|
||||
|
||||
switch (data.type())
|
||||
{
|
||||
case FIELD_NONE: dataType = MYSQL_TYPE_NULL; break;
|
||||
case FIELD_BOOL: dataType = MYSQL_TYPE_BIT; bUnsigned = 1; break;
|
||||
case FIELD_I8: dataType = MYSQL_TYPE_TINY; break;
|
||||
case FIELD_UI8: dataType = MYSQL_TYPE_TINY; bUnsigned = 1; break;
|
||||
case FIELD_I16: dataType = MYSQL_TYPE_SHORT; break;
|
||||
case FIELD_UI16: dataType = MYSQL_TYPE_SHORT; bUnsigned = 1; break;
|
||||
case FIELD_I32: dataType = MYSQL_TYPE_LONG; break;
|
||||
case FIELD_UI32: dataType = MYSQL_TYPE_LONG; bUnsigned = 1; break;
|
||||
case FIELD_I64: dataType = MYSQL_TYPE_LONGLONG; break;
|
||||
case FIELD_UI64: dataType = MYSQL_TYPE_LONGLONG; bUnsigned = 1; break;
|
||||
case FIELD_FLOAT: dataType = MYSQL_TYPE_FLOAT; break;
|
||||
case FIELD_DOUBLE: dataType = MYSQL_TYPE_DOUBLE; break;
|
||||
case FIELD_STRING: dataType = MYSQL_TYPE_STRING; break;
|
||||
}
|
||||
|
||||
return dataType;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
#ifndef _DATABASEMYSQL_H
|
||||
#define _DATABASEMYSQL_H
|
||||
|
||||
#include "Common.h"
|
||||
//#include "Common.h"
|
||||
#include "Database.h"
|
||||
#include "Policies/Singleton.h"
|
||||
#include "ace/Thread_Mutex.h"
|
||||
|
|
@ -34,10 +34,42 @@
|
|||
#include <mysql.h>
|
||||
#endif
|
||||
|
||||
//MySQL prepared statement class
|
||||
class MANGOS_DLL_SPEC MySqlPreparedStatement : public SqlPreparedStatement
|
||||
{
|
||||
public:
|
||||
MySqlPreparedStatement(const std::string& fmt, SqlConnection& conn, MYSQL * mysql);
|
||||
~MySqlPreparedStatement();
|
||||
|
||||
//prepare statement
|
||||
virtual bool prepare();
|
||||
|
||||
//bind input parameters
|
||||
virtual void bind(const SqlStmtParameters& holder);
|
||||
|
||||
//execute DML statement
|
||||
virtual bool execute();
|
||||
|
||||
protected:
|
||||
//bind parameters
|
||||
void addParam(int nIndex, const SqlStmtFieldData& data);
|
||||
|
||||
static enum_field_types ToMySQLType( const SqlStmtFieldData &data, my_bool &bUnsigned );
|
||||
|
||||
private:
|
||||
void RemoveBinds();
|
||||
|
||||
MYSQL * m_pMySQLConn;
|
||||
MYSQL_STMT * m_stmt;
|
||||
MYSQL_BIND * m_pInputArgs;
|
||||
MYSQL_BIND * m_pResult;
|
||||
MYSQL_RES *m_pResultMetadata;
|
||||
};
|
||||
|
||||
class MANGOS_DLL_SPEC MySQLConnection : public SqlConnection
|
||||
{
|
||||
public:
|
||||
MySQLConnection() : mMysql(NULL) {}
|
||||
MySQLConnection(Database& db) : SqlConnection(db), mMysql(NULL) {}
|
||||
~MySQLConnection();
|
||||
|
||||
bool Initialize(const char *infoString);
|
||||
|
|
@ -52,6 +84,9 @@ class MANGOS_DLL_SPEC MySQLConnection : public SqlConnection
|
|||
bool CommitTransaction();
|
||||
bool RollbackTransaction();
|
||||
|
||||
protected:
|
||||
SqlPreparedStatement * CreateStatement(const std::string& fmt);
|
||||
|
||||
private:
|
||||
bool _TransactionCmd(const char *sql);
|
||||
bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount);
|
||||
|
|
|
|||
|
|
@ -25,26 +25,26 @@
|
|||
|
||||
/// ---- ASYNC STATEMENTS / TRANSACTIONS ----
|
||||
|
||||
void SqlStatement::Execute(SqlConnection *conn)
|
||||
bool SqlPlainRequest::Execute(SqlConnection *conn)
|
||||
{
|
||||
/// just do it
|
||||
LOCK_DB_CONN(conn);
|
||||
conn->Execute(m_sql);
|
||||
return conn->Execute(m_sql);
|
||||
}
|
||||
|
||||
SqlTransaction::~SqlTransaction()
|
||||
{
|
||||
while(!m_queue.empty())
|
||||
{
|
||||
delete [] (const_cast<char*>(m_queue.back()));
|
||||
delete m_queue.back();
|
||||
m_queue.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
void SqlTransaction::Execute(SqlConnection *conn)
|
||||
bool SqlTransaction::Execute(SqlConnection *conn)
|
||||
{
|
||||
if(m_queue.empty())
|
||||
return;
|
||||
return true;
|
||||
|
||||
LOCK_DB_CONN(conn);
|
||||
|
||||
|
|
@ -53,30 +53,47 @@ void SqlTransaction::Execute(SqlConnection *conn)
|
|||
const int nItems = m_queue.size();
|
||||
for (int i = 0; i < nItems; ++i)
|
||||
{
|
||||
const char *sql = m_queue[i];
|
||||
SqlOperation * pStmt = m_queue[i];
|
||||
|
||||
if(!conn->Execute(sql))
|
||||
if(!pStmt->Execute(conn))
|
||||
{
|
||||
conn->RollbackTransaction();
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
conn->CommitTransaction();
|
||||
return conn->CommitTransaction();
|
||||
}
|
||||
|
||||
SqlPreparedRequest::SqlPreparedRequest(int nIndex, SqlStmtParameters * arg ) : m_nIndex(nIndex), m_param(arg)
|
||||
{
|
||||
}
|
||||
|
||||
SqlPreparedRequest::~SqlPreparedRequest()
|
||||
{
|
||||
delete m_param;
|
||||
}
|
||||
|
||||
bool SqlPreparedRequest::Execute( SqlConnection *conn )
|
||||
{
|
||||
LOCK_DB_CONN(conn);
|
||||
return conn->ExecuteStmt(m_nIndex, *m_param);
|
||||
}
|
||||
|
||||
/// ---- ASYNC QUERIES ----
|
||||
|
||||
void SqlQuery::Execute(SqlConnection *conn)
|
||||
bool SqlQuery::Execute(SqlConnection *conn)
|
||||
{
|
||||
if(!m_callback || !m_queue)
|
||||
return;
|
||||
return false;
|
||||
|
||||
LOCK_DB_CONN(conn);
|
||||
/// execute the query and store the result in the callback
|
||||
m_callback->SetResult(conn->Query(m_sql));
|
||||
/// add the callback to the sql result queue of the thread it originated from
|
||||
m_queue->add(m_callback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SqlResultQueue::Update()
|
||||
|
|
@ -190,10 +207,10 @@ void SqlQueryHolder::SetSize(size_t size)
|
|||
m_queries.resize(size);
|
||||
}
|
||||
|
||||
void SqlQueryHolderEx::Execute(SqlConnection *conn)
|
||||
bool SqlQueryHolderEx::Execute(SqlConnection *conn)
|
||||
{
|
||||
if(!m_holder || !m_callback || !m_queue)
|
||||
return;
|
||||
return false;
|
||||
|
||||
LOCK_DB_CONN(conn);
|
||||
/// we can do this, we are friends
|
||||
|
|
@ -207,4 +224,6 @@ void SqlQueryHolderEx::Execute(SqlConnection *conn)
|
|||
|
||||
/// sync with the caller thread
|
||||
m_queue->add(m_callback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,43 +31,53 @@
|
|||
class Database;
|
||||
class SqlConnection;
|
||||
class SqlDelayThread;
|
||||
class SqlStmtParameters;
|
||||
|
||||
class SqlOperation
|
||||
{
|
||||
public:
|
||||
virtual void OnRemove() { delete this; }
|
||||
virtual void Execute(SqlConnection *conn) = 0;
|
||||
virtual bool Execute(SqlConnection *conn) = 0;
|
||||
virtual ~SqlOperation() {}
|
||||
};
|
||||
|
||||
/// ---- ASYNC STATEMENTS / TRANSACTIONS ----
|
||||
|
||||
class SqlStatement : public SqlOperation
|
||||
class SqlPlainRequest : public SqlOperation
|
||||
{
|
||||
private:
|
||||
const char *m_sql;
|
||||
public:
|
||||
SqlStatement(const char *sql) : m_sql(mangos_strdup(sql)){}
|
||||
~SqlStatement() { char* tofree = const_cast<char*>(m_sql); delete [] tofree; }
|
||||
void Execute(SqlConnection *conn);
|
||||
SqlPlainRequest(const char *sql) : m_sql(mangos_strdup(sql)){}
|
||||
~SqlPlainRequest() { char* tofree = const_cast<char*>(m_sql); delete [] tofree; }
|
||||
bool Execute(SqlConnection *conn);
|
||||
};
|
||||
|
||||
class SqlTransaction : public SqlOperation
|
||||
{
|
||||
private:
|
||||
std::vector<const char *> m_queue;
|
||||
std::vector<SqlOperation * > m_queue;
|
||||
|
||||
public:
|
||||
SqlTransaction() {}
|
||||
~SqlTransaction();
|
||||
|
||||
void DelayExecute(const char *sql)
|
||||
{
|
||||
char* _sql = mangos_strdup(sql);
|
||||
m_queue.push_back(_sql);
|
||||
}
|
||||
void DelayExecute(SqlOperation * sql) { m_queue.push_back(sql); }
|
||||
|
||||
void Execute(SqlConnection *conn);
|
||||
bool Execute(SqlConnection *conn);
|
||||
};
|
||||
|
||||
class SqlPreparedRequest : public SqlOperation
|
||||
{
|
||||
public:
|
||||
SqlPreparedRequest(int nIndex, SqlStmtParameters * arg);
|
||||
~SqlPreparedRequest();
|
||||
|
||||
bool Execute(SqlConnection *conn);
|
||||
|
||||
private:
|
||||
const int m_nIndex;
|
||||
SqlStmtParameters * m_param;
|
||||
};
|
||||
|
||||
/// ---- ASYNC QUERIES ----
|
||||
|
|
@ -95,7 +105,7 @@ class SqlQuery : public SqlOperation
|
|||
SqlQuery(const char *sql, MaNGOS::IQueryCallback * callback, SqlResultQueue * queue)
|
||||
: m_sql(mangos_strdup(sql)), m_callback(callback), m_queue(queue) {}
|
||||
~SqlQuery() { char* tofree = const_cast<char*>(m_sql); delete [] tofree; }
|
||||
void Execute(SqlConnection *conn);
|
||||
bool Execute(SqlConnection *conn);
|
||||
};
|
||||
|
||||
class SqlQueryHolder
|
||||
|
|
@ -124,6 +134,6 @@ class SqlQueryHolderEx : public SqlOperation
|
|||
public:
|
||||
SqlQueryHolderEx(SqlQueryHolder *holder, MaNGOS::IQueryCallback * callback, SqlResultQueue * queue)
|
||||
: m_holder(holder), m_callback(callback), m_queue(queue) {}
|
||||
void Execute(SqlConnection *conn);
|
||||
bool Execute(SqlConnection *conn);
|
||||
};
|
||||
#endif //__SQLOPERATIONS_H
|
||||
|
|
|
|||
160
src/shared/Database/SqlPreparedStatement.cpp
Normal file
160
src/shared/Database/SqlPreparedStatement.cpp
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "DatabaseEnv.h"
|
||||
|
||||
SqlStmtParameters::SqlStmtParameters( int nParams )
|
||||
{
|
||||
//reserve memory if needed
|
||||
if(nParams > 0)
|
||||
m_params.reserve(nParams);
|
||||
}
|
||||
|
||||
void SqlStmtParameters::reset( const SqlStatement& stmt )
|
||||
{
|
||||
m_params.clear();
|
||||
//reserve memory if needed
|
||||
if(stmt.arguments() > 0)
|
||||
m_params.reserve(stmt.arguments());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SqlStatement& SqlStatement::operator=( const SqlStatement& index )
|
||||
{
|
||||
if(this != &index)
|
||||
{
|
||||
m_index = index.m_index;
|
||||
m_pDB = index.m_pDB;
|
||||
|
||||
if(m_pParams)
|
||||
{
|
||||
delete m_pParams;
|
||||
m_pParams = NULL;
|
||||
}
|
||||
|
||||
if(index.m_pParams)
|
||||
m_pParams = new SqlStmtParameters(*(index.m_pParams));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool SqlStatement::Execute()
|
||||
{
|
||||
SqlStmtParameters * args = detach();
|
||||
//verify amount of bound parameters
|
||||
if(args->boundParams() != arguments())
|
||||
{
|
||||
sLog.outError("SQL ERROR: wrong amount of parameters (%i instead of %i)", args->boundParams(), arguments());
|
||||
sLog.outError("SQL ERROR: statement: %s", m_pDB->GetStmtString(ID()).c_str());
|
||||
MANGOS_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_pDB->ExecuteStmt(m_index, args);
|
||||
}
|
||||
|
||||
bool SqlStatement::DirectExecute()
|
||||
{
|
||||
SqlStmtParameters * args = detach();
|
||||
//verify amount of bound parameters
|
||||
if(args->boundParams() != arguments())
|
||||
{
|
||||
sLog.outError("SQL ERROR: wrong amount of parameters (%i instead of %i)", args->boundParams(), arguments());
|
||||
sLog.outError("SQL ERROR: statement: %s", m_pDB->GetStmtString(ID()).c_str());
|
||||
MANGOS_ASSERT(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_pDB->DirectExecuteStmt(m_index, args);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
SqlPlainPreparedStatement::SqlPlainPreparedStatement( const std::string& fmt, SqlConnection& conn ) : SqlPreparedStatement(fmt, conn)
|
||||
{
|
||||
m_bPrepared = true;
|
||||
m_nParams = std::count(m_szFmt.begin(), m_szFmt.end(), '?');
|
||||
m_bIsQuery = strnicmp(m_szFmt.c_str(), "select", 6) == 0;
|
||||
}
|
||||
|
||||
void SqlPlainPreparedStatement::bind( const SqlStmtParameters& holder )
|
||||
{
|
||||
//verify if we bound all needed input parameters
|
||||
if(m_nParams != holder.boundParams())
|
||||
{
|
||||
MANGOS_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
//reset resulting plain SQL request
|
||||
m_szPlainRequest = m_szFmt;
|
||||
size_t nLastPos = 0;
|
||||
|
||||
SqlStmtParameters::ParameterContainer const& _args = holder.params();
|
||||
|
||||
SqlStmtParameters::ParameterContainer::const_iterator iter_last = _args.end();
|
||||
for (SqlStmtParameters::ParameterContainer::const_iterator iter = _args.begin(); iter != iter_last; ++iter)
|
||||
{
|
||||
//bind parameter
|
||||
const SqlStmtFieldData& data = (*iter);
|
||||
|
||||
std::ostringstream fmt;
|
||||
DataToString(data, fmt);
|
||||
|
||||
nLastPos = m_szPlainRequest.find('?', nLastPos);
|
||||
if(nLastPos != std::string::npos)
|
||||
{
|
||||
std::string tmp = fmt.str();
|
||||
m_szPlainRequest.replace(nLastPos, 1, tmp);
|
||||
nLastPos += tmp.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SqlPlainPreparedStatement::execute()
|
||||
{
|
||||
if(m_szPlainRequest.empty())
|
||||
return false;
|
||||
|
||||
return m_pConn.Execute(m_szPlainRequest.c_str());
|
||||
}
|
||||
|
||||
void SqlPlainPreparedStatement::DataToString( const SqlStmtFieldData& data, std::ostringstream& fmt )
|
||||
{
|
||||
switch (data.type())
|
||||
{
|
||||
case FIELD_BOOL: fmt << "'" << uint32(data.toBool()) << "'"; break;
|
||||
case FIELD_UI8: fmt << "'" << uint32(data.toUint8()) << "'"; break;
|
||||
case FIELD_UI16: fmt << "'" << uint32(data.toUint16()) << "'"; break;
|
||||
case FIELD_UI32: fmt << "'" << data.toUint32() << "'"; break;
|
||||
case FIELD_UI64: fmt << "'" << data.toUint64() << "'"; break;
|
||||
case FIELD_I8: fmt << "'" << int32(data.toInt8()) << "'"; break;
|
||||
case FIELD_I16: fmt << "'" << int32(data.toInt16()) << "'"; break;
|
||||
case FIELD_I32: fmt << "'" << data.toInt32() << "'"; break;
|
||||
case FIELD_I64: fmt << "'" << data.toInt64() << "'"; break;
|
||||
case FIELD_FLOAT: fmt << "'" << data.toFloat() << "'"; break;
|
||||
case FIELD_DOUBLE: fmt << "'" << data.toDouble() << "'"; break;
|
||||
case FIELD_STRING:
|
||||
{
|
||||
std::string tmp = data.toStr();
|
||||
m_pConn.DB().escape_string(tmp);
|
||||
fmt << "'" << tmp << "'";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
352
src/shared/Database/SqlPreparedStatement.h
Normal file
352
src/shared/Database/SqlPreparedStatement.h
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
* Copyright (C) 2005-2011 MaNGOS <http://getmangos.com/>
|
||||
*
|
||||
* 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef SQLPREPAREDSTATEMENTS_H
|
||||
#define SQLPREPAREDSTATEMENTS_H
|
||||
|
||||
#include "Common.h"
|
||||
#include <ace/TSS_T.h>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
class Database;
|
||||
class SqlConnection;
|
||||
class QueryResult;
|
||||
|
||||
union SqlStmtField
|
||||
{
|
||||
bool boolean;
|
||||
uint8 ui8;
|
||||
int8 i8;
|
||||
uint16 ui16;
|
||||
int16 i16;
|
||||
uint32 ui32;
|
||||
int32 i32;
|
||||
uint64 ui64;
|
||||
int64 i64;
|
||||
float f;
|
||||
double d;
|
||||
};
|
||||
|
||||
enum SqlStmtFieldType
|
||||
{
|
||||
FIELD_BOOL,
|
||||
FIELD_UI8,
|
||||
FIELD_UI16,
|
||||
FIELD_UI32,
|
||||
FIELD_UI64,
|
||||
FIELD_I8,
|
||||
FIELD_I16,
|
||||
FIELD_I32,
|
||||
FIELD_I64,
|
||||
FIELD_FLOAT,
|
||||
FIELD_DOUBLE,
|
||||
FIELD_STRING,
|
||||
FIELD_NONE
|
||||
};
|
||||
|
||||
//templates might be the best choice here
|
||||
//but I didn't have time to play with them
|
||||
class MANGOS_DLL_SPEC SqlStmtFieldData
|
||||
{
|
||||
public:
|
||||
SqlStmtFieldData() : m_type(FIELD_NONE) { m_binaryData.ui64 = 0; }
|
||||
~SqlStmtFieldData() {}
|
||||
|
||||
template<typename T>
|
||||
SqlStmtFieldData(T param) { set(param); }
|
||||
|
||||
template<typename T1>
|
||||
void set(T1 param1);
|
||||
|
||||
//getters
|
||||
bool toBool() const { MANGOS_ASSERT(m_type == FIELD_BOOL); return m_binaryData.boolean; }
|
||||
uint8 toUint8() const { MANGOS_ASSERT(m_type == FIELD_UI8); return m_binaryData.ui8; }
|
||||
int8 toInt8() const { MANGOS_ASSERT(m_type == FIELD_I8); return m_binaryData.i8; }
|
||||
uint16 toUint16() const { MANGOS_ASSERT(m_type == FIELD_UI16); return m_binaryData.ui16; }
|
||||
int16 toInt16() const { MANGOS_ASSERT(m_type == FIELD_I16); return m_binaryData.i16; }
|
||||
uint32 toUint32() const { MANGOS_ASSERT(m_type == FIELD_UI32); return m_binaryData.ui32; }
|
||||
int32 toInt32() const { MANGOS_ASSERT(m_type == FIELD_I32); return m_binaryData.i32; }
|
||||
uint64 toUint64() const { MANGOS_ASSERT(m_type == FIELD_UI64); return m_binaryData.ui64; }
|
||||
int64 toInt64() const { MANGOS_ASSERT(m_type == FIELD_I64); return m_binaryData.i64; }
|
||||
float toFloat() const { MANGOS_ASSERT(m_type == FIELD_FLOAT); return m_binaryData.f; }
|
||||
double toDouble() const { MANGOS_ASSERT(m_type == FIELD_DOUBLE); return m_binaryData.d; }
|
||||
const char * toStr() const { MANGOS_ASSERT(m_type == FIELD_STRING); return m_szStringData.c_str(); }
|
||||
|
||||
//get type of data
|
||||
SqlStmtFieldType type() const { return m_type; }
|
||||
//get underlying buffer type
|
||||
void * buff() const { return m_type == FIELD_STRING ? (void * )m_szStringData.c_str() : (void *)&m_binaryData; }
|
||||
|
||||
//get size of data
|
||||
size_t size() const
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case FIELD_NONE: return 0;
|
||||
case FIELD_BOOL: return sizeof(bool);
|
||||
case FIELD_UI8: return sizeof(uint8);
|
||||
case FIELD_UI16: return sizeof(uint16);
|
||||
case FIELD_UI32: return sizeof(uint32);
|
||||
case FIELD_UI64: return sizeof(uint64);
|
||||
case FIELD_I8: return sizeof(int8);
|
||||
case FIELD_I16: return sizeof(int16);
|
||||
case FIELD_I32: return sizeof(int32);
|
||||
case FIELD_I64: return sizeof(int64);
|
||||
case FIELD_FLOAT: return sizeof(float);
|
||||
case FIELD_DOUBLE: return sizeof(double);
|
||||
case FIELD_STRING: return m_szStringData.length();
|
||||
|
||||
default:
|
||||
throw std::runtime_error("unrecognized type of SqlStmtFieldType obtained");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SqlStmtFieldType m_type;
|
||||
SqlStmtField m_binaryData;
|
||||
std::string m_szStringData;
|
||||
};
|
||||
|
||||
//template specialization
|
||||
template<> inline void SqlStmtFieldData::set(bool val) { m_type = FIELD_BOOL; m_binaryData.boolean = val; }
|
||||
template<> inline void SqlStmtFieldData::set(uint8 val) { m_type = FIELD_UI8; m_binaryData.ui8 = val; }
|
||||
template<> inline void SqlStmtFieldData::set(int8 val) { m_type = FIELD_I8; m_binaryData.i8 = val; }
|
||||
template<> inline void SqlStmtFieldData::set(uint16 val) { m_type = FIELD_UI16; m_binaryData.ui16 = val; }
|
||||
template<> inline void SqlStmtFieldData::set(int16 val) { m_type = FIELD_I16; m_binaryData.i16 = val; }
|
||||
template<> inline void SqlStmtFieldData::set(uint32 val) { m_type = FIELD_UI32; m_binaryData.ui32 = val; }
|
||||
template<> inline void SqlStmtFieldData::set(int32 val) { m_type = FIELD_I32; m_binaryData.i32 = val; }
|
||||
template<> inline void SqlStmtFieldData::set(uint64 val) { m_type = FIELD_UI64; m_binaryData.ui64 = val; }
|
||||
template<> inline void SqlStmtFieldData::set(int64 val) { m_type = FIELD_I64; m_binaryData.i64 = val; }
|
||||
template<> inline void SqlStmtFieldData::set(float val) { m_type = FIELD_FLOAT; m_binaryData.f = val; }
|
||||
template<> inline void SqlStmtFieldData::set(double val) { m_type = FIELD_DOUBLE; m_binaryData.d = val; }
|
||||
template<> inline void SqlStmtFieldData::set(const char * val) { m_type = FIELD_STRING; m_szStringData = val; }
|
||||
|
||||
class SqlStatement;
|
||||
//prepared statement executor
|
||||
class MANGOS_DLL_SPEC SqlStmtParameters
|
||||
{
|
||||
public:
|
||||
typedef std::vector<SqlStmtFieldData> ParameterContainer;
|
||||
|
||||
//reserve memory to contain all input parameters of stmt
|
||||
explicit SqlStmtParameters(int nParams);
|
||||
|
||||
~SqlStmtParameters() {}
|
||||
|
||||
//get amount of bound parameters
|
||||
int boundParams() const { return int(m_params.size()); }
|
||||
//add parameter
|
||||
void addParam(const SqlStmtFieldData& data) { m_params.push_back(data); }
|
||||
//empty SQL statement parameters. In case nParams > 1 - reserve memory for parameters
|
||||
//should help to reuse the same object with batched SQL requests
|
||||
void reset(const SqlStatement& stmt);
|
||||
//swaps contents of intenral param container
|
||||
void swap(SqlStmtParameters& obj);
|
||||
//get bound parameters
|
||||
const ParameterContainer& params() const { return m_params; }
|
||||
|
||||
private:
|
||||
SqlStmtParameters& operator=(const SqlStmtParameters& obj);
|
||||
|
||||
//statement parameter holder
|
||||
ParameterContainer m_params;
|
||||
};
|
||||
|
||||
//statement ID encapsulation logic
|
||||
class SqlStatementID
|
||||
{
|
||||
public:
|
||||
SqlStatementID() : m_bInitialized(false) {}
|
||||
|
||||
int ID() const { return m_nIndex; }
|
||||
int arguments() const { return m_nArguments; }
|
||||
bool initialized() const { return m_bInitialized; }
|
||||
|
||||
private:
|
||||
friend class Database;
|
||||
void init(int nID, int nArgs) { m_nIndex = nID; m_nArguments = nArgs; m_bInitialized = true; }
|
||||
|
||||
int m_nIndex;
|
||||
int m_nArguments;
|
||||
bool m_bInitialized;
|
||||
};
|
||||
|
||||
//statement index
|
||||
class MANGOS_DLL_SPEC SqlStatement
|
||||
{
|
||||
public:
|
||||
~SqlStatement() { delete m_pParams; }
|
||||
|
||||
SqlStatement(const SqlStatement& index) : m_index(index.m_index), m_pDB(index.m_pDB), m_pParams(NULL)
|
||||
{
|
||||
if(index.m_pParams)
|
||||
m_pParams = new SqlStmtParameters(*(index.m_pParams));
|
||||
}
|
||||
|
||||
SqlStatement& operator=(const SqlStatement& index);
|
||||
|
||||
int ID() const { return m_index.ID(); }
|
||||
int arguments() const { return m_index.arguments(); }
|
||||
|
||||
bool Execute();
|
||||
bool DirectExecute();
|
||||
|
||||
//templates to simplify 1-4 parameter bindings
|
||||
template<typename ParamType1>
|
||||
bool PExecute(ParamType1 param1)
|
||||
{
|
||||
arg(param1);
|
||||
return Execute();
|
||||
}
|
||||
|
||||
template<typename ParamType1, typename ParamType2>
|
||||
bool PExecute(ParamType1 param1, ParamType2 param2)
|
||||
{
|
||||
arg(param1);
|
||||
arg(param2);
|
||||
return Execute();
|
||||
}
|
||||
|
||||
template<typename ParamType1, typename ParamType2, typename ParamType3>
|
||||
bool PExecute(ParamType1 param1, ParamType2 param2, ParamType3 param3)
|
||||
{
|
||||
arg(param1);
|
||||
arg(param2);
|
||||
arg(param3);
|
||||
return Execute();
|
||||
}
|
||||
|
||||
template<typename ParamType1, typename ParamType2, typename ParamType3, typename ParamType4>
|
||||
bool PExecute(ParamType1 param1, ParamType2 param2, ParamType3 param3, ParamType4 param4)
|
||||
{
|
||||
arg(param1);
|
||||
arg(param2);
|
||||
arg(param3);
|
||||
arg(param4);
|
||||
return Execute();
|
||||
}
|
||||
|
||||
//bind parameters with specified type
|
||||
void addBool(bool var) { arg(var); }
|
||||
void addUInt8(uint8 var) { arg(var); }
|
||||
void addInt8(int8 var) { arg(var); }
|
||||
void addUInt16(uint16 var) { arg(var); }
|
||||
void addInt16(int16 var) { arg(var); }
|
||||
void addUInt32(uint32 var) { arg(var); }
|
||||
void addInt32(int32 var) { arg(var); }
|
||||
void addUInt64(uint64 var) { arg(var); }
|
||||
void addInt64(int64 var) { arg(var); }
|
||||
void addFloat(float var) { arg(var); }
|
||||
void addDouble(double var) { arg(var); }
|
||||
void addString(const char * var) { arg(var); }
|
||||
void addString(const std::string& var) { arg(var.c_str()); }
|
||||
void addString(std::ostringstream& ss) { arg(ss.str().c_str()); ss.str(std::string()); }
|
||||
|
||||
protected:
|
||||
//don't allow anyone except Database class to create static SqlStatement objects
|
||||
friend class Database;
|
||||
SqlStatement(const SqlStatementID& index, Database& db) : m_index(index), m_pDB(&db), m_pParams(NULL) {}
|
||||
|
||||
private:
|
||||
|
||||
SqlStmtParameters * get()
|
||||
{
|
||||
if(!m_pParams)
|
||||
m_pParams = new SqlStmtParameters(arguments());
|
||||
|
||||
return m_pParams;
|
||||
}
|
||||
|
||||
SqlStmtParameters * detach()
|
||||
{
|
||||
SqlStmtParameters * p = m_pParams ? m_pParams : new SqlStmtParameters(0);
|
||||
m_pParams = NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
//helper function
|
||||
//use appropriate add* functions to bind specific data type
|
||||
template<typename ParamType>
|
||||
void arg(ParamType val)
|
||||
{
|
||||
SqlStmtParameters * p = get();
|
||||
p->addParam(SqlStmtFieldData(val));
|
||||
}
|
||||
|
||||
SqlStatementID m_index;
|
||||
Database * m_pDB;
|
||||
SqlStmtParameters * m_pParams;
|
||||
};
|
||||
|
||||
//base prepared statement class
|
||||
class MANGOS_DLL_SPEC SqlPreparedStatement
|
||||
{
|
||||
public:
|
||||
virtual ~SqlPreparedStatement() {}
|
||||
|
||||
bool isPrepared() const { return m_bPrepared; }
|
||||
bool isQuery() const { return m_bIsQuery; }
|
||||
|
||||
uint32 params() const { return m_nParams; }
|
||||
uint32 columns() const { return isQuery() ? m_nColumns : 0; }
|
||||
|
||||
//initialize internal structures of prepared statement
|
||||
//upon success m_bPrepared should be true
|
||||
virtual bool prepare() = 0;
|
||||
//bind parameters for prepared statement from parameter placeholder
|
||||
virtual void bind(const SqlStmtParameters& holder) = 0;
|
||||
|
||||
//execute statement w/o result set
|
||||
virtual bool execute() = 0;
|
||||
|
||||
protected:
|
||||
SqlPreparedStatement(const std::string& fmt, SqlConnection& conn) : m_szFmt(fmt), m_nParams(0), m_nColumns(0), m_bPrepared(false), m_bIsQuery(false), m_pConn(conn) {}
|
||||
|
||||
uint32 m_nParams;
|
||||
uint32 m_nColumns;
|
||||
bool m_bIsQuery;
|
||||
bool m_bPrepared;
|
||||
std::string m_szFmt;
|
||||
SqlConnection& m_pConn;
|
||||
};
|
||||
|
||||
//prepared statements via plain SQL string requests
|
||||
class MANGOS_DLL_SPEC SqlPlainPreparedStatement : public SqlPreparedStatement
|
||||
{
|
||||
public:
|
||||
SqlPlainPreparedStatement(const std::string& fmt, SqlConnection& conn);
|
||||
~SqlPlainPreparedStatement() {}
|
||||
|
||||
//this statement is always prepared
|
||||
virtual bool prepare() { return true; }
|
||||
|
||||
//we should replace all '?' symbols with substrings with proper format
|
||||
virtual void bind(const SqlStmtParameters& holder);
|
||||
|
||||
virtual bool execute();
|
||||
|
||||
protected:
|
||||
void DataToString(const SqlStmtFieldData& data, std::ostringstream& fmt);
|
||||
|
||||
std::string m_szPlainRequest;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue