#3321: manually merge ODBC text encoding support

This commit is contained in:
Günter Obiltschnig 2021-06-22 08:30:05 +02:00
parent 970182b57d
commit 7f720ee6e1
6 changed files with 112 additions and 24 deletions

View File

@ -32,6 +32,8 @@
#include "Poco/Dynamic/Var.h" #include "Poco/Dynamic/Var.h"
#include "Poco/Nullable.h" #include "Poco/Nullable.h"
#include "Poco/UTFString.h" #include "Poco/UTFString.h"
#include "Poco/TextEncoding.h"
#include "Poco/TextConverter.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
#include <map> #include <map>
#ifdef POCO_OS_FAMILY_WINDOWS #ifdef POCO_OS_FAMILY_WINDOWS
@ -53,7 +55,8 @@ public:
typedef Preparator::Ptr PreparatorPtr; typedef Preparator::Ptr PreparatorPtr;
Extractor(const StatementHandle& rStmt, Extractor(const StatementHandle& rStmt,
Preparator::Ptr pPreparator); Preparator::Ptr pPreparator,
Poco::TextEncoding::Ptr pDBEncoding = nullptr);
/// Creates the Extractor. /// Creates the Extractor.
~Extractor(); ~Extractor();
@ -580,6 +583,40 @@ private:
return false; return false;
} }
template <typename C>
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 <typename C>
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; bool isNullLengthIndicator(SQLLEN val) const;
/// The reason for this utility wrapper are platforms where /// The reason for this utility wrapper are platforms where
/// SQLLEN macro (a.k.a. SQLINTEGER) yields 64-bit value, /// SQLLEN macro (a.k.a. SQLINTEGER) yields 64-bit value,
@ -591,6 +628,9 @@ private:
PreparatorPtr _pPreparator; PreparatorPtr _pPreparator;
Preparator::DataExtraction _dataExtraction; Preparator::DataExtraction _dataExtraction;
std::vector<SQLLEN> _lengths; std::vector<SQLLEN> _lengths;
Poco::TextEncoding::Ptr _pDBEncoding;
bool _transcode;
Poco::TextEncoding::Ptr _pToEncoding;
}; };

View File

@ -136,8 +136,6 @@ private:
/// Called whenever SQLExecute returns SQL_NEED_DATA. This is expected /// Called whenever SQLExecute returns SQL_NEED_DATA. This is expected
/// behavior for PB_AT_EXEC binding mode. /// behavior for PB_AT_EXEC binding mode.
void getData();
void addPreparator(); void addPreparator();
void fillColumns(); void fillColumns();
void checkError(SQLRETURN rc, const std::string& msg=""); void checkError(SQLRETURN rc, const std::string& msg="");

View File

@ -25,6 +25,7 @@
#include "Poco/Data/ODBC/Handle.h" #include "Poco/Data/ODBC/Handle.h"
#include "Poco/Data/ODBC/ODBCException.h" #include "Poco/Data/ODBC/ODBCException.h"
#include "Poco/Data/AbstractSessionImpl.h" #include "Poco/Data/AbstractSessionImpl.h"
#include "Poco/TextEncoding.h"
#include "Poco/SharedPtr.h" #include "Poco/SharedPtr.h"
#include "Poco/Mutex.h" #include "Poco/Mutex.h"
#ifdef POCO_OS_FAMILY_WINDOWS #ifdef POCO_OS_FAMILY_WINDOWS
@ -162,6 +163,16 @@ public:
/// Returns the timeout (in seconds) for queries, /// Returns the timeout (in seconds) for queries,
/// or -1 if no timeout has been set. /// 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; const ConnectionHandle& dbc() const;
/// Returns the connection handle. /// Returns the connection handle.
@ -193,6 +204,7 @@ private:
mutable char _canTransact; mutable char _canTransact;
bool _inTransaction; bool _inTransaction;
int _queryTimeout; int _queryTimeout;
std::string _dbEncoding;
Poco::FastMutex _mutex; 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 } } } // namespace Poco::Data::ODBC

View File

