#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/Nullable.h"
#include "Poco/UTFString.h"
#include "Poco/TextEncoding.h"
#include "Poco/TextConverter.h"
#include "Poco/Exception.h"
#include <map>
#ifdef POCO_OS_FAMILY_WINDOWS
@ -53,7 +55,8 @@ public:
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 @@ private:
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;
/// The reason for this utility wrapper are platforms where
/// SQLLEN macro (a.k.a. SQLINTEGER) yields 64-bit value,
@ -591,6 +628,9 @@ private:
PreparatorPtr _pPreparator;
Preparator::DataExtraction _dataExtraction;
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
/// behavior for PB_AT_EXEC binding mode.
void getData();
void addPreparator();
void fillColumns();
void checkError(SQLRETURN rc, const std::string& msg="");

View File

@ -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 @@ public:
/// 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 @@ private:
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

View File

@ -19,7 +19,6 @@
#include "Poco/Data/ODBC/ODBCException.h"
#include "Poco/Data/LOB.h"
#include "Poco/Buffer.h"
#include "Poco/Exception.h"
#include <typeinfo>
@ -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<double>& 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<std::string>& 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<std::string>& 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<std::string>& 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);
}

View File

@ -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<std::string>(session().getProperty("dbEncoding")))));
}

View File

@ -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<std::string>(value);
Poco::TextEncoding::byName(enc); // throws if not found
_dbEncoding = enc;
}
bool SessionImpl::isConnected() const
{
SQLULEN value = 0;