fix(Data): Poco::Data::ODBC-dbEncoding property not used for insert/update #3396

This commit is contained in:
Alex Fabijanic 2022-05-03 13:56:24 -05:00
parent 87a1294c75
commit 08d68ea474
22 changed files with 467 additions and 72 deletions

View File

@ -71,7 +71,9 @@ public:
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();
@ -538,7 +540,7 @@ private:
}
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))
@ -547,7 +549,19 @@ private:
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.");
@ -560,7 +574,7 @@ private:
if (size == _maxFieldSize)
{
getMinValueSize(val, size);
getMinValueSize(*pVal, size);
// accomodate for terminating zero
if (size != _maxFieldSize) ++size;
}
@ -574,20 +588,25 @@ private:
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,
@ -600,7 +619,7 @@ private:
(SQLINTEGER) size,
&(*_vecLengthIndicator[pos])[0])))
{
throw StatementException(_rStmt, "SQLBindParameter(std::vector<std::string>)");
throw StatementException(_rStmt, Poco::format("SQLBindParameter(%s)", typeID));
}
}

View File

@ -55,7 +55,8 @@ public:
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();

View File

@ -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),
@ -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());
@ -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.");
@ -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())

View File

@ -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 {
@ -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 (...)
{

View File

@ -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 {
@ -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 (...)
{

View File

@ -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())
@ -719,6 +720,7 @@ bool Extractor::extract(std::size_t pos, std::string& val)
else
ret = extractBoundImpl(pos, result);
transcode(result, val);
}
return ret;

View File

@ -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();
@ -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")));
}

View File

@ -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;
@ -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");
@ -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;

View File

@ -74,8 +74,10 @@ private:
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;

View File

@ -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.");
@ -1341,7 +1360,8 @@ 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;
@ -1349,7 +1369,10 @@ ODBCTest::SessionPtr ODBCTest::init(const std::string& driver,
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;

View File

@ -151,6 +151,7 @@ public:
virtual void testNullable();
virtual void testUnicode();
virtual void testEncoding();
virtual void testReconnect();
@ -177,13 +178,15 @@ protected:
virtual void recreateMiscTable();
virtual void recreateLogTable();
virtual void recreateUnicodeTable();
virtual void recreateEncodingTables();
static SessionPtr init(const std::string& driver,
std::string& dsn,
std::string& uid,
std::string& pwd,
std::string& dbConnString,
const std::string& db = "");
const std::string& db = "",
const std::string& dbEncoding = "");
static bool canConnect(const std::string& driver,
std::string& dsn,
@ -374,6 +377,12 @@ inline void ODBCTest::recreateUnicodeTable()
}
inline void ODBCTest::recreateEncodingTables()
{
throw Poco::NotImplementedException("ODBCTest::recreateUnicodeTables()");
}
inline bool ODBCTest::bindValue(int i)
{
poco_assert (i < 8);

View File

@ -296,9 +296,10 @@ const std::string SQLExecutor::MULTI_SELECT =
"SELECT * FROM Test WHERE First = '5';";
SQLExecutor::SQLExecutor(const std::string& name, Poco::Data::Session* pSession):
SQLExecutor::SQLExecutor(const std::string& name, Poco::Data::Session* pSession, Poco::Data::Session* pEncSession):
CppUnit::TestCase(name),
_pSession(pSession)
_pSession(pSession),
_pEncSession(pEncSession)
{
}
@ -3530,6 +3531,7 @@ void SQLExecutor::sqlLogger(const std::string& connect)
rs.moveNext();
assertTrue ("TestSQLChannel" == rs["Source"]);
assertTrue ("b Warning message" == rs["Text"]);
root.setChannel(nullptr);
}
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail ("sqlLogger()"); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail ("sqlLogger()"); }
@ -4033,4 +4035,55 @@ void SQLExecutor::unicode(const std::string& dbConnString)
session() << "SELECT str FROM UnicodeTable", into(wtext), now;
Poco::UnicodeConverter::convert(wtext, text);
assertTrue (text == std::string((const char*)supp));
}
}
void SQLExecutor::encoding(const std::string& dbConnString)
{
try
{
const unsigned char latinChars[] = { 'g', 252, 'n', 't', 'e', 'r', 0 };
const unsigned char utf8Chars[] = { 'g', 195, 188, 'n', 't', 'e', 'r', 0 };
std::string latinText((const char*)latinChars);
std::string utf8TextIn((const char*)utf8Chars);
session(true) << "INSERT INTO Latin1Table VALUES (?)", use(utf8TextIn), now;
std::string latinTextOut;
session() << "SELECT str FROM Latin1Table", into(latinTextOut), now;
assertTrue(latinText == latinTextOut);
std::string utf8TextOut;
session(true) << "SELECT str FROM Latin1Table", into(utf8TextOut), now;
assertTrue(utf8TextIn == utf8TextOut);
const unsigned char latinChars2[] = { 'G', 220, 'N', 'T', 'E', 'R', 0 };
const unsigned char utf8Chars2[] = { 'G', 195, 156, 'N', 'T', 'E', 'R', 0 };
std::string latinText2 = (const char*)latinChars2;
std::string utf8TextIn2 = (const char*)utf8Chars2;
session(true) << "INSERT INTO Latin1Table VALUES (?)", use(utf8TextIn2), now;
std::vector<std::string> textOutVec;
session() << "SELECT str FROM Latin1Table", into(textOutVec), now;
assertTrue(textOutVec.size() == 2);
assertTrue(textOutVec[0] == latinText);
assertTrue(textOutVec[1] == latinText2);
textOutVec.clear();
session(true) << "SELECT str FROM Latin1Table", into(textOutVec), now;
assertTrue(textOutVec.size() == 2);
assertTrue(textOutVec[0] == utf8TextIn);
assertTrue(textOutVec[1] == utf8TextIn2);
}
catch (Poco::Exception& ex)
{
std::cout << ex.displayText() << std::endl;
throw;
}
catch (std::exception& ex)
{
std::cout << ex.what() << std::endl;
throw;
}
}

