poco/Data/ODBC/src/Binder.cpp
Aleksandar Fabijanic fca08a18df step, date, time
2007-11-10 23:21:28 +00:00

451 lines
12 KiB
C++

//
// Binder.cpp
//
// $Id: //poco/Main/Data/ODBC/src/Binder.cpp#4 $
//
// Library: ODBC
// Package: ODBC
// Module: Binder
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
#include "Poco/Data/ODBC/Binder.h"
#include "Poco/Data/ODBC/Utility.h"
#include "Poco/Data/BLOB.h"
#include "Poco/Data/ODBC/ODBCException.h"
#include "Poco/DateTime.h"
#include "Poco/Exception.h"
#include <sql.h>
namespace Poco {
namespace Data {
namespace ODBC {
Binder::Binder(const StatementHandle& rStmt,
Binder::ParameterBinding dataBinding,
TypeInfo* pDataTypes):
_rStmt(rStmt),
_paramBinding(dataBinding),
_pTypeInfo(pDataTypes)
{
}
Binder::~Binder()
{
LengthVec::iterator itLen = _lengthIndicator.begin();
LengthVec::iterator itLenEnd = _lengthIndicator.end();
for(; itLen != itLenEnd; ++itLen) delete *itLen;
TimestampMap::iterator itTS = _timestamps.begin();
TimestampMap::iterator itTSEnd = _timestamps.end();
for(; itTS != itTSEnd; ++itTS) delete itTS->first;
StringMap::iterator itStr = _strings.begin();
StringMap::iterator itStrEnd = _strings.end();
for(; itStr != itStrEnd; ++itStr) std::free(itStr->first);
}
void Binder::bind(std::size_t pos, const std::string& val, Direction dir)
{
SQLPOINTER pVal = 0;
SQLINTEGER size = (SQLINTEGER) val.size();
if (isOutBound(dir))
{
try
{
Parameter p(_rStmt, pos);
size = (SQLUINTEGER) p.columnSize();
}
catch (StatementException&)
{
size = DEFAULT_PARAM_SIZE;
//On Linux, PostgreSQL driver segfaults on SQLGetDescField, so this is disabled for now
#ifdef POCO_OS_FAMILY_WINDOWS
SQLHDESC hIPD = 0;
if (!Utility::isError(SQLGetStmtAttr(_rStmt, SQL_ATTR_IMP_PARAM_DESC, &hIPD, 0, 0)))
{
SQLUINTEGER sz = 0;
if (!Utility::isError(SQLGetDescField(hIPD, (SQLSMALLINT) pos + 1, SQL_DESC_LENGTH, &sz, sizeof(sz), 0)) &&
sz > 0)
{
size = sz;
}
}
#endif
}
char* pChar = (char*) std::calloc(size, sizeof(char));
pVal = (SQLPOINTER) pChar;
_outParams.insert(ParamMap::value_type(pVal, size));
_strings.insert(StringMap::value_type(pChar, const_cast<std::string*>(&val)));
}
else if (isInBound(dir))
{
pVal = (SQLPOINTER) val.c_str();
_inParams.insert(ParamMap::value_type(pVal, size));
}
else
throw IllegalStateException("Parameter must be [in] OR [out] bound.");
SQLLEN* pLenIn = new SQLLEN;
*pLenIn = SQL_NTS;
if (PB_AT_EXEC == _paramBinding)
*pLenIn = SQL_LEN_DATA_AT_EXEC(size);
_lengthIndicator.push_back(pLenIn);
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_CHAR,
SQL_LONGVARCHAR,
(SQLUINTEGER) size,
0,
pVal,
(SQLINTEGER) size,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(std::string)");
}
}
void Binder::bind(std::size_t pos, const Poco::Data::BLOB& val, Direction dir)
{
if (isOutBound(dir) || !isInBound(dir))
throw NotImplementedException("BLOB parameter type can only be inbound.");
SQLPOINTER pVal = (SQLPOINTER) val.rawContent();
SQLINTEGER size = (SQLINTEGER) val.size();
_inParams.insert(ParamMap::value_type(pVal, size));
SQLLEN* pLenIn = new SQLLEN;
*pLenIn = size;
if (PB_AT_EXEC == _paramBinding)
*pLenIn = SQL_LEN_DATA_AT_EXEC(size);
_lengthIndicator.push_back(pLenIn);
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
SQL_PARAM_INPUT,
SQL_C_BINARY,
SQL_LONGVARBINARY,
(SQLUINTEGER) size,
0,
pVal,
(SQLINTEGER) size,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(BLOB)");
}
}
void Binder::bind(std::size_t pos, const Date& val, Direction dir)
{
SQLINTEGER size = (SQLINTEGER) sizeof(SQL_DATE_STRUCT);
SQLLEN* pLenIn = new SQLLEN;
*pLenIn = size;
_lengthIndicator.push_back(pLenIn);
SQL_DATE_STRUCT* pDS = new SQL_DATE_STRUCT;
Utility::dateSync(*pDS, val);
_dates.insert(DateMap::value_type(pDS, const_cast<Date*>(&val)));
SQLINTEGER colSize = 0;
SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, SQL_TYPE_DATE, colSize, decDigits);
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_DATE,
SQL_DATE,
colSize,
decDigits,
(SQLPOINTER) pDS,
0,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(BLOB)");
}
}
void Binder::bind(std::size_t pos, const Time& val, Direction dir)
{
SQLINTEGER size = (SQLINTEGER) sizeof(SQL_TIME_STRUCT);
SQLLEN* pLenIn = new SQLLEN;
*pLenIn = size;
_lengthIndicator.push_back(pLenIn);
SQL_TIME_STRUCT* pTS = new SQL_TIME_STRUCT;
Utility::timeSync(*pTS, val);
_times.insert(TimeMap::value_type(pTS, const_cast<Time*>(&val)));
SQLINTEGER colSize = 0;
SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, SQL_TYPE_TIME, colSize, decDigits);
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TIME,
SQL_TIME,
colSize,
decDigits,
(SQLPOINTER) pTS,
0,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(BLOB)");
}
}
void Binder::bind(std::size_t pos, const Poco::DateTime& val, Direction dir)
{
SQLINTEGER size = (SQLINTEGER) sizeof(SQL_TIMESTAMP_STRUCT);
SQLLEN* pLenIn = new SQLLEN;
*pLenIn = size;
_lengthIndicator.push_back(pLenIn);
SQL_TIMESTAMP_STRUCT* pTS = new SQL_TIMESTAMP_STRUCT;
Utility::dateTimeSync(*pTS, val);
_timestamps.insert(TimestampMap::value_type(pTS, const_cast<DateTime*>(&val)));
SQLINTEGER colSize = 0;
SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, SQL_TYPE_TIMESTAMP, colSize, decDigits);
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TIMESTAMP,
SQL_TIMESTAMP,
colSize,
decDigits,
(SQLPOINTER) pTS,
0,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(BLOB)");
}
}
void Binder::bind(std::size_t pos, const NullData& val, Direction dir)
{
if (isOutBound(dir) || !isInBound(dir))
throw NotImplementedException("NULL parameter type can only be inbound.");
switch (val)
{
case NULL_GENERIC:
case NULL_INT8: bindNull(pos, SQL_C_STINYINT); break;
case NULL_UINT8: bindNull(pos, SQL_C_UTINYINT); break;
case NULL_INT16: bindNull(pos, SQL_C_SSHORT); break;
case NULL_UINT16: bindNull(pos, SQL_C_USHORT); break;
case NULL_INT32: bindNull(pos, SQL_C_SLONG); break;
case NULL_UINT32: bindNull(pos, SQL_C_ULONG); break;
case NULL_INT64: bindNull(pos, SQL_C_SBIGINT); break;
case NULL_UINT64: bindNull(pos, SQL_C_UBIGINT); break;
case NULL_BOOL: bindNull(pos, SQL_C_BIT); break;
case NULL_FLOAT: bindNull(pos, SQL_C_FLOAT); break;
case NULL_DOUBLE: bindNull(pos, SQL_C_DOUBLE); break;
case NULL_STRING: bindNull(pos, SQL_C_CHAR); break;
case NULL_BLOB: bindNull(pos, SQL_C_BINARY); break;
case NULL_DATE: bindNull(pos, SQL_C_DATE); break;
case NULL_TIME: bindNull(pos, SQL_C_TIME); break;
case NULL_TIMESTAMP: bindNull(pos, SQL_C_TIMESTAMP); break;
default:
throw DataFormatException("Unsupported data type.");
}
}
void Binder::bindNull(std::size_t pos, SQLSMALLINT cDataType)
{
_inParams.insert(ParamMap::value_type(0, 0));
SQLLEN* pLenIn = new SQLLEN;
*pLenIn = SQL_NULL_DATA;
_lengthIndicator.push_back(pLenIn);
SQLINTEGER colSize = 0;
SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, cDataType, colSize, decDigits);
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
SQL_PARAM_INPUT,
cDataType,
Utility::sqlDataType(cDataType),
colSize,
decDigits,
0,
0,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter()");
}
}
std::size_t Binder::parameterSize(SQLPOINTER pAddr) const
{
ParamMap::const_iterator it = _inParams.find(pAddr);
if (it != _inParams.end()) return it->second;
it = _outParams.find(pAddr);
if (it != _outParams.end()) return it->second;
throw NotFoundException("Requested data size not found.");
}
void Binder::bind(std::size_t pos, const char* const &pVal, Direction dir)
{
throw NotImplementedException("char* binding not implemented, Use std::string instead.");
}
SQLSMALLINT Binder::toODBCDirection(Direction dir) const
{
bool in = isInBound(dir);
bool out = isOutBound(dir);
SQLSMALLINT ioType = SQL_PARAM_TYPE_UNKNOWN;
if (in && out) ioType = SQL_PARAM_INPUT_OUTPUT;
else if(in) ioType = SQL_PARAM_INPUT;
else if(out) ioType = SQL_PARAM_OUTPUT;
else throw Poco::IllegalStateException("Binder not bound (must be [in] OR [out]).");
return ioType;
}
void Binder::synchronize()
{
if (_dates.size())
{
DateMap::iterator itTS = _dates.begin();
DateMap::iterator itTSEnd = _dates.end();
for(; itTS != itTSEnd; ++itTS)
Utility::dateSync(*itTS->second, *itTS->first);
}
if (_times.size())
{
TimeMap::iterator itTS = _times.begin();
TimeMap::iterator itTSEnd = _times.end();
for(; itTS != itTSEnd; ++itTS)
Utility::timeSync(*itTS->second, *itTS->first);
}
if (_timestamps.size())
{
TimestampMap::iterator itTS = _timestamps.begin();
TimestampMap::iterator itTSEnd = _timestamps.end();
for(; itTS != itTSEnd; ++itTS)
Utility::dateTimeSync(*itTS->second, *itTS->first);
}
if (_strings.size())
{
StringMap::iterator itStr = _strings.begin();
StringMap::iterator itStrEnd = _strings.end();
for(; itStr != itStrEnd; ++itStr)
itStr->second->assign(itStr->first, strlen(itStr->first));
}
}
void Binder::getColSizeAndPrecision(std::size_t pos,
SQLSMALLINT cDataType,
SQLINTEGER& colSize,
SQLSMALLINT& decDigits)
{
// Not all drivers are equally willing to cooperate in this matter.
// Hence the funky flow control.
try
{
if (_pTypeInfo)
{
colSize = _pTypeInfo->getInfo(cDataType, "COLUMN_SIZE");
decDigits = _pTypeInfo->getInfo(cDataType, "MINIMUM_SCALE");
return;
}
else
throw NotFoundException();
}catch (NotFoundException&)
{
try
{
Parameter p(_rStmt, pos);
colSize = (SQLINTEGER) p.columnSize();
decDigits = (SQLSMALLINT) p.decimalDigits();
return;
}catch (StatementException&)
{
try
{
ODBCColumn c(_rStmt, pos);
colSize = (SQLINTEGER) c.length();
decDigits = (SQLSMALLINT) c.precision();
return;
}catch (StatementException&) { }
}
}
// no success, set to zero and hope for the best
// (most drivers do not require these most of the times anyway)
colSize = 0;
decDigits = 0;
return;
}
} } } // namespace Poco::Data::ODBC