Skip to content

Commit

Permalink
#3321: manually merge ODBC text encoding support
Browse files Browse the repository at this point in the history
  • Loading branch information
obiltschnig committed Jun 22, 2021
1 parent 970182b commit 7f720ee
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 24 deletions.
42 changes: 41 additions & 1 deletion Data/ODBC/include/Poco/Data/ODBC/Extractor.h
Expand Up @@ -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
Expand All @@ -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();
Expand Down Expand Up @@ -580,6 +583,40 @@ class ODBC_API Extractor: public Poco::Data::AbstractExtractor
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,
Expand All @@ -591,6 +628,9 @@ class ODBC_API Extractor: public Poco::Data::AbstractExtractor
PreparatorPtr _pPreparator;
Preparator::DataExtraction _dataExtraction;
std::vector<SQLLEN> _lengths;
Poco::TextEncoding::Ptr _pDBEncoding;
bool _transcode;
Poco::TextEncoding::Ptr _pToEncoding;
};


Expand Down
2 changes: 0 additions & 2 deletions Data/ODBC/include/Poco/Data/ODBC/ODBCStatementImpl.h
Expand Up @@ -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="");
Expand Down
24 changes: 24 additions & 0 deletions Data/ODBC/include/Poco/Data/ODBC/SessionImpl.h
Expand Up @@ -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
Expand Down Expand Up @@ -162,6 +163,16 @@ class ODBC_API SessionImpl: public Poco::Data::AbstractSessionImpl<SessionImpl>
/// 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.

Expand Down Expand Up @@ -193,6 +204,7 @@ class ODBC_API SessionImpl: public Poco::Data::AbstractSessionImpl<SessionImpl>
mutable char _canTransact;
bool _inTransaction;
int _queryTimeout;
std::string _dbEncoding;
Poco::FastMutex _mutex;
};

Expand Down Expand Up @@ -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


Expand Down
47 changes: 29 additions & 18 deletions Data/ODBC/src/Extractor.cpp
Expand Up @@ -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>


Expand All @@ -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)
{
}

Expand Down Expand Up @@ -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);
}


Expand Down
3 changes: 2 additions & 1 deletion Data/ODBC/src/ODBCStatementImpl.cpp
Expand Up @@ -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")))));
}


Expand Down
18 changes: 16 additions & 2 deletions Data/ODBC/src/SessionImpl.cpp
Expand Up @@ -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();
Expand All @@ -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();
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 7f720ee

Please sign in to comment.