View File

@ -87,7 +87,7 @@ public:
DE_BOUND
};
SQLExecutor(const std::string& name, Poco::Data::Session* _pSession);
SQLExecutor(const std::string& name, Poco::Data::Session* pSession, Poco::Data::Session* pEncSession = 0);
~SQLExecutor();
void execute(const std::string& sql);
@ -505,6 +505,7 @@ public:
void nullable();
void unicode(const std::string& dbConnString);
void encoding(const std::string& dbConnString);
void reconnect();
@ -514,15 +515,24 @@ private:
void setTransactionIsolation(Poco::Data::Session& session, Poco::UInt32 ti);
Poco::Data::Session& session();
Poco::Data::Session& session(bool enc = false);
Poco::Data::Session* _pSession;
Poco::Data::Session* _pEncSession;
};
inline Poco::Data::Session& SQLExecutor::session()
inline Poco::Data::Session& SQLExecutor::session(bool enc)
{
poco_check_ptr (_pSession);
return *_pSession;
if (!enc)
{
poco_check_ptr(_pSession);
return *_pSession;
}
else
{
poco_check_ptr(_pEncSession);
return *_pEncSession;
}
}

View File

@ -28,6 +28,7 @@
#include "Poco/Any.h"
#include "Poco/Dynamic/Var.h"
#include "Poco/UTFString.h"
#include "Poco/TextEncoding.h"
#include <vector>
#include <deque>
#include <list>
@ -39,7 +40,7 @@ namespace Data {
using NullData = NullType;
class Transcoder;
namespace Keywords {
@ -64,7 +65,8 @@ public:
PD_IN_OUT
};
AbstractBinder();
AbstractBinder(Poco::TextEncoding::Ptr pFromEncoding = nullptr,
Poco::TextEncoding::Ptr pDBEncoding = nullptr);
/// Creates the AbstractBinder.
virtual ~AbstractBinder();
@ -356,6 +358,14 @@ public:
static bool isInBound(Direction dir);
/// Returns true if direction is in bound;
protected:
bool transcodeRequired() const;
void transcode(const std::string& from, std::string& to);
void reverseTranscode(const std::string& from, std::string& to);
private:
std::unique_ptr<Transcoder> _pTranscoder;
};
@ -380,6 +390,12 @@ inline bool AbstractBinder::isInBound(Direction dir)
}
inline bool AbstractBinder::transcodeRequired() const
{
return _pTranscoder.operator bool();
}
} } // namespace Poco::Data

