Skip to content

Commit

Permalink
fix(Data): Poco::Data::ODBC-dbEncoding property not used for insert/u…
Browse files Browse the repository at this point in the history
…pdate #3396
  • Loading branch information
aleks-f committed May 3, 2022
1 parent 87a1294 commit 08d68ea
Show file tree
Hide file tree
Showing 22 changed files with 467 additions and 72 deletions.
37 changes: 28 additions & 9 deletions Data/ODBC/include/Poco/Data/ODBC/Binder.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder
Binder(const StatementHandle& rStmt,
std::size_t maxFieldSize,
ParameterBinding dataBinding = PB_IMMEDIATE,
const TypeInfo* pDataTypes = 0);
const TypeInfo* pDataTypes = 0,
Poco::TextEncoding::Ptr pFromEncoding = nullptr,
Poco::TextEncoding::Ptr pDBEncoding = nullptr);
/// Creates the Binder.

~Binder();
Expand Down Expand Up @@ -538,7 +540,7 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder
}

template <typename C>
void bindImplContainerString(std::size_t pos, const C& val, Direction dir)
void bindImplContainerString(std::size_t pos, const C& valC, Direction dir)
/// Utility function to bind containers of strings.
{
if (isOutBound(dir) || !isInBound(dir))
Expand All @@ -547,7 +549,19 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder
if (PB_IMMEDIATE != _paramBinding)
throw InvalidAccessException("Containers can only be bound immediately.");

std::size_t length = val.size();
const C* pVal = 0;
if (!transcodeRequired()) pVal = &valC;
else
{
pVal = new C(valC.size());
typename C::const_iterator valIt = valC.begin();
typename C::const_iterator valEnd = valC.end();
typename C::iterator tcIt = const_cast<C*>(pVal)->begin();
for (; valIt != valEnd; ++valIt, ++tcIt)
transcode(*valIt, *tcIt);
}

std::size_t length = pVal->size();

if (0 == length)
throw InvalidArgumentException("Empty container not allowed.");
Expand All @@ -560,7 +574,7 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder

if (size == _maxFieldSize)
{
getMinValueSize(val, size);
getMinValueSize(*pVal, size);
// accomodate for terminating zero
if (size != _maxFieldSize) ++size;
}
Expand All @@ -574,20 +588,25 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder
if (_charPtrs.size() <= pos)
_charPtrs.resize(pos + 1, 0);

_charPtrs[pos] = (char*) std::calloc(val.size() * size, sizeof(char));
_charPtrs[pos] = (char*) std::calloc(pVal->size() * size, sizeof(char));

std::string typeID = typeid(*pVal).name();
std::size_t strSize;
std::size_t offset = 0;
typename C::const_iterator it = val.begin();
typename C::const_iterator end = val.end();
typename C::const_iterator it = pVal->begin();
typename C::const_iterator end = pVal->end();
for (; it != end; ++it)
{
strSize = it->size();
if (strSize > size)
throw LengthExceededException("SQLBindParameter(std::vector<std::string>)");
{
if (transcodeRequired()) delete pVal;
throw LengthExceededException(Poco::format("SQLBindParameter(%s)", typeID));
}
std::memcpy(_charPtrs[pos] + offset, it->c_str(), strSize);
offset += size;
}
if (transcodeRequired()) delete pVal;

if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
Expand All @@ -600,7 +619,7 @@ class ODBC_API Binder: public Poco::Data::AbstractBinder
(SQLINTEGER) size,
&(*_vecLengthIndicator[pos])[0])))
{
throw StatementException(_rStmt, "SQLBindParameter(std::vector<std::string>)");
throw StatementException(_rStmt, Poco::format("SQLBindParameter(%s)", typeID));
}
}

Expand Down
3 changes: 2 additions & 1 deletion Data/ODBC/include/Poco/Data/ODBC/Extractor.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ class ODBC_API Extractor: public Poco::Data::AbstractExtractor

