Код: Выделить всё
// allocate statement
// prepare statement
// bind return code
template
inline void StoredProc::bindInputParameter(const T& param)
{
lengths.push_back(0); // member of StoredProc class
SQLLEN* len = &lengths.back();
bound_parameters.push_back(param); // member of StoredProc class
auto& stored_param = std::any_cast(bound_parameters.back());
if constexpr (std::is_same_v) {
strings.push_back(param);
std::string& stored_param = strings.back();
errorCode = SQLBindParameter(
statementHandle, paramNumber++, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_VARCHAR, 80,
0, (SQLPOINTER)(stored_param.c_str()), stored_param.size() + 1,
NULL
);
}
else if constexpr (std::is_same_v) {
errorCode = SQLBindParameter(
statementHandle, paramNumber++, SQL_PARAM_INPUT,
SQL_C_WCHAR, SQL_WVARCHAR, 80,
0, (SQLPOINTER)(stored_param.c_str()), (stored_param.size() + 1) * sizeof(wchar_t),
NULL
);
}
// other types ...
if (!SQL_SUCCEEDED(errorCode)) { // no errors
cleanup();
throw std::exception(Errors::SQL_STATEMENT_BIND_INPUT_ERROR.data());
}
}
//... bind output parameters
// SQLExecute(statementHandle);
// return code == 0 and no errors registered
Ideb std :: ecector , использовал его для обработки `std :: string` parameters привязка, и это сработало. Однако логика загромождена в этом подходе. < /P>
Код: Выделить всё
// this works
if constexpr (std::is_same_v) {
strings.push_back(param); //member in StoredProc
std::string& stored_param = strings.back();
errorCode = SQLBindParameter(
statementHandle, paramNumber++, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_VARCHAR, 80,
0, (SQLPOINTER)(stored_param.c_str()), stored_param.size() + 1,
NULL
);
}
Весь код класса:
Код: Выделить всё
//SoredProc.h
#pragma once
#include "Errors.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
using procedureName = std::string;
class StoredProc {
public:
StoredProc(const SQLHDBC dbH) : connectionHandle{ dbH } {}
~StoredProc() = default;
template
std::tuple execute(const procedureName& p, const InputTypes&... parameters);
protected:
inline static size_t MAX_SIZE_BUFFER{ 255 };
static void handleDiagnosticRecord(SQLHANDLE hHandle, SQLSMALLINT hType, RETCODE RetCode);
void allocate();
void prepare(const procedureName& p, const size_t totalNumberOfParameters);
void bindReturnCode();
template
void bindOutputs(std::tuple& results);
template
void bindInputs(const InputTypes&... parameters);
template
void call(const procedureName& p, const size_t totalNumberOfParameters, std::tuple& results, const InputTypes&... parameters);
template
void fetchOutputs(const size_t totalNumberOfParameters, std::tuple& results);
void cleanup();
template
void bindInputParameter(const T& param);
template
void bindOutputParameter(const T& param);
template
void fetchOneOutput(T& param, size_t index);
SQLHDBC connectionHandle{};
SQLHSTMT statementHandle{};
SQLRETURN errorCode{ 0 };
SQLUSMALLINT paramNumber{ 1 };
SQLINTEGER returnCode{ -1 };
std::vector bound_parameters{};
std::vector lengths{};
};
template
inline std::tuple StoredProc::execute(const procedureName& p, const InputTypes&... parameters)
{
std::tuple results{};
size_t totalNumberOfParameters = std::tuple_size::value + sizeof...(parameters);
allocate();
prepare(p, totalNumberOfParameters);
bindReturnCode();
bindInputs(parameters...);
bindOutputs(results);
call(p, totalNumberOfParameters, results, parameters...);
fetchOutputs(totalNumberOfParameters, results);
cleanup();
return results;
}
template
inline void StoredProc::bindInputs(const InputTypes&... parameters)
{
(bindInputParameter(parameters), ...);
}
template
inline void StoredProc::bindOutputs(std::tuple& results)
{
std::apply([this](auto&... params) {
(this->bindOutputParameter(params), ...);
},
results);
}
template
inline void StoredProc::call(const procedureName& p, const size_t totalNumberOfParameters, std::tuple& results, const InputTypes&... parameters)
{
errorCode = SQLExecute(statementHandle);
if (!SQL_SUCCEEDED(errorCode)) {
cleanup();
throw std::exception(Errors::SQL_EXECUTE_ERROR.data());
}
if (returnCode) {
cleanup();
throw std::exception(Errors::SQL_RETURN_CODE_ERROR.data());
}
}
template
inline void StoredProc::fetchOutputs(const size_t totalNumberOfParameters, std::tuple& results)
{
}
template
inline void StoredProc::bindInputParameter(const T& param)
{
lengths.push_back(0);
SQLLEN* len = &lengths.back();
bound_parameters.push_back(param); // store value
auto& stored_param = std::any_cast(bound_parameters.back());
if constexpr (std::is_same_v) {
*len = sizeof(int);
errorCode = SQLBindParameter(
statementHandle, paramNumber++, SQL_PARAM_INPUT,
SQL_C_LONG, SQL_INTEGER, 10,
0, (SQLPOINTER)std::addressof(stored_param), 0,
NULL
);
}
else if constexpr (std::is_same_v) {
*len = sizeof(bool);
errorCode = SQLBindParameter(
statementHandle, paramNumber++, SQL_PARAM_INPUT,
SQL_C_BIT, SQL_BIT, 1,
0, (SQLPOINTER)std::addressof(stored_param), 0,
NULL
);
}
else if constexpr (std::is_same_v) { // here wrong
errorCode = SQLBindParameter(
statementHandle, paramNumber++, SQL_PARAM_INPUT,
SQL_C_CHAR, SQL_VARCHAR, 80,
0, (SQLPOINTER)(stored_param.c_str()), stored_param.size() + 1,
NULL
);
}
else if constexpr (std::is_same_v) { // here correct
errorCode = SQLBindParameter(
statementHandle, paramNumber++, SQL_PARAM_INPUT,
SQL_C_WCHAR, SQL_WVARCHAR, 80,
0, (SQLPOINTER)(stored_param.c_str()), (stored_param.size() + 1) * sizeof(wchar_t),
NULL
);
}
if (!SQL_SUCCEEDED(errorCode)) {
cleanup();
throw std::exception(Errors::SQL_STATEMENT_BIND_INPUT_ERROR.data());
}
}
template
inline void StoredProc::bindOutputParameter(const T& param)
{
lengths.push_back(0);
SQLLEN* len = &lengths.back();
bound_parameters.push_back(param);
auto& stored_param = std::any_cast(bound_parameters.back());
if constexpr (std::is_same_v) {
errorCode = SQLBindParameter(
statementHandle, paramNumber++, SQL_PARAM_OUTPUT,
SQL_C_LONG, SQL_INTEGER, sizeof(stored_param),
0, (SQLPOINTER)std::addressof(stored_param), sizeof(stored_param),
len
);
}
else if constexpr (std::is_same_v) {
errorCode = SQLBindParameter(
statementHandle, paramNumber++, SQL_PARAM_OUTPUT,
SQL_C_TINYINT, SQL_TINYINT, 1,
0, (SQLPOINTER)std::addressof(stored_param), 0,
len
);
}
else if constexpr (std::is_same_v) {
*len = MAX_SIZE_BUFFER;
stored_param.resize(*len);
errorCode = SQLBindParameter(
statementHandle, paramNumber++, SQL_PARAM_OUTPUT,
SQL_C_CHAR, SQL_VARCHAR, 80,
0, (SQLPOINTER)stored_param.data(), static_cast(stored_param.size()),
len
);
}
else if constexpr (std::is_same_v) {
*len = MAX_SIZE_BUFFER * sizeof(wchar_t);
stored_param.resize(MAX_SIZE_BUFFER);
errorCode = SQLBindParameter(
statementHandle, paramNumber++, SQL_PARAM_OUTPUT,
SQL_C_WCHAR, SQL_WVARCHAR, 80,
0, (SQLPOINTER)stored_param.data(), static_cast(stored_param.size()) * sizeof(wchar_t),
len
);
}
if (!SQL_SUCCEEDED(errorCode)) {
cleanup();
throw std::exception(Errors::SQL_STATEMENT_BIND_OUTPUT_ERROR.data());
}
}
template
inline void StoredProc::fetchOneOutput(T& param, size_t any_index)
{
}
//StoredProc.cpp
#include "StoredProc.h"
void StoredProc::handleDiagnosticRecord(SQLHANDLE hHandle, SQLSMALLINT hType, RETCODE RetCode)
{
SQLSMALLINT iRec = 0;
SQLINTEGER iError;
WCHAR wszMessage[1000];
WCHAR wszState[SQL_SQLSTATE_SIZE + 1];
if (RetCode == SQL_INVALID_HANDLE)
{
fwprintf(stderr, L"Invalid handle!\n");
return;
}
while (SQLGetDiagRecW(
hType, hHandle, ++iRec,
wszState, &iError, wszMessage,
(SQLSMALLINT)(sizeof(wszMessage) / sizeof(WCHAR)),
(SQLSMALLINT*)NULL) == SQL_SUCCESS)
if (wcsncmp(wszState, L"01004", 5))
fwprintf(stderr, L"[%5.5s] %s (%d)\n", wszState, wszMessage, iError);
}
void StoredProc::allocate()
{
errorCode = SQLAllocHandle(SQL_HANDLE_STMT, connectionHandle, &statementHandle);
if (!SQL_SUCCEEDED(errorCode))
throw std::exception(Errors::SQL_STATEMENT_ALLOC_ERROR.data());
}
void StoredProc::prepare(const procedureName& p, const size_t totalNumberOfParameters)
{
std::string procedureCall{ "{ ? = CALL " + p + "(" };
for (size_t i = 0; i < totalNumberOfParameters; ++i)
procedureCall += "?, ";
if (totalNumberOfParameters > 0)
procedureCall.erase(procedureCall.end() - 2, procedureCall.end());
procedureCall += ") }";
errorCode = SQLPrepare(statementHandle, (SQLCHAR*)(procedureCall.c_str()), SQL_NTS);
if (!SQL_SUCCEEDED(errorCode)) {
cleanup();
throw std::exception(Errors::SQL_STATEMENT_PREP_ERROR.data());
}
}
void StoredProc::bindReturnCode()
{
// must be first
lengths.push_back(0);
SQLLEN* len = &lengths.back();
errorCode = SQLBindParameter(
statementHandle, paramNumber++, SQL_PARAM_OUTPUT,
SQL_C_LONG, SQL_INTEGER, sizeof(returnCode),
0, reinterpret_cast(&returnCode), sizeof(returnCode),
len
);
if (!SQL_SUCCEEDED(errorCode)) {
cleanup();
throw std::exception(Errors::SQL_STATEMENT_BIND_RETCODE_ERROR.data());
}
}
void StoredProc::cleanup()
{
handleDiagnosticRecord(statementHandle, SQL_HANDLE_STMT, errorCode);
errorCode = SQLFreeHandle(SQL_HANDLE_STMT, statementHandle);
if (!SQL_SUCCEEDED(errorCode)) {
handleDiagnosticRecord(statementHandle, SQL_HANDLE_STMT, errorCode);
throw std::exception(Errors::SQL_STATEMENT_CLEANUP_ERROR.data());
}
}
< /code>
Вы сможете вызвать любую хранимую процедуру как: < /p>
#include "StoredProc.h"
int main()
{
// connect to database
StoredProc proc{ connectionHandle };
// define parameters;
std::string in_param1 = "param1";
std::wstring in_param2 = L"something";
int in_param3 = 6;
bool in_param4 = true;
// order of types matters: returns an int and a bool as OUTPUT parameters
auto t = proc.execute("[dbo][]", in_param1, in_param2, in_param3, in_param4);
return 0;
}
Подробнее здесь: https://stackoverflow.com/questions/797 ... rocedure-c