例子:
auto stmt = pMysqlSession->findByIndex(StmtGMArenaSeasonPlayer::ID);
auto pStmtData = static_cast(stmt->getStmtData());
pStmtData->bindParams(fb->player_id(), fb->season_id());
stmt->exec();
int maxRank = 0;
int count = 0;
if (stmt->fetch())
{
maxRank = pStmtData->get_max_rank();
count = pStmtData->get_count();
}
源文件:
StmtGMArenaCount::StmtGMArenaCount()
: MysqlStmtData(ID, MYSQL_STMT_TYPE::PROCESS, "CALL cmd_arena_count(?,?,?,?,?);")
{
clear();
}
void StmtGMArenaCount::bind(MysqlPrepareStmt* pStmt)
{
pStmt->bind(DatabaseStmtBindArgs(DATABASE_STMT_BIND_TYPE_PARAM, DATABASE_STMT_TYPE::INT, 0, "params.m_begin_time", ¶ms.m_begin_time, nullptr));
pStmt->bind(DatabaseStmtBindArgs(DATABASE_STMT_BIND_TYPE_PARAM, DATABASE_STMT_TYPE::INT, 1, "params.m_end_time", ¶ms.m_end_time, nullptr));
pStmt->bind(DatabaseStmtBindArgs(DATABASE_STMT_BIND_TYPE_PARAM, DATABASE_STMT_TYPE::BYTE, 2, "params.m_including_ai", ¶ms.m_including_ai, nullptr));
pStmt->bind(DatabaseStmtBindArgs(DATABASE_STMT_BIND_TYPE_PARAM, DATABASE_STMT_TYPE::BYTE, 3, "params.m_including_giveup", ¶ms.m_including_giveup, nullptr));
pStmt->bind(DatabaseStmtBindArgs(DATABASE_STMT_BIND_TYPE_PARAM, DATABASE_STMT_TYPE::INT, 4, "params.m_inopt", ¶ms.m_inopt, nullptr));
pStmt->bind(DatabaseStmtBindArgs(DATABASE_STMT_BIND_TYPE_RESULT, DATABASE_STMT_TYPE::INT, 0, "results.m_count", &results.m_count, nullptr));
}
void StmtGMArenaCount::clear()
{
memset(¶ms, 0, sizeof(params));
memset(&results, 0, sizeof(results));
}
void StmtGMArenaCount::bindParams(int32_t inopt, int32_t begin_time, int32_t end_time, int8_t including_ai, int8_t including_giveup)
{
set_inopt(inopt);
set_begin_time(begin_time);
set_end_time(end_time);
set_including_ai(including_ai);
set_including_giveup(including_giveup);
}
void StmtGMArenaCount::set_begin_time(int32_t begin_time)
{
params.m_begin_time = begin_time;
}
void StmtGMArenaCount::set_end_time(int32_t end_time)
{
params.m_end_time = end_time;
}
void StmtGMArenaCount::set_inopt(int32_t inopt)
{
params.m_inopt = inopt;
}
void StmtGMArenaCount::set_including_ai(int8_t including_ai)
{
params.m_including_ai = including_ai;
}
void StmtGMArenaCount::set_including_giveup(int8_t including_giveup)
{
params.m_including_giveup = including_giveup;
}
int32_t StmtGMArenaCount::get_count() const
{
return results.m_count;
}
头文件:
struct StmtGMArenaCount final: public MysqlStmtData
{
StmtGMArenaCount();
virtual ~StmtGMArenaCount() = default;
enum { ID = 40 };
virtual uint32_t getParamCount() override { return 5; }
virtual uint32_t getResultCount() override { return 1; }
virtual void bind(MysqlPrepareStmt* pStmt) override;
void clear();
void bindParams(int32_t inopt, int32_t begin_time, int32_t end_time, int8_t including_ai, int8_t including_giveup);
void set_inopt(int32_t inopt);
void set_begin_time(int32_t begin_time);
void set_end_time(int32_t end_time);
void set_including_ai(int8_t including_ai);
void set_including_giveup(int8_t including_giveup);
int32_t get_count() const;
struct Params
{
int32_t m_begin_time;
int32_t m_end_time;
int8_t m_including_ai;
int8_t m_including_giveup;
int32_t m_inopt;
} params;
struct Results
{
int32_t m_count;
} results;
};
MysqlStmtData.h
#pragma once
#include "prerequisites.h"
enum class MYSQL_STMT_TYPE
{
UPDATE, // insert delete update replace 无返回的存储过程
SELECT, // select
PROCESS, // 有返回的存储过程
};
class MysqlPrepareStmt;
// 预处理数据
class MysqlStmtData : private boost::noncopyable
{
public:
MysqlStmtData(uint32_t index, MYSQL_STMT_TYPE type, const char* sql);
virtual ~MysqlStmtData() = default;
virtual uint32_t getParamCount() = 0;
virtual uint32_t getResultCount() = 0;
virtual void bind(MysqlPrepareStmt* pStmt) = 0;
uint32_t getIndex() const;
MYSQL_STMT_TYPE getType() const;
const char* getSql() const;
protected:
unsigned long bindString(char* szDest, size_t nDestCount, std::string_view src);
protected:
uint32_t m_index;
MYSQL_STMT_TYPE m_type;
const char* m_sql;
};
inline uint32_t MysqlStmtData::getIndex() const
{
return m_index;
}
inline MYSQL_STMT_TYPE MysqlStmtData::getType() const
{
return m_type;
}
inline const char* MysqlStmtData::getSql() const
{
return m_sql;
}
MysqlStmtData.cpp
#include "MysqlStmtData.h"
MysqlStmtData::MysqlStmtData(uint32_t index, MYSQL_STMT_TYPE type, const char* sql)
: m_index(index)
, m_type(type)
, m_sql(sql)
{
}
unsigned long MysqlStmtData::bindString(char* szDest, size_t nDestCount, std::string_view src)
{
if (src.empty())
{
szDest[0] = 0;
return 0;
}
unsigned long len = std::min(nDestCount, src.size());
memcpy(szDest, src.data(), len);
return len;
}
MysqlPrepareStmt.cpp
#include "MysqlPrepareStmt.h"
#include "MysqlException.h"
MysqlPrepareStmt::MysqlPrepareStmt()
: m_stmt(nullptr)
, m_pStmtData(nullptr)
{
memset(m_pBind, 0, sizeof(m_pBind));
memset(m_bindSize, 0, sizeof(m_bindSize));
}
MysqlPrepareStmt::~MysqlPrepareStmt()
{
close();
for (int i = 0; i < DATABASE_STMT_BIND_TYPE_MAX; i++)
{
if (m_pBind[i])
{
delete[] m_pBind[i];
m_pBind[i] = nullptr;
}
m_bindSize[i] = 0;
}
}
void MysqlPrepareStmt::load(MysqlStmtData* pStmtData)
{
uint32_t size = pStmtData->getParamCount();
if (size > 0)
{
m_pBind[DATABASE_STMT_BIND_TYPE_PARAM] = DEBUG_NEW MYSQL_BIND[size];
memset(m_pBind[DATABASE_STMT_BIND_TYPE_PARAM], 0, sizeof(MYSQL_BIND) * size);
m_bindSize[DATABASE_STMT_BIND_TYPE_PARAM] = size;
}
size = pStmtData->getResultCount();
if (size > 0)
{
m_pBind[DATABASE_STMT_BIND_TYPE_RESULT] = DEBUG_NEW MYSQL_BIND[size];
memset(m_pBind[DATABASE_STMT_BIND_TYPE_RESULT], 0, sizeof(MYSQL_BIND) * size);
m_bindSize[DATABASE_STMT_BIND_TYPE_RESULT] = size;
}
pStmtData->bind(this);
m_pStmtData = pStmtData;
}
void MysqlPrepareStmt::initialize(MYSQL* pMysql)
{
if (!m_pStmtData)
{
MYSQL_DB_THROW("m_pStmtData == nullptr");
}
close();
const char* sql = m_pStmtData->getSql();
if (!(sql && sql[0]))
{
MYSQL_DB_THROW(fmt::format("sql is empty,index={}", m_pStmtData->getIndex()));
}
m_stmt = mysql_stmt_init(pMysql);
if (!m_stmt)
{
MYSQL_DB_THROW(mysql_errno(pMysql), mysql_error(pMysql), mysql_sqlstate(pMysql), fmt::format("mysql_stmt_init fail,index={},sql={}", m_pStmtData->getIndex(), sql));
}
if (mysql_stmt_prepare(m_stmt, sql, (unsigned long)strlen(sql)))
{
uint32_t uerrno = mysql_errno(pMysql);
std::string strerror = mysql_error(pMysql);
std::string strstate = mysql_sqlstate(pMysql);
mysql_stmt_close(m_stmt);
m_stmt = nullptr;
MYSQL_DB_THROW(uerrno, strerror.c_str(), strstate.c_str(), fmt::format("mysql_stmt_prepare fail,index={},sql={}", m_pStmtData->getIndex(), sql));
}
bindComplete();
}
void MysqlPrepareStmt::close()
{
if (m_stmt)
{
mysql_stmt_close(m_stmt);
m_stmt = nullptr;
}
}
void MysqlPrepareStmt::bindComplete()
{
bindParam();
if (m_pStmtData->getType() == MYSQL_STMT_TYPE::SELECT)
{
bindResult();
}
}
void MysqlPrepareStmt::bindParam()
{
if (0 != mysql_stmt_bind_param(m_stmt, m_pBind[DATABASE_STMT_BIND_TYPE_PARAM]))
{
MYSQL_DB_THROW(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt), mysql_stmt_sqlstate(m_stmt), fmt::format("mysql_stmt_bind_param fail,index={},sql={}", m_pStmtData->getIndex(), m_pStmtData->getSql()));
}
unsigned long size = mysql_stmt_param_count(m_stmt);
if (size != m_bindSize[DATABASE_STMT_BIND_TYPE_PARAM])
{
MYSQL_DB_THROW(fmt::format("index={},sql={}, bind param size({}) != max({})", m_pStmtData->getIndex(), m_pStmtData->getSql(), size, m_bindSize[DATABASE_STMT_BIND_TYPE_PARAM]));
}
}
void MysqlPrepareStmt::bindResult()
{
if (0 != mysql_stmt_bind_result(m_stmt, m_pBind[DATABASE_STMT_BIND_TYPE_RESULT]))
{
MYSQL_DB_THROW(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt), mysql_stmt_sqlstate(m_stmt), fmt::format("mysql_stmt_bind_result fail,index={}", m_pStmtData->getIndex()));
}
}
int MysqlPrepareStmt::exec()
{
if (0 != mysql_stmt_execute(m_stmt))
{
MYSQL_DB_THROW(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt), mysql_stmt_sqlstate(m_stmt), fmt::format("mysql_stmt_execute fail,index={}", m_pStmtData->getIndex()));
}
if (m_pStmtData->getType() == MYSQL_STMT_TYPE::UPDATE)
{
return (int)mysql_stmt_affected_rows(m_stmt);
}
if (0 != mysql_stmt_store_result(m_stmt))
{
MYSQL_DB_THROW(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt), mysql_stmt_sqlstate(m_stmt), fmt::format("mysql_stmt_store_result fail,index={}", m_pStmtData->getIndex()));
}
if (m_pStmtData->getType() == MYSQL_STMT_TYPE::PROCESS)
{
bindResult();
}
return 0;
}
bool MysqlPrepareStmt::fetch()
{
int ret = mysql_stmt_fetch(m_stmt);
if (1 == ret)
{
MYSQL_DB_THROW(mysql_stmt_errno(m_stmt), mysql_stmt_error(m_stmt), mysql_stmt_sqlstate(m_stmt), fmt::format("mysql_stmt_fetch fail,index={}", m_pStmtData->getIndex()));
}
return MYSQL_NO_DATA != ret;
}
void MysqlPrepareStmt::clear()
{
if (m_pStmtData->getType() == MYSQL_STMT_TYPE::UPDATE)
{
return;
}
//取没有取完的
while (0 == mysql_stmt_fetch(m_stmt));
if (m_pStmtData->getType() == MYSQL_STMT_TYPE::SELECT)
{
return;
}
do
{
MYSQL_RES* result = mysql_store_result(m_stmt->mysql);
mysql_free_result(result);
} while (!mysql_next_result(m_stmt->mysql));
}
uint64_t MysqlPrepareStmt::getInsertId()
{
return (uint64_t)mysql_stmt_insert_id(m_stmt);
}
uint64_t MysqlPrepareStmt::getAffectedRows()
{
return (uint64_t)mysql_stmt_affected_rows(m_stmt);
}
void MysqlPrepareStmt::bind(const DatabaseStmtBindArgs& args)
{
switch (args.bindType)
{
case DATABASE_STMT_BIND_TYPE_PARAM:
case DATABASE_STMT_BIND_TYPE_RESULT:
break;
default:
MYSQL_DB_THROW(fmt::format("index={},sql={}, args bindType error: {}", m_pStmtData->getIndex(), m_pStmtData->getSql(), (int)args.bindType));
break;
}
if (args.index >= m_bindSize[args.bindType])
{
MYSQL_DB_THROW(fmt::format("index={},sql={}, args idx({}:{}) >= max({})", m_pStmtData->getIndex(), m_pStmtData->getSql(), args.index, (args.indexName ? args.indexName : "NULL"), m_bindSize[args.bindType]));
}
if (nullptr == args.buffer)
{
MYSQL_DB_THROW(fmt::format("index={},sql={}, args buffer is null", m_pStmtData->getIndex(), m_pStmtData->getSql()));
}
if (DATABASE_STMT_TYPE::STRING == args.type ||
DATABASE_STMT_TYPE::VARSTRING == args.type ||
DATABASE_STMT_TYPE::TINYBLOB == args.type ||
DATABASE_STMT_TYPE::BLOB == args.type ||
DATABASE_STMT_TYPE::MEDIUMBLOB == args.type)
{
if (0 == args.bufferSize || nullptr == args.bufferUsed)
{
MYSQL_DB_THROW(fmt::format("index={},sql={}, args buffer size is 0", m_pStmtData->getIndex(), m_pStmtData->getSql()));
}
}
if (nullptr != m_pBind[args.bindType][args.index].buffer)
{
MYSQL_DB_THROW(fmt::format("index={},sql={}, bind args is exist, type={}, idx={}:{}", m_pStmtData->getIndex(), m_pStmtData->getSql(), (int)args.bindType, args.index, (args.indexName ? args.indexName : "NULL")));
}
switch (args.type)
{
case DATABASE_STMT_TYPE::BOOL:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_TINY;
m_pBind[args.bindType][args.index].is_unsigned = true;
break;
case DATABASE_STMT_TYPE::BYTE:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_TINY;
m_pBind[args.bindType][args.index].is_unsigned = false;
break;
case DATABASE_STMT_TYPE::UBYTE:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_TINY;
m_pBind[args.bindType][args.index].is_unsigned = true;
break;
case DATABASE_STMT_TYPE::SHORT:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_SHORT;
m_pBind[args.bindType][args.index].is_unsigned = false;
break;
case DATABASE_STMT_TYPE::USHORT:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_SHORT;
m_pBind[args.bindType][args.index].is_unsigned = true;
break;
case DATABASE_STMT_TYPE::INT:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_LONG;
m_pBind[args.bindType][args.index].is_unsigned = false;
break;
case DATABASE_STMT_TYPE::UINT:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_LONG;
m_pBind[args.bindType][args.index].is_unsigned = true;
break;
case DATABASE_STMT_TYPE::LONG:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_LONGLONG;
m_pBind[args.bindType][args.index].is_unsigned = false;
break;
case DATABASE_STMT_TYPE::ULONG:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_LONGLONG;
m_pBind[args.bindType][args.index].is_unsigned = true;
break;
case DATABASE_STMT_TYPE::FLOAT:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_FLOAT;
break;
case DATABASE_STMT_TYPE::DOUBLE:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_DOUBLE;
break;
case DATABASE_STMT_TYPE::STRING:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_STRING;
break;
case DATABASE_STMT_TYPE::VARSTRING:
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_VAR_STRING;
break;
case DATABASE_STMT_TYPE::TINYBLOB:
// 255 byte
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_TINY_BLOB;
break;
case DATABASE_STMT_TYPE::BLOB:
// (2^16-1) byte
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_BLOB;
break;
case DATABASE_STMT_TYPE::MEDIUMBLOB:
// (2^24-1) byte
m_pBind[args.bindType][args.index].buffer_type = MYSQL_TYPE_MEDIUM_BLOB;
break;
default:
MYSQL_DB_THROW(fmt::format("index={},sql={}, args type error", m_pStmtData->getIndex(), m_pStmtData->getSql()));
break;
}
m_pBind[args.bindType][args.index].buffer = args.buffer;
if (DATABASE_STMT_TYPE::STRING == args.type ||
DATABASE_STMT_TYPE::VARSTRING == args.type ||
DATABASE_STMT_TYPE::TINYBLOB == args.type ||
DATABASE_STMT_TYPE::BLOB == args.type ||
DATABASE_STMT_TYPE::MEDIUMBLOB == args.type)
{
m_pBind[args.bindType][args.index].buffer_length = args.bufferSize;
m_pBind[args.bindType][args.index].length = args.bufferUsed;
*args.bufferUsed = 0;
}
if (args.maybeNull)
{
m_pBind[args.bindType][args.index].is_null = args.maybeNull;
*args.maybeNull = 0;
}
}
MysqlPrepareStmt.h
#pragma once
#include "prerequisites.h"
#include
#include
#include
#include "MysqlStmtData.h"
enum DATABASE_STMT_BIND_TYPE
{
DATABASE_STMT_BIND_TYPE_PARAM, // 绑定参数
DATABASE_STMT_BIND_TYPE_RESULT, // 绑定结果
DATABASE_STMT_BIND_TYPE_MAX,
};
enum class DATABASE_STMT_TYPE
{
BOOL,
BYTE,
UBYTE,
SHORT,
USHORT,
INT,
UINT,
LONG,
ULONG,
FLOAT,
DOUBLE,
STRING,
VARSTRING,
TINYBLOB,
BLOB,
MEDIUMBLOB,
};
struct DatabaseStmtBindArgs
{
DatabaseStmtBindArgs(DATABASE_STMT_BIND_TYPE bindType_
, DATABASE_STMT_TYPE type_
, uint32_t index_
, const char* indexName_
, void* buffer_
, my_bool* maybeNull_)
: bindType(bindType_)
, type(type_)
, index(index_)
, indexName(indexName_)
, buffer(buffer_)
, bufferSize(0)
, bufferUsed(nullptr)
, maybeNull(maybeNull_)
{
}
DatabaseStmtBindArgs(DATABASE_STMT_BIND_TYPE bindType_
, DATABASE_STMT_TYPE type_
, uint32_t index_
, const char* indexName_
, void* buffer_
, uint32_t bufferSize_
, unsigned long* bufferUsed_
, my_bool* maybeNull_)
: bindType(bindType_)
, type(type_)
, index(index_)
, indexName(indexName_)
, buffer(buffer_)
, bufferSize(bufferSize_)
, bufferUsed(bufferUsed_)
, maybeNull(maybeNull_)
{
}
DATABASE_STMT_BIND_TYPE bindType;
DATABASE_STMT_TYPE type;
uint32_t index;
const char* indexName;
void* buffer;
uint32_t bufferSize; // string blob..
unsigned long* bufferUsed;
my_bool* maybeNull;
};
// mysql预处理
class MysqlPrepareStmt : private boost::noncopyable
{
public:
MysqlPrepareStmt();
virtual ~MysqlPrepareStmt();
virtual void load(MysqlStmtData* pStmtData);
virtual MysqlStmtData* getStmtData();
virtual void initialize(MYSQL* pMysql);
virtual void close();
virtual bool isvalid() const;
virtual void bind(const DatabaseStmtBindArgs& args);
public:
virtual int exec();
virtual bool fetch();
virtual void clear();
uint64_t getInsertId();
uint64_t getAffectedRows();
private:
void bindComplete();
void bindParam();
void bindResult();
private:
MYSQL_STMT* m_stmt;
MYSQL_BIND* m_pBind[DATABASE_STMT_BIND_TYPE_MAX];
uint32_t m_bindSize[DATABASE_STMT_BIND_TYPE_MAX];
MysqlStmtData* m_pStmtData;
};
inline MysqlStmtData* MysqlPrepareStmt::getStmtData()
{
return m_pStmtData;
}
inline bool MysqlPrepareStmt::isvalid() const
{
return nullptr != m_stmt && nullptr != m_pStmtData;
}