Extractor(const StatementHandle& rStmt,
Preparator::Ptr pPreparator,
Poco::TextEncoding::Ptr pDBEncoding = nullptr);
Poco::TextEncoding::Ptr pDBEncoding = nullptr,
Poco::TextEncoding::Ptr pToEncoding = nullptr);
/// Creates the Extractor.

~Extractor();
Expand Down
62 changes: 53 additions & 9 deletions Data/ODBC/src/Binder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ namespace ODBC {
Binder::Binder(const StatementHandle& rStmt,
std::size_t maxFieldSize,
Binder::ParameterBinding dataBinding,
const TypeInfo* pDataTypes):
const TypeInfo* pDataTypes,
Poco::TextEncoding::Ptr pFromEncoding,
Poco::TextEncoding::Ptr pDBEncoding):
Poco::Data::AbstractBinder(pFromEncoding, pDBEncoding),
_rStmt(rStmt),
_paramBinding(dataBinding),
_pTypeInfo(pDataTypes),
Expand Down Expand Up @@ -99,13 +102,30 @@ void Binder::freeMemory()
DateTimeVecVec::iterator itDateTimeVec = _dateTimeVecVec.begin();
DateTimeVecVec::iterator itDateTimeVecEnd = _dateTimeVecVec.end();
for (; itDateTimeVec != itDateTimeVecEnd; ++itDateTimeVec) delete *itDateTimeVec;

if (transcodeRequired() && _inParams.size())
{
ParamMap::iterator itInParams = _inParams.begin();
ParamMap::iterator itInParamsEnd = _inParams.end();
for (; itInParams != itInParamsEnd; ++itInParams) free(itInParams->first);
}
}


void Binder::bind(std::size_t pos, const std::string& val, Direction dir)
{
char* pTCVal = 0;
SQLINTEGER size = 0;
if (transcodeRequired())
{
std::string tcVal;
transcode(val, tcVal);
size = (SQLINTEGER)tcVal.size();
pTCVal = reinterpret_cast<char*>(std::calloc((size_t)size+1, 1));
std::memcpy(pTCVal, tcVal.data(), size);
}
else size = (SQLINTEGER)val.size();
SQLPOINTER pVal = 0;
SQLINTEGER size = (SQLINTEGER) val.size();
SQLINTEGER colSize = 0;
SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, SQL_C_CHAR, colSize, decDigits, val.size());
Expand All @@ -120,8 +140,16 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir)
}
else if (isInBound(dir))
{
pVal = (SQLPOINTER) val.c_str();
_inParams.insert(ParamMap::value_type(pVal, size));
if (!pTCVal)
{
pVal = (SQLPOINTER)val.c_str();
_inParams.insert(ParamMap::value_type(pVal, size));
}
else
{
pVal = (SQLPOINTER)pTCVal;
_inParams.insert(ParamMap::value_type(pVal, size));
}
}
else
throw InvalidArgumentException("Parameter must be [in] OR [out] bound.");
Expand Down Expand Up @@ -421,12 +449,28 @@ void Binder::synchronize()
Utility::dateTimeSync(*it->second, *it->first);
}

if (_strings.size())
if (!transcodeRequired())
{
StringMap::iterator it = _strings.begin();
StringMap::iterator end = _strings.end();
for(; it != end; ++it)
it->second->assign(it->first, std::strlen(it->first));
if (_strings.size())
{
StringMap::iterator it = _strings.begin();
StringMap::iterator end = _strings.end();
for (; it != end; ++it)
it->second->assign(it->first, std::strlen(it->first));
}
}
else
{
if (_strings.size())
{
StringMap::iterator it = _strings.begin();
StringMap::iterator end = _strings.end();
for (; it != end; ++it)
{
std::string str(it->first, std::strlen(it->first));
reverseTranscode(str, *it->second);
}
}
}

if (_uuids.size())
Expand Down
9 changes: 6 additions & 3 deletions Data/ODBC/src/ConnectionHandle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "Poco/Data/ODBC/ConnectionHandle.h"
#include "Poco/Data/ODBC/Utility.h"
#include "Poco/Data/ODBC/ODBCException.h"
#include "Poco/Error.h"
#include "Poco/Debugger.h"