@ -19,7 +19,6 @@
#include "Poco/Data/ODBC/ODBCException.h" #include "Poco/Data/ODBC/ODBCException.h"
#include "Poco/Data/LOB.h" #include "Poco/Data/LOB.h"
#include "Poco/Buffer.h" #include "Poco/Buffer.h"
#include "Poco/Exception.h"
#include <typeinfo> #include <typeinfo>
@ -35,10 +34,14 @@ const std::string Extractor::FLD_SIZE_EXCEEDED_FMT = "Specified data size (%z by
Extractor::Extractor(const StatementHandle& rStmt, Extractor::Extractor(const StatementHandle& rStmt,
Preparator::Ptr pPreparator): Preparator::Ptr pPreparator,
TextEncoding::Ptr pDBEncoding):
_rStmt(rStmt), _rStmt(rStmt),
_pPreparator(pPreparator), _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<double>& val)
bool Extractor::extract(std::size_t pos, std::string& val) bool Extractor::extract(std::size_t pos, std::string& val)
{ {
if (Preparator::DE_MANUAL == _dataExtraction) bool ret = false;
return extractManualImpl(pos, val, SQL_C_CHAR);
if (!_transcode)
{
if (Preparator::DE_MANUAL == _dataExtraction)
ret = extractManualImpl(pos, val, SQL_C_CHAR);
else
ret = extractBoundImpl(pos, val);
}
else 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<std::string>& val) bool Extractor::extract(std::size_t pos, std::vector<std::string>& val)
{ {
if (Preparator::DE_BOUND == _dataExtraction) return stringContainerExtract(pos, val);
return extractBoundImplContainer(pos, val);
else
throw InvalidAccessException("Direct container extraction only allowed for bound mode.");
} }
bool Extractor::extract(std::size_t pos, std::deque<std::string>& val) bool Extractor::extract(std::size_t pos, std::deque<std::string>& val)
{ {
if (Preparator::DE_BOUND == _dataExtraction) return stringContainerExtract(pos, val);
return extractBoundImplContainer(pos, val);
else
throw InvalidAccessException("Direct container extraction only allowed for bound mode.");
} }
bool Extractor::extract(std::size_t pos, std::list<std::string>& val) bool Extractor::extract(std::size_t pos, std::list<std::string>& val)
{ {
if (Preparator::DE_BOUND == _dataExtraction) return stringContainerExtract(pos, val);
return extractBoundImplContainer(pos, val);
else
throw InvalidAccessException("Direct container extraction only allowed for bound mode.");
} }

View File

@ -145,7 +145,8 @@ void ODBCStatementImpl::addPreparator()
else else
_preparations.push_back(new Preparator(*_preparations[0])); _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<std::string>(session().getProperty("dbEncoding")))));
} }

View File

@ -39,7 +39,8 @@ SessionImpl::SessionImpl(const std::string& connect,
_autoExtract(autoExtract), _autoExtract(autoExtract),
_canTransact(ODBC_TXN_CAPABILITY_UNKNOWN), _canTransact(ODBC_TXN_CAPABILITY_UNKNOWN),
_inTransaction(false), _inTransaction(false),
_queryTimeout(-1) _queryTimeout(-1),
_dbEncoding("UTF-8")
{ {
setFeature("bulk", true); setFeature("bulk", true);
open(); open();
@ -58,7 +59,8 @@ SessionImpl::SessionImpl(const std::string& connect,
_autoExtract(autoExtract), _autoExtract(autoExtract),
_canTransact(ODBC_TXN_CAPABILITY_UNKNOWN), _canTransact(ODBC_TXN_CAPABILITY_UNKNOWN),
_inTransaction(false), _inTransaction(false),
_queryTimeout(-1) _queryTimeout(-1),
_dbEncoding("UTF-8")
{ {
setFeature("bulk", true); setFeature("bulk", true);
open(); open();
@ -158,12 +160,24 @@ void SessionImpl::open(const std::string& connect)
&SessionImpl::setQueryTimeout, &SessionImpl::setQueryTimeout,
&SessionImpl::getQueryTimeout); &SessionImpl::getQueryTimeout);
addProperty("dbEncoding",
&SessionImpl::setDBEncoding,
&SessionImpl::getDBEncoding);
Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_QUIET_MODE, 0, 0); Poco::Data::ODBC::SQLSetConnectAttr(_db, SQL_ATTR_QUIET_MODE, 0, 0);
if (!canTransact()) autoCommit("", true); if (!canTransact()) autoCommit("", true);
} }
void SessionImpl::setDBEncoding(const std::string&, const Poco::Any& value)
{
const std::string& enc = Poco::RefAnyCast<std::string>(value);
Poco::TextEncoding::byName(enc); // throws if not found
_dbEncoding = enc;
}
bool SessionImpl::isConnected() const bool SessionImpl::isConnected() const
{ {
SQLULEN value = 0; SQLULEN value = 0;