View File

@ -23,7 +23,6 @@
#include "Poco/Data/LOB.h"
#include "Poco/UUID.h"
#include "Poco/UTFString.h"
#include "Poco/TextConverter.h"
#include "Poco/TextEncoding.h"
#include <memory>
#include <vector>
@ -35,7 +34,6 @@
namespace Poco {
class DateTime;
class Any;
@ -48,6 +46,7 @@ namespace Data {
class Date;
class Time;
class Transcoder;
class Data_API AbstractExtractor
@ -353,18 +352,18 @@ public:
protected:
bool transcodeRequired() const;
void transcode(const std::string& val1, std::string& val2);
void transcode(const std::string& from, std::string& to);
void reverseTranscode(const std::string& from, std::string& to);
private:
Poco::TextEncoding::Ptr _pDBEncoding;
Poco::TextEncoding::Ptr _pToEncoding;
std::unique_ptr<Poco::TextConverter> _pConverter;
std::unique_ptr<Transcoder> _pTranscoder;
};
///
/// inlines
///
inline void AbstractExtractor::reset()
{
//default no-op
@ -373,17 +372,7 @@ inline void AbstractExtractor::reset()
inline bool AbstractExtractor::transcodeRequired() const
{
return _pConverter.operator bool();
}
inline void AbstractExtractor::transcode(const std::string& val1, std::string& val2)
{
if (_pConverter)
{
val2.clear();
_pConverter->convert(val1, val2);
}
return _pTranscoder.operator bool();
}

View File

@ -47,7 +47,7 @@ class Binding: public AbstractBinding
/// is passed to binding, the storage it refers to must be valid at the statement execution time.
/// To pass a copy of a variable, constant or string literal, use utility function bind().
/// Variables can be passed as either copies or references (i.e. using either use() or bind()).
/// Constants, however, can only be passed as copies. this is best achieved using bind() utility
/// Constants, however, can only be passed as copies. This is best achieved using bind() utility
/// function. An attempt to pass a constant by reference shall result in compile-time error.
{
public:

View File

@ -0,0 +1,97 @@
//
// Transcoder.h
//
// Library: Data
// Package: DataCore
// Module: Transcoder
//
// Definition of the Transcoder class.
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef Data_Transcoder_INCLUDED
#define Data_Transcoder_INCLUDED
#include "Poco/Data/Data.h"
#include "Poco/TextConverter.h"
#include "Poco/TextEncoding.h"
#include <memory>
#include <string>
namespace Poco {
namespace Data {
class Data_API Transcoder
/// Utility class used to convert string data encoding.
///
/// The purpose of this class is to help with deciding
/// whether conversion is actually required, and to keep
/// the encodings lifetimes aligned with the converter lifetime.
{
public:
using Ptr = std::unique_ptr<Transcoder>;
using ConverterPtr = std::unique_ptr<Poco::TextConverter>;
virtual ~Transcoder();
/// Destroys the Transcoder.
static Ptr create(Poco::TextEncoding::Ptr pFromEncoding = nullptr,
Poco::TextEncoding::Ptr pToEncoding = nullptr);
/// Returns a unique pointer to Transcode instance;
/// if there is no need for transcoding, null pointer
/// is returned.
std::string fromEncoding() const;
/// Returns "from" encoding canonical name.
std::string toEncoding() const;
/// Returns "from" encoding canonical name.
void transcode(const std::string& from, std::string& to);
/// Performs the conversion. Any prior content of the
/// destination string is cleared.
void reverseTranscode(const std::string& from, std::string& to);
/// Performs the reverse conversion. Any prior content of the
/// destination string is cleared.
private:
Transcoder(Poco::TextEncoding::Ptr pFromEncoding,
Poco::TextEncoding::Ptr pToEncoding);
/// Creates the Transcoder.
Poco::TextEncoding::Ptr _pFromEncoding;
Poco::TextEncoding::Ptr _pToEncoding;
ConverterPtr _pConverter;
ConverterPtr _pReverseConverter;
};
//
// inlines
//
inline std::string Transcoder::fromEncoding() const
{
return _pFromEncoding->canonicalName();
}
inline std::string Transcoder::toEncoding() const
{
return _pToEncoding->canonicalName();
}
} } // namespace Poco::Data
#endif // Data_Transcoder_INCLUDED

View File

@ -16,6 +16,7 @@
#include "Poco/Data/Date.h"
#include "Poco/Data/Time.h"
#include "Poco/Data/LOB.h"
#include "Poco/Data/Transcoder.h"
#include "Poco/Data/DataException.h"
#include "Poco/DateTime.h"
#include "Poco/Any.h"
@ -26,7 +27,9 @@ namespace Poco {
namespace Data {
AbstractBinder::AbstractBinder()
AbstractBinder::AbstractBinder(Poco::TextEncoding::Ptr pFromEncoding,
Poco::TextEncoding::Ptr pDBEncoding) :
_pTranscoder(Transcoder::create(pFromEncoding, pDBEncoding))
{
}
@ -36,6 +39,20 @@ AbstractBinder::~AbstractBinder()
}
void AbstractBinder::transcode(const std::string& from, std::string& to)
{
if (_pTranscoder)
_pTranscoder->transcode(from, to);
}
void AbstractBinder::reverseTranscode(const std::string& from, std::string& to)
{
if (_pTranscoder)
_pTranscoder->reverseTranscode(from, to);
}
void AbstractBinder::bind(std::size_t pos, const std::vector<Poco::Int8>& val, Direction dir)
{
throw NotImplementedException("std::vector binder must be implemented.");

View File

@ -13,6 +13,7 @@
#include "Poco/Data/AbstractExtractor.h"
#include "Poco/Data/Transcoder.h"
#include "Poco/Exception.h"
@ -22,16 +23,8 @@ namespace Data {
AbstractExtractor::AbstractExtractor(Poco::TextEncoding::Ptr pDBEncoding,
Poco::TextEncoding::Ptr pToEncoding):
_pDBEncoding(pDBEncoding),
_pToEncoding(pToEncoding ?
pToEncoding : _pDBEncoding ?
Poco::TextEncoding::find("UTF-8") : nullptr),
_pConverter(_pDBEncoding ?
new Poco::TextConverter(*pDBEncoding, *_pToEncoding) :
nullptr)
_pTranscoder(Transcoder::create(pDBEncoding, pToEncoding))
{
poco_assert_dbg((!_pDBEncoding && !_pToEncoding) ||
(_pDBEncoding && _pToEncoding));
}
@ -40,6 +33,20 @@ AbstractExtractor::~AbstractExtractor()
}
void AbstractExtractor::transcode(const std::string& from, std::string& to)
{
if (_pTranscoder)
_pTranscoder->transcode(from, to);
}
void AbstractExtractor::reverseTranscode(const std::string& from, std::string& to)
{
if (_pTranscoder)
_pTranscoder->reverseTranscode(from, to);
}
bool AbstractExtractor::extract(std::size_t pos, std::vector<Poco::Int8>& val)
{
throw NotImplementedException("std::vector extractor must be implemented.");

76
Data/src/Transcoder.cpp Normal file
View File

@ -0,0 +1,76 @@
//
// Transcoder.cpp
//
// Library: Data
// Package: DataCore
// Module: Transcoder
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "Poco/Data/Transcoder.h"
namespace Poco {
namespace Data {
Transcoder::Transcoder(Poco::TextEncoding::Ptr pFromEncoding,
Poco::TextEncoding::Ptr pToEncoding):
_pFromEncoding(pFromEncoding),
_pToEncoding(pToEncoding),
_pConverter(new Poco::TextConverter(*_pFromEncoding, *_pToEncoding)),
_pReverseConverter(new Poco::TextConverter(*_pToEncoding, *_pFromEncoding))
{
}
Transcoder::~Transcoder()
{
}
Transcoder::Ptr Transcoder::create(Poco::TextEncoding::Ptr pFromEncoding,
Poco::TextEncoding::Ptr pToEncoding)
{
Ptr pTranscoder;
if (!pFromEncoding && !pToEncoding)
return pTranscoder;
if (!pFromEncoding) pFromEncoding = Poco::TextEncoding::find("UTF-8");
if (!pToEncoding) pToEncoding = Poco::TextEncoding::find("UTF-8");
if (pToEncoding->isA(pFromEncoding->canonicalName()))
return pTranscoder;
pTranscoder.reset(new Transcoder(pFromEncoding, pToEncoding));
return pTranscoder;
}
void Transcoder::transcode(const std::string& from, std::string& to)
{
if (_pConverter)
{
to.clear();
_pConverter->convert(from, to);
}
}
void Transcoder::reverseTranscode(const std::string& from, std::string& to)
{
if (_pConverter)
{
to.clear();
_pReverseConverter->convert(from, to);
}
}
} } // namespace Poco::Data

View File

@ -1377,6 +1377,9 @@ void DataTest::testTranscode()
assertTrue (ext.extract(0, utf8Out));
assertTrue(utf8Out == latin1Text);
Latin1Encoding::Ptr pe = new Latin1Encoding();
auto pUTF8E = Poco::TextEncoding::find("UTF-8");
Poco::Data::Test::Extractor ext2(new Latin1Encoding());
ext2.setString(latin1Text);
utf8Out.clear();

View File

@ -7,25 +7,25 @@ progen.libsuffix.release_static_md = md.lib
progen.libsuffix.release_static_mt = mt.lib
progen.project.guidFromName.namespaceUUID = F4193868-E4EB-4090-9A01-344E7233004B
progen.postprocess.upgrade2008to2015.tool = ${system.env.VS140COMNTOOLS}\\..\\IDE\\DevEnv.exe
progen.postprocess.upgrade2008to2015.tool = ${system.env.VS160COMNTOOLS}\\..\\IDE\\DevEnv.exe
progen.postprocess.upgrade2008to2015.args = %;/Upgrade
progen.postprocess.upgrade2008to2015.deleteOriginalFile = true
progen.postprocess.upgrade2008to2015.deleteFiles = Backup;_UpgradeReport_Files;UpgradeLog.XML;UpgradeLog.htm
progen.postprocess.upgrade2008to2015.fix2015ProjectFile = true
progen.postprocess.upgrade2008to2017.tool = ${system.env.VS150COMNTOOLS}\\..\\IDE\\DevEnv.exe
progen.postprocess.upgrade2008to2017.tool = ${system.env.VS160COMNTOOLS}\\..\\IDE\\DevEnv.exe
progen.postprocess.upgrade2008to2017.args = %;/Upgrade
progen.postprocess.upgrade2008to2017.deleteOriginalFile = true
progen.postprocess.upgrade2008to2017.deleteFiles = Backup;_UpgradeReport_Files;UpgradeLog.XML;UpgradeLog.htm
progen.postprocess.upgrade2008to2017.fix2017ProjectFile = true
progen.postprocess.upgrade2008to2019.tool = ${system.env.VS150COMNTOOLS}\\..\\IDE\\DevEnv.exe
progen.postprocess.upgrade2008to2019.tool = ${system.env.VS160COMNTOOLS}\\..\\IDE\\DevEnv.exe
progen.postprocess.upgrade2008to2019.args = %;/Upgrade
progen.postprocess.upgrade2008to2019.deleteOriginalFile = true
progen.postprocess.upgrade2008to2019.deleteFiles = Backup;_UpgradeReport_Files;UpgradeLog.XML;UpgradeLog.htm
progen.postprocess.upgrade2008to2019.fix2019ProjectFile = true
progen.postprocess.upgrade2008to2022.tool = ${system.env.VS150COMNTOOLS}\\..\\IDE\\DevEnv.exe
progen.postprocess.upgrade2008to2022.tool = ${system.env.VS160COMNTOOLS}\\..\\IDE\\DevEnv.exe
progen.postprocess.upgrade2008to2022.args = %;/Upgrade
progen.postprocess.upgrade2008to2022.deleteOriginalFile = true
progen.postprocess.upgrade2008to2022.deleteFiles = Backup;_UpgradeReport_Files;UpgradeLog.XML;UpgradeLog.htm