namespace Poco {
Expand Down Expand Up @@ -42,10 +44,11 @@ ConnectionHandle::~ConnectionHandle()
{
SQLDisconnect(_hdbc);
SQLRETURN rc = SQLFreeHandle(SQL_HANDLE_DBC, _hdbc);

if (_ownsEnvironment) delete _pEnvironment;

poco_assert (!Utility::isError(rc));
#if defined(_DEBUG)
if (Utility::isError(rc))
Debugger::enter(Poco::Error::getMessage(Poco::Error::last()), __FILE__, __LINE__);
#endif
}
catch (...)
{
Expand Down
7 changes: 6 additions & 1 deletion Data/ODBC/src/EnvironmentHandle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
#include "Poco/Data/ODBC/EnvironmentHandle.h"
#include "Poco/Data/ODBC/Utility.h"
#include "Poco/Data/ODBC/ODBCException.h"
#include "Poco/Error.h"
#include "Poco/Debugger.h"


namespace Poco {
Expand Down Expand Up @@ -42,7 +44,10 @@ EnvironmentHandle::~EnvironmentHandle()
try
{
SQLRETURN rc = SQLFreeHandle(SQL_HANDLE_ENV, _henv);
poco_assert (!Utility::isError(rc));
#if defined(_DEBUG)
if (Utility::isError(rc))
Debugger::enter(Poco::Error::getMessage(Poco::Error::last()), __FILE__, __LINE__);
#endif
}
catch (...)
{
Expand Down
4 changes: 3 additions & 1 deletion Data/ODBC/src/Extractor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ const std::string Extractor::FLD_SIZE_EXCEEDED_FMT = "Specified data size (%z by

Extractor::Extractor(const StatementHandle& rStmt,
Preparator::Ptr pPreparator,
TextEncoding::Ptr pDBEncoding): AbstractExtractor(pDBEncoding),
TextEncoding::Ptr pDBEncoding,
Poco::TextEncoding::Ptr pToEncoding): AbstractExtractor(pDBEncoding, pToEncoding),
_rStmt(rStmt),
_pPreparator(pPreparator),
_dataExtraction(pPreparator->getDataExtraction())
Expand Down Expand Up @@ -719,6 +720,7 @@ bool Extractor::extract(std::size_t pos, std::string& val)
else
ret = extractBoundImpl(pos, result);
transcode(result, val);

}

return ret;
Expand Down
6 changes: 4 additions & 2 deletions Data/ODBC/src/ODBCStatementImpl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ void ODBCStatementImpl::compileImpl()

std::size_t maxFieldSize = AnyCast<std::size_t>(session().getProperty("maxFieldSize"));

_pBinder = new Binder(_stmt, maxFieldSize, bind, pDT);
_pBinder = new Binder(_stmt, maxFieldSize, bind, pDT, TextEncoding::find("UTF-8"),
TextEncoding::find(Poco::RefAnyCast<std::string>(session().getProperty("dbEncoding"))));

makeInternalExtractors();
doPrepare();
Expand Down Expand Up @@ -146,7 +147,8 @@ void ODBCStatementImpl::addPreparator()
_preparations.push_back(new Preparator(*_preparations[0]));

_extractors.push_back(new Extractor(_stmt, _preparations.back(),
TextEncoding::find(Poco::RefAnyCast<std::string>(session().getProperty("dbEncoding")))));
TextEncoding::find(Poco::RefAnyCast<std::string>(session().getProperty("dbEncoding"))),
TextEncoding::find("UTF-8")));
}


Expand Down
19 changes: 18 additions & 1 deletion Data/ODBC/testsuite/src/ODBCSQLServerTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ using Poco::DateTime;


