From 7f720ee6e13272e37efb8b3e4561a529d383eaab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnter=20Obiltschnig?= Date: Tue, 22 Jun 2021 08:30:05 +0200 Subject: [PATCH] #3321: manually merge ODBC text encoding support --- Data/ODBC/include/Poco/Data/ODBC/Extractor.h | 42 ++++++++++++++++- .../Poco/Data/ODBC/ODBCStatementImpl.h | 2 - .../ODBC/include/Poco/Data/ODBC/SessionImpl.h | 24 ++++++++++ Data/ODBC/src/Extractor.cpp | 47 ++++++++++++------- Data/ODBC/src/ODBCStatementImpl.cpp | 3 +- Data/ODBC/src/SessionImpl.cpp | 18 ++++++- 6 files changed, 112 insertions(+), 24 deletions(-) diff --git a/Data/ODBC/include/Poco/Data/ODBC/Extractor.h b/Data/ODBC/include/Poco/Data/ODBC/Extractor.h index 0d8ed3d9d7..1779cb6184 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/Extractor.h +++ b/Data/ODBC/include/Poco/Data/ODBC/Extractor.h @@ -32,6 +32,8 @@ #include "Poco/Dynamic/Var.h" #include "Poco/Nullable.h" #include "Poco/UTFString.h" +#include "Poco/TextEncoding.h" +#include "Poco/TextConverter.h" #include "Poco/Exception.h" #include #ifdef POCO_OS_FAMILY_WINDOWS @@ -53,7 +55,8 @@ class ODBC_API Extractor: public Poco::Data::AbstractExtractor typedef Preparator::Ptr PreparatorPtr; Extractor(const StatementHandle& rStmt, - Preparator::Ptr pPreparator); + Preparator::Ptr pPreparator, + Poco::TextEncoding::Ptr pDBEncoding = nullptr); /// Creates the Extractor. ~Extractor(); @@ -580,6 +583,40 @@ class ODBC_API Extractor: public Poco::Data::AbstractExtractor return false; } + template + bool stringContainerExtractConvert(std::size_t pos, C& val) + { + bool ret = false; + C res; + ret = extractBoundImplContainer(pos, res); + val.clear(); + if (ret) + { + Poco::TextConverter conv(*_pDBEncoding, *_pToEncoding); + val.resize(res.size()); + typename C::iterator vIt = val.begin(); + typename C::iterator it = res.begin(); + for (; it != res.end(); ++it, ++vIt) conv.convert(*it, *vIt); + } + return ret; + } + + template + bool stringContainerExtract(std::size_t pos, C& val) + { + bool ret = false; + if (Preparator::DE_BOUND == _dataExtraction) + { + if (!_transcode) + ret = extractBoundImplContainer(pos, val); + else + ret = stringContainerExtractConvert(pos, val); + } + else + throw InvalidAccessException("Direct container extraction only allowed for bound mode."); + return ret; + } + bool isNullLengthIndicator(SQLLEN val) const; /// The reason for this utility wrapper are platforms where /// SQLLEN macro (a.k.a. SQLINTEGER) yields 64-bit value, @@ -591,6 +628,9 @@ class ODBC_API Extractor: public Poco::Data::AbstractExtractor PreparatorPtr _pPreparator; Preparator::DataExtraction _dataExtraction; std::vector _lengths; + Poco::TextEncoding::Ptr _pDBEncoding; + bool _transcode; + Poco::TextEncoding::Ptr _pToEncoding; }; diff --git a/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h b/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h index c4f8b3f956..e3a6492ca5 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h +++ b/Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h @@ -136,8 +136,6 @@ class ODBC_API ODBCStatementImpl: public Poco::Data::StatementImpl /// Called whenever SQLExecute returns SQL_NEED_DATA. This is expected /// behavior for PB_AT_EXEC binding mode. - void getData(); - void addPreparator(); void fillColumns(); void checkError(SQLRETURN rc, const std::string& msg=""); diff --git a/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h b/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h index 2b81117831..0b132cbc78 100644 --- a/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h +++ b/Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h @@ -25,6 +25,7 @@ #include "Poco/Data/ODBC/Handle.h" #include "Poco/Data/ODBC/ODBCException.h" #include "Poco/Data/AbstractSessionImpl.h" +#include "Poco/TextEncoding.h" #include "Poco/SharedPtr.h" #include "Poco/Mutex.h" #ifdef POCO_OS_FAMILY_WINDOWS @@ -162,6 +163,16 @@ class ODBC_API SessionImpl: public Poco::Data::AbstractSessionImpl /// Returns the timeout (in seconds) for queries, /// or -1 if no timeout has been set. + void setDBEncoding(const std::string&, const Poco::Any& value); + /// Sets the database encoding. + /// Value must be of type std::string. + + Poco::Any getDBEncoding(const std::string&) const; + /// Returns the database encoding. + + const std::string& dbEncoding() const; + /// Returns the database encoding. + const ConnectionHandle& dbc() const; /// Returns the connection handle. @@ -193,6 +204,7 @@ class ODBC_API SessionImpl: public Poco::Data::AbstractSessionImpl mutable char _canTransact; bool _inTransaction; int _queryTimeout; + std::string _dbEncoding; Poco::FastMutex _mutex; }; @@ -291,6 +303,18 @@ inline int SessionImpl::queryTimeout() const } +inline Poco::Any SessionImpl::getDBEncoding(const std::string&) const +{ + return _dbEncoding; +} + + +inline const std::string& SessionImpl::dbEncoding() const +{ + return _dbEncoding; +} + + } } } // namespace Poco::Data::ODBC diff --git a/Data/ODBC/src/Extractor.cpp b/Data/ODBC/src/Extractor.cpp index 4d60dc7c81..90da78517c 100644 --- a/Data/ODBC/src/Extractor.cpp +++ b/Data/ODBC/src/Extractor.cpp @@ -19,7 +19,6 @@ #include "Poco/Data/ODBC/ODBCException.h" #include "Poco/Data/LOB.h" #include "Poco/Buffer.h" -#include "Poco/Exception.h" #include @@ -35,10 +34,14 @@ const std::string Extractor::FLD_SIZE_EXCEEDED_FMT = "Specified data size (%z by Extractor::Extractor(const StatementHandle& rStmt, - Preparator::Ptr pPreparator): + Preparator::Ptr pPreparator, + TextEncoding::Ptr pDBEncoding): _rStmt(rStmt), _pPreparator(pPreparator), - _dataExtraction(pPreparator->getDataExtraction()) + _dataExtraction(pPreparator->getDataExtraction()), + _pDBEncoding(pDBEncoding), + _transcode(_pDBEncoding && !_pDBEncoding->isA("UTF-8")), + _pToEncoding(_transcode ? Poco::TextEncoding::find("UTF-8") : nullptr) { } @@ -702,37 +705,45 @@ bool Extractor::extract(std::size_t pos, std::list& val) bool Extractor::extract(std::size_t pos, std::string& val) { - if (Preparator::DE_MANUAL == _dataExtraction) - return extractManualImpl(pos, val, SQL_C_CHAR); + bool ret = false; + + if (!_transcode) + { + if (Preparator::DE_MANUAL == _dataExtraction) + ret = extractManualImpl(pos, val, SQL_C_CHAR); + else + ret = extractBoundImpl(pos, val); + } else - return extractBoundImpl(pos, val); + { + std::string result; + if (Preparator::DE_MANUAL == _dataExtraction) + ret = extractManualImpl(pos, result, SQL_C_CHAR); + else + ret = extractBoundImpl(pos, result); + Poco::TextConverter converter(*_pDBEncoding, *_pToEncoding); + converter.convert(result, val); + } + + return ret; } bool Extractor::extract(std::size_t pos, std::vector& val) { - if (Preparator::DE_BOUND == _dataExtraction) - return extractBoundImplContainer(pos, val); - else - throw InvalidAccessException("Direct container extraction only allowed for bound mode."); + return stringContainerExtract(pos, val); } bool Extractor::extract(std::size_t pos, std::deque& val) { - if (Preparator::DE_BOUND == _dataExtraction) - return extractBoundImplContainer(pos, val); - else - throw InvalidAccessException("Direct container extraction only allowed for bound mode."); + return stringContainerExtract(pos, val); } bool Extractor::extract(std::size_t pos, std::list& val) { - if (Preparator::DE_BOUND == _dataExtraction) - return extractBoundImplContainer(pos, val); - else - throw InvalidAccessException("Direct container extraction only allowed for bound mode."); + return stringContainerExtract(pos, val); } diff --git a/Data/ODBC/src/ODBCStatementImpl.cpp b/Data/ODBC/src/ODBCStatementImpl.cpp index 11f5ed09ff..ce4c149b9e 100644 --- a/Data/ODBC/src/ODBCStatementImpl.cpp +++ b/Data/ODBC/src/ODBCStatementImpl.cpp @@ -145,7 +145,8 @@ void ODBCStatementImpl::addPreparator() else _preparations.push_back(new Preparator(*_preparations[0])); - _extractors.push_back(new Extractor(_stmt, _preparations.back())); + _extractors.push_back(new Extractor(_stmt, _preparations.back(), + TextEncoding::find(Poco::RefAnyCast(session().getProperty("dbEncoding"))))); } diff --git a/Data/ODBC/src/SessionImpl.cpp b/Data/ODBC/src/SessionImpl.cpp index e0cf5c08b9..50d83470d9 100644 --- a/Data/ODBC/src/SessionImpl.cpp +++ b/Data/ODBC/src/SessionImpl.cpp @@ -39,7 +39,8 @@ SessionImpl::SessionImpl(const std::string& connect, _autoExtract(autoExtract), _canTransact(ODBC_TXN_CAPABILITY_UNKNOWN), _inTransaction(false), - _queryTimeout(-1) + _queryTimeout(-1), + _dbEncoding("UTF-8") { setFeature("bulk", true); open(); @@ -58,7 +59,8 @@ SessionImpl::SessionImpl(const std::string& connect, _autoExtract(autoExtract), _canTransact(ODBC_TXN_CAPABILITY_UNKNOWN), _inTransaction(false), - _queryTimeout(-1) + _queryTimeout(-1), + _dbEncoding("UTF-8") { setFeature("bulk", true); open(); @@ -158,12 +160,24 @@ void SessionImpl::open(const std::string& connect) &SessionImpl::setQueryTimeout, &SessionImpl::getQueryTimeout); + addProperty("dbEncoding", + &SessionImpl::setDBEncoding, + &SessionImpl::getDBEncoding); + Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_QUIET_MODE, 0, 0); if (!canTransact()) autoCommit("", true); } +void SessionImpl::setDBEncoding(const std::string&, const Poco::Any& value) +{ + const std::string& enc = Poco::RefAnyCast(value); + Poco::TextEncoding::byName(enc); // throws if not found + _dbEncoding = enc; +} + + bool SessionImpl::isConnected() const { SQLULEN value = 0;