252 lines
7.1 KiB
C++
252 lines
7.1 KiB
C++
#include "stdafx.h"
|
|
#include "OdbcConnection.h"
|
|
|
|
OdbcCommand::OdbcCommand(HDBC conn) : m_connHandle(conn), m_odbcConnection(nullptr), m_hStmt(nullptr), m_resultCode(-1)
|
|
{
|
|
}
|
|
|
|
OdbcCommand::OdbcCommand(OdbcConnection * conn) : m_odbcConnection(conn), m_hStmt(nullptr)
|
|
{
|
|
m_connHandle = conn->GetConnectionHandle();
|
|
m_odbcConnection->AddCommand(this);
|
|
}
|
|
|
|
bool OdbcCommand::BindParameters()
|
|
{
|
|
for (auto itr = m_params.begin(); itr != m_params.end(); itr++)
|
|
{
|
|
auto param = itr->second;
|
|
|
|
if (!SQL_SUCCEEDED(SQLBindParameter(m_hStmt, itr->first + 1, param->GetParameterType(),
|
|
param->GetCDataType(), param->GetDataType(), param->GetDataTypeSize(), 0,
|
|
param->GetAddress(), param->GetDataTypeSize(), param->GetCBValue())))
|
|
{
|
|
if (m_odbcConnection != nullptr)
|
|
m_szError = m_odbcConnection->ReportSQLError(SQL_HANDLE_STMT, m_hStmt, _T("SQLBindParameter"), _T("Failed to bind parameter."));
|
|
else
|
|
m_szError = OdbcConnection::GetSQLError(SQL_HANDLE_STMT, m_hStmt);
|
|
|
|
Close();
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool OdbcCommand::Open(bool bRetry /*= false*/)
|
|
{
|
|
if (isOpen())
|
|
Close();
|
|
|
|
if (!SQL_SUCCEEDED(SQLAllocHandle(SQL_HANDLE_STMT, m_connHandle, &m_hStmt)))
|
|
{
|
|
if (m_odbcConnection != nullptr)
|
|
m_szError = m_odbcConnection->ReportSQLError(SQL_HANDLE_DBC, m_connHandle, _T("SQLAllocHandle"), _T("Failed to allocate statement handle."));
|
|
else
|
|
m_szError = OdbcConnection::GetSQLError(SQL_HANDLE_DBC, m_connHandle);
|
|
|
|
// Attempt full SQL reconnection once.
|
|
if (m_odbcConnection == nullptr || bRetry)
|
|
return false;
|
|
|
|
// Perform soft disconnect, preserving existing commands
|
|
m_odbcConnection->Close();
|
|
|
|
// Reconnect
|
|
m_odbcConnection->Connect();
|
|
|
|
// Now try running the statement once more time.
|
|
return Open(true);
|
|
}
|
|
|
|
SQLExecDirect(m_hStmt, (SQLTCHAR *)_T("SET NOCOUNT ON"), SQL_NTS);
|
|
return true;
|
|
}
|
|
|
|
bool OdbcCommand::Execute(const tstring & szSQL)
|
|
{
|
|
if (!Open())
|
|
return false;
|
|
|
|
#ifdef USE_SQL_TRACE
|
|
TRACE((szSQL + _T("\n")).c_str());
|
|
#endif
|
|
|
|
if (!BindParameters())
|
|
return false;
|
|
|
|
SQLRETURN result = SQLExecDirect(m_hStmt, (SQLTCHAR *)szSQL.c_str(), szSQL.length());
|
|
if (!(result == SQL_SUCCESS || result == SQL_SUCCESS_WITH_INFO || result == SQL_NO_DATA))
|
|
{
|
|
if (m_odbcConnection != nullptr)
|
|
m_szError = m_odbcConnection->ReportSQLError(SQL_HANDLE_STMT, m_hStmt, (TCHAR *)szSQL.c_str(), _T("Failed to execute statement."));
|
|
else
|
|
m_szError = OdbcConnection::GetSQLError(SQL_HANDLE_STMT, m_hStmt);
|
|
|
|
Close();
|
|
return false;
|
|
}
|
|
|
|
if (!MoveNext())
|
|
MoveNextSet();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OdbcCommand::Prepare(const tstring & szSQL)
|
|
{
|
|
if (!Open())
|
|
return false;
|
|
|
|
#ifdef USE_SQL_TRACE
|
|
TRACE((szSQL + _T("\n")).c_str());
|
|
#endif
|
|
|
|
if (!SQL_SUCCEEDED(SQLPrepare(m_hStmt, (SQLTCHAR *)szSQL.c_str(), szSQL.length())))
|
|
{
|
|
if (m_odbcConnection != nullptr)
|
|
m_szError = m_odbcConnection->ReportSQLError(SQL_HANDLE_STMT, m_hStmt, _T("SQLPrepare"), _T("Failed to prepare statement."));
|
|
else
|
|
m_szError = OdbcConnection::GetSQLError(SQL_HANDLE_STMT, m_hStmt);
|
|
|
|
Close();
|
|
return false;
|
|
}
|
|
|
|
if (!BindParameters())
|
|
return false;
|
|
|
|
SQLRETURN result = SQLExecute(m_hStmt);
|
|
if (!(result == SQL_SUCCESS || result == SQL_SUCCESS_WITH_INFO || result == SQL_NO_DATA))
|
|
{
|
|
if (m_odbcConnection != nullptr)
|
|
m_szError = m_odbcConnection->ReportSQLError(SQL_HANDLE_STMT, m_hStmt, (TCHAR *)szSQL.c_str(), _T("Failed to execute prepared statement."));
|
|
else
|
|
m_szError = OdbcConnection::GetSQLError(SQL_HANDLE_STMT, m_hStmt);
|
|
|
|
Close();
|
|
return false;
|
|
}
|
|
|
|
// If there's no rows to move through, skip to the next result set.
|
|
if (!MoveNext())
|
|
MoveNextSet();
|
|
|
|
ClearParameters();
|
|
return true;
|
|
}
|
|
|
|
bool OdbcCommand::MoveNext()
|
|
{
|
|
if (!isOpen())
|
|
return false;
|
|
|
|
return SQL_SUCCEEDED(m_resultCode = SQLFetch(m_hStmt));
|
|
}
|
|
|
|
bool OdbcCommand::MoveNextSet()
|
|
{
|
|
if (!isOpen())
|
|
return false;
|
|
|
|
return SQL_SUCCEEDED(m_resultCode = SQLMoreResults(m_hStmt));
|
|
}
|
|
|
|
#define ADD_ODBC_PARAMETER(name, type, sqlType) void OdbcCommand::AddParameter(SQLSMALLINT paramType, type *value, SQLLEN maxLength) { m_params.insert(std::make_pair(m_params.size(), new OdbcParameter(paramType, sqlType, (SQLPOINTER)value, maxLength))); } \
|
|
type OdbcCommand::Fetch ## name(int pos) { type value; SQLLEN cb = SQL_NTS; SQLGetData(m_hStmt, pos, sqlType, &value, 0, &cb); return value; } \
|
|
void OdbcCommand::Fetch ## name(int pos, type & value) { SQLLEN cb = SQL_NTS; SQLGetData(m_hStmt, pos, sqlType, &value, 0, &cb); }
|
|
ADD_ODBC_PARAMETER(Byte, uint8, SQL_C_UTINYINT)
|
|
ADD_ODBC_PARAMETER(SByte, int8, SQL_C_STINYINT)
|
|
ADD_ODBC_PARAMETER(UInt16, uint16, SQL_C_USHORT)
|
|
ADD_ODBC_PARAMETER(Int16, int16, SQL_C_SSHORT)
|
|
ADD_ODBC_PARAMETER(UInt32, uint32, SQL_C_ULONG)
|
|
ADD_ODBC_PARAMETER(Int32, int32, SQL_C_SLONG)
|
|
ADD_ODBC_PARAMETER(Single, float, SQL_C_FLOAT)
|
|
ADD_ODBC_PARAMETER(Double, double, SQL_C_DOUBLE)
|
|
ADD_ODBC_PARAMETER(UInt64, uint64, SQL_C_UBIGINT)
|
|
ADD_ODBC_PARAMETER(Int64, int64, SQL_C_SBIGINT)
|
|
#undef ADD_ODBC_PARAMETER
|
|
|
|
void OdbcCommand::AddParameter(SQLSMALLINT paramType, const char *value, SQLLEN maxLength = 1, SQLSMALLINT sqlDataType /*= SQL_CHAR*/)
|
|
{
|
|
m_params.insert(std::make_pair(m_params.size(), new OdbcParameter(paramType, sqlDataType, (SQLPOINTER)value, maxLength)));
|
|
}
|
|
|
|
bool OdbcCommand::FetchString(int pos, char *charArray, SQLLEN maxLength, SQLLEN *bufferSize)
|
|
{
|
|
memset(charArray, 0x00, maxLength);
|
|
return SQL_SUCCEEDED(SQLGetData(m_hStmt, pos, SQL_CHAR, charArray, maxLength, bufferSize));
|
|
}
|
|
|
|
bool OdbcCommand::FetchBinary(int pos, char *charArray, SQLLEN maxLength, SQLLEN *bufferSize)
|
|
{
|
|
return SQL_SUCCEEDED(SQLGetData(m_hStmt, pos, SQL_BINARY, charArray, maxLength, bufferSize));
|
|
}
|
|
|
|
bool OdbcCommand::FetchString(int pos, std::string & value)
|
|
{
|
|
SQLLEN bufferSize = 0;
|
|
char buffer[256] = "";
|
|
|
|
// Attempt to fetch "small" string of 256 bytes at most (should fit everything we'll need)
|
|
if (!FetchString(pos, buffer, sizeof(buffer), &bufferSize))
|
|
{
|
|
// Error fetching string, nothing we can do.
|
|
if (bufferSize <= 0)
|
|
return false;
|
|
|
|
// Allocate a buffer large enough for the string's actual length
|
|
std::unique_ptr<char> varBuffer(new char[bufferSize + 1]);
|
|
|
|
// If the string still couldn't be fetched, nothing we can do.
|
|
if (!FetchString(pos, varBuffer.get(), bufferSize + 1, &bufferSize))
|
|
return false;
|
|
|
|
value = varBuffer.get();
|
|
}
|
|
// String could be fetched, copy it over to the output var.
|
|
else
|
|
{
|
|
value = buffer;
|
|
}
|
|
|
|
// This line's necessary for SQL_CHAR type columns to trim the padding.
|
|
// NOTE: If we need the padding, we should be using the OTHER FetchString() method for it (to fetch it straight into a char array)
|
|
rtrim(value);
|
|
return true;
|
|
}
|
|
|
|
void OdbcCommand::ClearParameters()
|
|
{
|
|
if (m_params.empty())
|
|
return;
|
|
|
|
for (auto itr = m_params.begin(); itr != m_params.end(); itr++)
|
|
delete itr->second;
|
|
|
|
m_params.clear();
|
|
}
|
|
|
|
void OdbcCommand::Close()
|
|
{
|
|
if (m_hStmt != nullptr)
|
|
{
|
|
SQLCloseCursor(m_hStmt); // free results, if any
|
|
SQLFreeHandle(SQL_HANDLE_STMT, m_hStmt);
|
|
m_hStmt = nullptr;
|
|
}
|
|
}
|
|
|
|
void OdbcCommand::Detach()
|
|
{
|
|
m_odbcConnection = nullptr;
|
|
}
|
|
|
|
OdbcCommand::~OdbcCommand()
|
|
{
|
|
Close();
|
|
ClearParameters();
|
|
|
|
if (m_odbcConnection != nullptr)
|
|
m_odbcConnection->RemoveCommand(this);
|
|
} |