ODBCTest::SessionPtr ODBCSQLServerTest::_pSession;
ODBCTest::SessionPtr ODBCSQLServerTest::_pEncSession;
ODBCTest::ExecPtr ODBCSQLServerTest::_pExecutor;
std::string ODBCSQLServerTest::_driver = MS_SQL_SERVER_ODBC_DRIVER;
std::string ODBCSQLServerTest::_dsn = MS_SQL_SERVER_DSN;
Expand Down Expand Up @@ -733,13 +734,28 @@ void ODBCSQLServerTest::recreateUnicodeTable()
}


void ODBCSQLServerTest::recreateEncodingTables()
{
#if defined (POCO_ODBC_UNICODE)
dropObject("TABLE", "Latin1Table");
try { session() << "CREATE TABLE Latin1Table (str NVARCHAR(30))", now; }
catch (ConnectionException& ce) { std::cout << ce.toString() << std::endl; fail("recreateEncodingTables()"); }
catch (StatementException& se) { std::cout << se.toString() << std::endl; fail("recreateEncodingTables()"); }
#endif
}


CppUnit::Test* ODBCSQLServerTest::suite()
{
if ((_pSession = init(_driver, _dsn, _uid, _pwd, _connectString, _db)))
{
std::cout << "*** Connected to [" << _driver << "] test database." << std::endl;
std::string enc = "Latin1";
if ((_pEncSession = init(_driver, _dsn, _uid, _pwd, _connectString, _db, enc)))
std::cout << "*** Connected to [" << _driver << "] test database, encoding: [" << enc << "]." << std::endl;
// ...

_pExecutor = new SQLExecutor(_driver + " SQL Executor", _pSession);
_pExecutor = new SQLExecutor(_driver + " SQL Executor", _pSession, _pEncSession);

CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("ODBCSQLServerTest");

Expand Down Expand Up @@ -822,6 +838,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
CppUnit_addTest(pSuite, ODBCSQLServerTest, testTransactor);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testNullable);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testUnicode);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testEncoding);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testReconnect);

return pSuite;
Expand Down
2 changes: 2 additions & 0 deletions Data/ODBC/testsuite/src/ODBCSQLServerTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ class ODBCSQLServerTest: public ODBCTest
void recreateMiscTable();
void recreateLogTable();
void recreateUnicodeTable();
void recreateEncodingTables();

static SessionPtr _pSession;
static SessionPtr _pEncSession;
static ExecPtr _pExecutor;
static std::string _driver;
static std::string _dsn;
Expand Down
27 changes: 25 additions & 2 deletions Data/ODBC/testsuite/src/ODBCTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,25 @@ void ODBCTest::testUnicode()
}


void ODBCTest::testEncoding()
{
#if defined (POCO_ODBC_UNICODE)
if (!_pSession) fail("Test not available.");

for (int i = 0; i < 8;)
{
recreateEncodingTables();
_pSession->setFeature("autoBind", bindValue(i));
_pSession->setFeature("autoExtract", bindValue(i + 1));
_pExecutor->encoding(_rConnectString);
i += 2;
}
#else
std::cout << "Not an UNICODE build, skipping." << std::endl;
#endif
}


void ODBCTest::testReconnect()
{
if (!_pSession) fail ("Test not available.");
Expand Down Expand Up @@ -1341,15 +1360,19 @@ ODBCTest::SessionPtr ODBCTest::init(const std::string& driver,
std::string& uid,
std::string& pwd,
std::string& dbConnString,
const std::string& db)
const std::string& db,
const std::string& dbEncoding)
{
Utility::drivers(_drivers);
if (!canConnect(driver, dsn, uid, pwd, dbConnString, db)) return 0;

try
{
std::cout << "Conecting to [" << dbConnString << ']' << std::endl;
return new Session(Poco::Data::ODBC::Connector::KEY, dbConnString, 5);
SessionPtr ptr = new Session(Poco::Data::ODBC::Connector::KEY, dbConnString, 5);
if (!dbEncoding.empty())
ptr->setProperty("dbEncoding", dbEncoding);
return ptr;
}catch (ConnectionFailedException& ex)
{
std::cout << ex.displayText() << std::endl;
Expand Down

0 comments on commit 08d68ea

Please sign in to comment.