GH #290: Unicode support

This commit is contained in:
Alex Fabijanic 2014-05-21 03:28:24 -05:00
parent 8b39a87fd6
commit 1aa28e1491
46 changed files with 1168 additions and 114 deletions

View File

@ -67,6 +67,7 @@ Release 1.5.3 (2014-05-xx)
- fixed GH #442: Use correct prefix length field of Windows IP_ADAPTER_PREFIX structure
- improved GH #328: NetworkInterface on Windows XP
- fixed GH #154 Add support for MYSQL_TYPE_NEWDECIMAL to Poco::Data::MySQL
- fixed GH #290: Unicode support
Release 1.5.2 (2013-09-16)
==========================

View File

@ -252,6 +252,18 @@ public:
void bind(std::size_t pos, const std::list<std::string>& val, Direction dir);
/// Binds a string list.
void bind(std::size_t pos, const UTF16String& val, Direction dir);
/// Binds a string.
void bind(std::size_t pos, const std::vector<UTF16String>& val, Direction dir);
/// Binds a string vector.
void bind(std::size_t pos, const std::deque<UTF16String>& val, Direction dir);
/// Binds a string deque.
void bind(std::size_t pos, const std::list<UTF16String>& val, Direction dir);
/// Binds a string list.
void bind(std::size_t pos, const BLOB& val, Direction dir);
/// Binds a BLOB. In-bound only.
@ -341,18 +353,20 @@ public:
/// Clears the cached storage.
private:
typedef std::vector<SQLLEN*> LengthVec;
typedef std::vector<std::vector<SQLLEN> > LengthVecVec;
typedef std::vector<char*> CharPtrVec;
typedef std::vector<bool*> BoolPtrVec;
typedef std::vector<std::vector<SQL_DATE_STRUCT> > DateVec;
typedef std::vector<std::vector<SQL_TIME_STRUCT> > TimeVec;
typedef std::vector<std::vector<SQL_TIMESTAMP_STRUCT> > DateTimeVec;
typedef std::vector<std::vector<Poco::Any> > AnyVec;
typedef std::map<char*, std::string*> StringMap;
typedef std::map<SQL_DATE_STRUCT*, Date*> DateMap;
typedef std::map<SQL_TIME_STRUCT*, Time*> TimeMap;
typedef std::map<SQL_TIMESTAMP_STRUCT*, DateTime*> TimestampMap;
typedef std::vector<SQLLEN*> LengthVec;
typedef std::vector<std::vector<SQLLEN> > LengthVecVec;
typedef std::vector<char*> CharPtrVec;
typedef std::vector<UTF16Char*> UTF16CharPtrVec;
typedef std::vector<bool*> BoolPtrVec;
typedef std::vector<std::vector<SQL_DATE_STRUCT> > DateVec;
typedef std::vector<std::vector<SQL_TIME_STRUCT> > TimeVec;
typedef std::vector<std::vector<SQL_TIMESTAMP_STRUCT> > DateTimeVec;
typedef std::vector<std::vector<Poco::Any> > AnyVec;
typedef std::map<char*, std::string*> StringMap;
typedef std::map<UTF16String::value_type*, UTF16String*> UTF16StringMap;
typedef std::map<SQL_DATE_STRUCT*, Date*> DateMap;
typedef std::map<SQL_TIME_STRUCT*, Time*> TimeMap;
typedef std::map<SQL_TIMESTAMP_STRUCT*, DateTime*> TimestampMap;
void describeParameter(std::size_t pos);
/// Sets the description field for the parameter, if needed.
@ -581,6 +595,71 @@ private:
}
}
template <typename C>
void bindImplContainerUTF16String(std::size_t pos, const C& val, Direction dir)
/// Utility function to bind containers of strings.
{
if (isOutBound(dir) || !isInBound(dir))
throw NotImplementedException("String container parameter type can only be inbound.");
if (PB_IMMEDIATE != _paramBinding)
throw InvalidAccessException("Containers can only be bound immediately.");
if (0 == val.size())
throw InvalidArgumentException("Empty container not allowed.");
setParamSetSize(val.size());
SQLINTEGER size = 0;
getColumnOrParameterSize(pos, size);
poco_assert(size > 0);
if (size == _maxFieldSize)
{
getMinValueSize(val, size);
// accomodate for terminating zero
if (size != _maxFieldSize) size += sizeof(UTF16Char);
}
if (_vecLengthIndicator.size() <= pos)
{
_vecLengthIndicator.resize(pos + 1);
_vecLengthIndicator[pos].resize(val.size(), SQL_NTS);
}
if (_utf16CharPtrs.size() <= pos)
_utf16CharPtrs.resize(pos + 1, 0);
_utf16CharPtrs[pos] = (UTF16Char*)std::calloc(val.size() * size, sizeof(UTF16Char));
std::size_t strSize;
std::size_t offset = 0;
typename C::const_iterator it = val.begin();
typename C::const_iterator end = val.end();
for (; it != end; ++it)
{
strSize = it->size() * sizeof(UTF16Char);
if (strSize > size)
throw LengthExceededException("SQLBindParameter(std::vector<UTF16String>)");
std::memcpy(_utf16CharPtrs[pos] + offset, it->data(), strSize);
offset += (size / sizeof(UTF16Char));
}
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT)pos + 1,
toODBCDirection(dir),
SQL_C_WCHAR,
SQL_WLONGVARCHAR,
(SQLUINTEGER)size - 1,
0,
_utf16CharPtrs[pos],
(SQLINTEGER)size,
&_vecLengthIndicator[pos][0])))
{
throw StatementException(_rStmt, "SQLBindParameter(std::vector<UTF16String>)");
}
}
template <typename C>
void bindImplContainerLOB(std::size_t pos, const C& val, Direction dir)
{
@ -859,7 +938,7 @@ private:
typename T::const_iterator end = val.end();
for (; it != end; ++it)
{
std::size_t sz = it->size();
std::size_t sz = it->size() * sizeof(T);
if (sz > _maxFieldSize)
throw LengthExceededException();
@ -888,11 +967,13 @@ private:
TimeMap _times;
TimestampMap _timestamps;
StringMap _strings;
UTF16StringMap _utf16Strings;
DateVec _dateVec;
TimeVec _timeVec;
DateTimeVec _dateTimeVec;
CharPtrVec _charPtrs;
UTF16CharPtrVec _utf16CharPtrs;
BoolPtrVec _boolPtrs;
const TypeInfo* _pTypeInfo;
SQLINTEGER _paramSetSize;
@ -1241,6 +1322,24 @@ inline void Binder::bind(std::size_t pos, const std::list<std::string>& val, Dir
bindImplContainerString(pos, val, dir);
}
inline void Binder::bind(std::size_t pos, const std::vector<UTF16String>& val, Direction dir)
{
bindImplContainerUTF16String(pos, val, dir);
}
inline void Binder::bind(std::size_t pos, const std::deque<UTF16String>& val, Direction dir)
{
bindImplContainerUTF16String(pos, val, dir);
}
inline void Binder::bind(std::size_t pos, const std::list<UTF16String>& val, Direction dir)
{
bindImplContainerUTF16String(pos, val, dir);
}
inline void Binder::bind(std::size_t pos, const BLOB& val, Direction dir)
{
bindImplLOB<BLOB>(pos, val, dir);

View File

@ -33,6 +33,7 @@
#include "Poco/Any.h"
#include "Poco/Dynamic/Var.h"
#include "Poco/Nullable.h"
#include "Poco/UTFString.h"
#include "Poco/Exception.h"
#include <map>
#ifdef POCO_OS_FAMILY_WINDOWS
@ -232,6 +233,19 @@ public:
bool extract(std::size_t pos, std::list<std::string>& val);
/// Extracts a string list.
/// Extracts a single character list.
bool extract(std::size_t pos, UTF16String& val);
/// Extracts a string.
bool extract(std::size_t pos, std::vector<UTF16String>& val);
/// Extracts a string vector.
bool extract(std::size_t pos, std::deque<UTF16String>& val);
/// Extracts a string deque.
bool extract(std::size_t pos, std::list<UTF16String>& val);
/// Extracts a string list.
bool extract(std::size_t pos, Poco::Data::BLOB& val);
/// Extracts a BLOB.
@ -372,7 +386,10 @@ private:
bool extractBoundImplContainer(std::size_t pos, std::vector<std::string>& values);
bool extractBoundImplContainer(std::size_t pos, std::deque<std::string>& values);
bool extractBoundImplContainer(std::size_t pos, std::list<std::string>& values);
bool extractBoundImplContainer(std::size_t pos, std::list<std::string>& values);
bool extractBoundImplContainer(std::size_t pos, std::vector<Poco::UTF16String>& values);
bool extractBoundImplContainer(std::size_t pos, std::deque<Poco::UTF16String>& values);
bool extractBoundImplContainer(std::size_t pos, std::list<Poco::UTF16String>& values);
bool extractBoundImplContainer(std::size_t pos, std::vector<Poco::Data::CLOB>& values);
bool extractBoundImplContainer(std::size_t pos, std::deque<Poco::Data::CLOB>& values);
bool extractBoundImplContainer(std::size_t pos, std::list<Poco::Data::CLOB>& values);
@ -394,7 +411,19 @@ private:
ItType it = values.begin();
ItType end = values.end();
for (int row = 0; it != end; ++it, ++row)
it->assign(*pc + row * colWidth, _pPreparator->actualDataSize(pos, row));
{
it->assign(*pc + row * colWidth / sizeof(CharType), _pPreparator->actualDataSize(pos, row));
// clean up superfluous null chars returned by some drivers
typename StringType::size_type trimLen = 0;
typename StringType::reverse_iterator sIt = it->rbegin();
typename StringType::reverse_iterator sEnd = it->rend();
for (; sIt != sEnd; ++sIt)
{
if (*sIt == '\0') ++trimLen;
else break;
}
if (trimLen) it->assign(it->begin(), it->begin() + it->length() - trimLen);
}
return true;
}
@ -522,6 +551,9 @@ private:
case MetaColumn::FDT_STRING:
{ return extAny<T, std::string>(pos, val); }
case MetaColumn::FDT_WSTRING:
{ return extAny<T, Poco::UTF16String>(pos, val); }
case MetaColumn::FDT_BLOB:
{ return extAny<T, Poco::Data::BLOB>(pos, val); }
@ -592,6 +624,24 @@ inline bool Extractor::extractBoundImplContainer(std::size_t pos, std::list<std:
}
inline bool Extractor::extractBoundImplContainer(std::size_t pos, std::vector<Poco::UTF16String>& values)
{
return extractBoundImplContainerString(pos, values);
}
inline bool Extractor::extractBoundImplContainer(std::size_t pos, std::deque<Poco::UTF16String>& values)
{
return extractBoundImplContainerString(pos, values);
}
inline bool Extractor::extractBoundImplContainer(std::size_t pos, std::list<Poco::UTF16String>& values)
{
return extractBoundImplContainerString(pos, values);
}
inline bool Extractor::extractBoundImplContainer(std::size_t pos,
std::vector<Poco::Data::CLOB>& values)
{

View File

@ -31,6 +31,7 @@
#include "Poco/DynamicAny.h"
#include "Poco/DateTime.h"
#include "Poco/SharedPtr.h"
#include "Poco/UTFString.h"
#include <vector>
#ifdef POCO_OS_FAMILY_WINDOWS
#include <windows.h>
@ -87,8 +88,10 @@ public:
DT_BOOL,
DT_BOOL_ARRAY,
DT_CHAR,
DT_WCHAR,
DT_UCHAR,
DT_CHAR_ARRAY,
DT_WCHAR_ARRAY,
DT_UCHAR_ARRAY,
DT_DATE,
DT_TIME,
@ -280,6 +283,18 @@ public:
void prepare(std::size_t pos, const std::list<std::string>& val);
/// Prepares a string list.
void prepare(std::size_t pos, const UTF16String& val);
/// Prepares a string.
void prepare(std::size_t pos, const std::vector<UTF16String>& val);
/// Prepares a string vector.
void prepare(std::size_t pos, const std::deque<UTF16String>& val);
/// Prepares a string deque.
void prepare(std::size_t pos, const std::list<UTF16String>& val);
/// Prepares a string list.
void prepare(std::size_t pos, const Poco::Data::BLOB& val);
/// Prepares a BLOB.
@ -490,6 +505,15 @@ private:
else
return prepareVariableLen<char>(pos, SQL_C_CHAR, maxDataSize(pos), DT_CHAR);
case MetaColumn::FDT_WSTRING:
{
typedef UTF16String::value_type CharType;
if (pVal)
return prepareCharArray<CharType, DT_WCHAR_ARRAY>(pos, SQL_C_WCHAR, maxDataSize(pos), pVal->size());
else
return prepareVariableLen<CharType>(pos, SQL_C_WCHAR, maxDataSize(pos), DT_WCHAR);
}
case MetaColumn::FDT_BLOB:
{
typedef Poco::Data::BLOB::ValueType CharType;
@ -1007,6 +1031,30 @@ inline void Preparator::prepare(std::size_t pos, const std::list<std::string>& v
}
inline void Preparator::prepare(std::size_t pos, const UTF16String&)
{
prepareVariableLen<UTF16String::value_type>(pos, SQL_C_WCHAR, maxDataSize(pos), DT_CHAR);
}
inline void Preparator::prepare(std::size_t pos, const std::vector<UTF16String>& val)
{
prepareCharArray<UTF16String::value_type, DT_WCHAR_ARRAY>(pos, SQL_C_WCHAR, maxDataSize(pos), val.size());
}
inline void Preparator::prepare(std::size_t pos, const std::deque<UTF16String>& val)
{
prepareCharArray<UTF16String::value_type, DT_WCHAR_ARRAY>(pos, SQL_C_WCHAR, maxDataSize(pos), val.size());
}
inline void Preparator::prepare(std::size_t pos, const std::list<UTF16String>& val)
{
prepareCharArray<UTF16String::value_type, DT_WCHAR_ARRAY>(pos, SQL_C_WCHAR, maxDataSize(pos), val.size());
}
inline void Preparator::prepare(std::size_t pos, const Poco::Data::BLOB&)
{
prepareVariableLen<Poco::Data::BLOB::ValueType>(pos, SQL_C_BINARY, maxDataSize(pos), DT_UCHAR);

View File

@ -73,6 +73,10 @@ void Binder::freeMemory()
CharPtrVec::iterator endChr = _charPtrs.end();
for (; itChr != endChr; ++itChr) std::free(*itChr);
UTF16CharPtrVec::iterator itUTF16Chr = _utf16CharPtrs.begin();
UTF16CharPtrVec::iterator endUTF16Chr = _utf16CharPtrs.end();
for (; itUTF16Chr != endUTF16Chr; ++itUTF16Chr) std::free(*itUTF16Chr);
BoolPtrVec::iterator itBool = _boolPtrs.begin();
BoolPtrVec::iterator endBool = _boolPtrs.end();
for (; itBool != endBool; ++itBool) delete [] *itBool;
@ -127,6 +131,58 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir)
}
void Binder::bind(std::size_t pos, const UTF16String& val, Direction dir)
{
typedef UTF16String::value_type CharT;
SQLPOINTER pVal = 0;
SQLINTEGER size = (SQLINTEGER)(val.size() * sizeof(CharT));
if (isOutBound(dir))
{
getColumnOrParameterSize(pos, size);
CharT* pChar = (CharT*)std::calloc(size, 1);
pVal = (SQLPOINTER)pChar;
_outParams.insert(ParamMap::value_type(pVal, size));
_utf16Strings.insert(UTF16StringMap::value_type(pChar, const_cast<UTF16String*>(&val)));
}
else if (isInBound(dir))
{
pVal = (SQLPOINTER)val.c_str();
_inParams.insert(ParamMap::value_type(pVal, size));
}
else
throw InvalidArgumentException("Parameter must be [in] OR [out] bound.");
SQLLEN* pLenIn = new SQLLEN;
SQLINTEGER colSize = 0;
SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, SQL_C_WCHAR, colSize, decDigits);
*pLenIn = SQL_NTS;
if (PB_AT_EXEC == _paramBinding)
{
*pLenIn = SQL_LEN_DATA_AT_EXEC(size);
}
_lengthIndicator.push_back(pLenIn);
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT)pos + 1,
toODBCDirection(dir),
SQL_C_WCHAR,
SQL_WLONGVARCHAR,
(SQLUINTEGER)colSize,
0,
pVal,
(SQLINTEGER)size,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(std::string)");
}
}
void Binder::bind(std::size_t pos, const Date& val, Direction dir)
{
SQLINTEGER size = (SQLINTEGER) sizeof(SQL_DATE_STRUCT);

View File

@ -64,6 +64,22 @@ bool Extractor::extractBoundImpl<std::string>(std::size_t pos, std::string& val)
}
template<>
bool Extractor::extractBoundImpl<UTF16String>(std::size_t pos, UTF16String& val)
{
typedef UTF16String::value_type CharT;
if (isNull(pos)) return false;
std::size_t dataSize = _pPreparator->actualDataSize(pos);
CharT* sp = AnyCast<CharT*>(_pPreparator->at(pos));
std::size_t len = Poco::UnicodeConverter::UTFStrlen(sp);
if (len < dataSize) dataSize = len;
checkDataSize(dataSize);
val.assign(sp, dataSize);
return true;
}
template<>
bool Extractor::extractBoundImpl<Poco::Data::Date>(std::size_t pos, Poco::Data::Date& val)
{
@ -280,6 +296,61 @@ bool Extractor::extractManualImpl<std::string>(std::size_t pos, std::string& val
}
template<>
bool Extractor::extractManualImpl<UTF16String>(std::size_t pos, UTF16String& val, SQLSMALLINT cType)
{
std::size_t maxSize = _pPreparator->getMaxFieldSize();
std::size_t fetchedSize = 0;
std::size_t totalSize = 0;
SQLLEN len;
const int bufSize = CHUNK_SIZE;
Poco::Buffer<UTF16String::value_type> apChar(bufSize);
UTF16String::value_type* pChar = apChar.begin();
SQLRETURN rc = 0;
val.clear();
resizeLengths(pos);
do
{
std::memset(pChar, 0, bufSize);
len = 0;
rc = SQLGetData(_rStmt,
(SQLUSMALLINT)pos + 1,
cType, //C data type
pChar, //returned value
bufSize, //buffer length
&len); //length indicator
if (SQL_NO_DATA != rc && Utility::isError(rc))
throw StatementException(_rStmt, "SQLGetData()");
if (SQL_NO_TOTAL == len)//unknown length, throw
throw UnknownDataLengthException("Could not determine returned data length.");
if (isNullLengthIndicator(len))
{
_lengths[pos] = len;
return false;
}
if (SQL_NO_DATA == rc || !len)
break;
_lengths[pos] += len;
fetchedSize = _lengths[pos] > CHUNK_SIZE ? CHUNK_SIZE : _lengths[pos];
totalSize += fetchedSize;
if (totalSize <= maxSize)
val.append(pChar, fetchedSize / sizeof(UTF16Char));
else
throw DataException(format(FLD_SIZE_EXCEEDED_FMT, fetchedSize, maxSize));
} while (true);
return true;
}
template<>
bool Extractor::extractManualImpl<Poco::Data::CLOB>(std::size_t pos,
Poco::Data::CLOB& val,
@ -608,6 +679,42 @@ bool Extractor::extract(std::size_t pos, std::list<std::string>& val)
}
bool Extractor::extract(std::size_t pos, UTF16String& val)
{
if (Preparator::DE_MANUAL == _dataExtraction)
return extractManualImpl(pos, val, SQL_C_WCHAR);
else
return extractBoundImpl(pos, val);
}
bool Extractor::extract(std::size_t pos, std::vector<UTF16String>& val)
{
if (Preparator::DE_BOUND == _dataExtraction)
return extractBoundImplContainer(pos, val);
else
throw InvalidAccessException("Direct container extraction only allowed for bound mode.");
}
bool Extractor::extract(std::size_t pos, std::deque<UTF16String>& val)
{
if (Preparator::DE_BOUND == _dataExtraction)
return extractBoundImplContainer(pos, val);
else
throw InvalidAccessException("Direct container extraction only allowed for bound mode.");
}
bool Extractor::extract(std::size_t pos, std::list<UTF16String>& val)
{
if (Preparator::DE_BOUND == _dataExtraction)
return extractBoundImplContainer(pos, val);
else
throw InvalidAccessException("Direct container extraction only allowed for bound mode.");
}
bool Extractor::extract(std::size_t pos, Poco::Data::BLOB& val)
{
if (Preparator::DE_MANUAL == _dataExtraction)

View File

@ -83,14 +83,16 @@ void ODBCMetaColumn::init()
{
case SQL_BIT:
setType(MetaColumn::FDT_BOOL); break;
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
case -8:// PostgreSQL CHAR (with size specified - psqlODBC)
case -9:// SQL Server NVARCHAR
case -10:// PostgreSQL VARCHAR (without size specified)
setType(MetaColumn::FDT_STRING); break;
case SQL_WCHAR:
case SQL_WVARCHAR:
case SQL_WLONGVARCHAR:
setType(MetaColumn::FDT_WSTRING); break;
case SQL_TINYINT:
setType(MetaColumn::FDT_INT8); break;

View File

@ -72,6 +72,10 @@ void Preparator::freeMemory() const
deleteCachedArray<char>(it->first);
break;
case DT_WCHAR:
deleteCachedArray<UTF16String>(it->first);
break;
case DT_UCHAR:
deleteCachedArray<unsigned char>(it->first);
break;
@ -83,6 +87,13 @@ void Preparator::freeMemory() const
break;
}
case DT_WCHAR_ARRAY:
{
UTF16String::value_type** pc = AnyCast<UTF16String::value_type*>(&_values[it->first]);
if (pc) std::free(*pc);
break;
}
case DT_UCHAR_ARRAY:
{
unsigned char** pc = AnyCast<unsigned char*>(&_values[it->first]);

View File

@ -834,6 +834,17 @@ void ODBCOracleTest::recreateLogTable()
}
void ODBCOracleTest::recreateUnicodeTable()
{
#if defined (POCO_ODBC_UNICODE)
dropObject("TABLE", "UnicodeTable");
try { session() << "CREATE TABLE UnicodeTable (str NVARCHAR2(30))", now; }
catch (ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail("recreateUnicodeTable()"); }
catch (StatementException& se){ std::cout << se.toString() << std::endl; fail("recreateUnicodeTable()"); }
#endif
}
CppUnit::Test* ODBCOracleTest::suite()
{
if ((_pSession = init(_driver, _dsn, _uid, _pwd, _connectString)))
@ -923,6 +934,7 @@ CppUnit::Test* ODBCOracleTest::suite()
CppUnit_addTest(pSuite, ODBCOracleTest, testTransaction);
CppUnit_addTest(pSuite, ODBCOracleTest, testTransactor);
CppUnit_addTest(pSuite, ODBCOracleTest, testNullable);
CppUnit_addTest(pSuite, ODBCOracleTest, testUnicode);
CppUnit_addTest(pSuite, ODBCOracleTest, testReconnect);
return pSuite;

View File

@ -70,6 +70,7 @@ private:
void recreateNullsTable(const std::string& notNull = "");
void recreateMiscTable();
void recreateLogTable();
void recreateUnicodeTable();
static ODBCTest::SessionPtr _pSession;
static ODBCTest::ExecPtr _pExecutor;

View File

@ -53,6 +53,10 @@ using Poco::DateTime;
#define POSTGRESQL_DSN "PocoDataPgSQLTest"
#endif
#if defined(POCO_OS_FAMILY_WINDOWS)
#pragma message ("Using " POSTGRESQL_ODBC_DRIVER " driver.")
#endif
#define POSTGRESQL_SERVER POCO_ODBC_TEST_DATABASE_SERVER
#define POSTGRESQL_PORT "5432"
#define POSTGRESQL_DB "postgres"
@ -559,6 +563,17 @@ void ODBCPostgreSQLTest::recreateLogTable()
}
void ODBCPostgreSQLTest::recreateUnicodeTable()
{
#if defined (POCO_ODBC_UNICODE)
dropObject("TABLE", "UnicodeTable");
try { session() << "CREATE TABLE UnicodeTable (str TEXT)", now; }
catch (ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail("recreateUnicodeTable()"); }
catch (StatementException& se){ std::cout << se.toString() << std::endl; fail("recreateUnicodeTable()"); }
#endif
}
CppUnit::Test* ODBCPostgreSQLTest::suite()
{
if ((_pSession = init(_driver, _dsn, _uid, _pwd, _connectString)))
@ -659,6 +674,7 @@ CppUnit::Test* ODBCPostgreSQLTest::suite()
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTransaction);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testTransactor);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testNullable);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testUnicode);
CppUnit_addTest(pSuite, ODBCPostgreSQLTest, testReconnect);
return pSuite;

View File

@ -69,6 +69,7 @@ private:
void recreateBoolTable();
void recreateMiscTable();
void recreateLogTable();
void recreateUnicodeTable();
void configurePLPgSQL();
/// Configures PL/pgSQL in the database. A reasonable defaults

View File

@ -715,6 +715,17 @@ void ODBCSQLServerTest::recreateLogTable()
}
void ODBCSQLServerTest::recreateUnicodeTable()
{
#if defined (POCO_ODBC_UNICODE)
dropObject("TABLE", "UnicodeTable");
try { session() << "CREATE TABLE UnicodeTable (str NVARCHAR(30))", now; }
catch (ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail("recreateUnicodeTable()"); }
catch (StatementException& se){ std::cout << se.toString() << std::endl; fail("recreateUnicodeTable()"); }
#endif
}
CppUnit::Test* ODBCSQLServerTest::suite()
{
if ((_pSession = init(_driver, _dsn, _uid, _pwd, _connectString, _db)))
@ -801,6 +812,7 @@ CppUnit::Test* ODBCSQLServerTest::suite()
CppUnit_addTest(pSuite, ODBCSQLServerTest, testTransaction);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testTransactor);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testNullable);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testUnicode);
CppUnit_addTest(pSuite, ODBCSQLServerTest, testReconnect);
return pSuite;

View File

@ -74,6 +74,7 @@ private:
void recreateBoolTable();
void recreateMiscTable();
void recreateLogTable();
void recreateUnicodeTable();
static SessionPtr _pSession;
static ExecPtr _pExecutor;

View File

@ -969,7 +969,11 @@ void ODBCTest::testInternalBulkExtraction()
recreatePersonTable();
_pSession->setFeature("autoBind", true);
_pSession->setFeature("autoExtract", true);
#ifdef POCO_ODBC_UNICODE
_pExecutor->internalBulkExtractionUTF16();
#else
_pExecutor->internalBulkExtraction();
#endif
}
@ -1199,6 +1203,25 @@ void ODBCTest::testNullable()
}
void ODBCTest::testUnicode()
{
#if defined (POCO_ODBC_UNICODE)
if (!_pSession) fail("Test not available.");
for (int i = 0; i < 8;)
{
recreateUnicodeTable();
_pSession->setFeature("autoBind", bindValue(i));
_pSession->setFeature("autoExtract", bindValue(i + 1));
_pExecutor->unicode(_rConnectString);
i += 2;
}
#else
std::cout << "Not an UNICODE build, skipping." << std::endl;
#endif
}
void ODBCTest::testReconnect()
{
if (!_pSession) fail ("Test not available.");
@ -1296,7 +1319,7 @@ 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);
return new Session(Poco::Data::ODBC::Connector::KEY, dbConnString, 5);
}catch (ConnectionFailedException& ex)
{
std::cout << ex.displayText() << std::endl;

View File

@ -149,6 +149,8 @@ public:
virtual void testTransactor();
virtual void testNullable();
virtual void testUnicode();
virtual void testReconnect();
protected:
@ -172,6 +174,7 @@ protected:
virtual void recreateBoolTable();
virtual void recreateMiscTable();
virtual void recreateLogTable();
virtual void recreateUnicodeTable();
static SessionPtr init(const std::string& driver,
std::string& dsn,
@ -357,6 +360,12 @@ inline void ODBCTest::recreateLogTable()
}
inline void ODBCTest::recreateUnicodeTable()
{
throw Poco::NotImplementedException("ODBCTest::recreateUnicodeTable()");
}
inline bool ODBCTest::bindValue(int i)
{
poco_assert (i < 8);

View File

@ -46,6 +46,8 @@
#include "Poco/Data/ODBC/Preparator.h"
#include "Poco/Data/ODBC/ODBCException.h"
#include "Poco/Data/ODBC/ODBCStatementImpl.h"
#include "Poco/UnicodeConverter.h"
#include "Poco/UTFString.h"
#include <sqltypes.h>
#include <iostream>
#include <sstream>
@ -94,7 +96,9 @@ using Poco::NotImplementedException;
using Poco::BadCastException;
using Poco::RangeException;
using Poco::TimeoutException;
using Poco::UnicodeConverter;
using Poco::UTF16String;
using Poco::UTF32String;
struct Person
{
@ -2441,24 +2445,24 @@ void SQLExecutor::dateTime()
DateTime born(1965, 6, 18, 5, 35, 1);
int count = 0;
try { session() << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(born), now; }
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
catch (ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail(funct); }
catch (StatementException& se){ std::cout << se.toString() << std::endl; fail(funct); }
try { session() << "SELECT COUNT(*) FROM Person", into(count), now; }
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
assert (count == 1);
catch (ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail(funct); }
catch (StatementException& se){ std::cout << se.toString() << std::endl; fail(funct); }
assert(count == 1);
DateTime res;
try { session() << "SELECT Born FROM Person", into(res), now; }
catch(ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.toString() << std::endl; fail (funct); }
assert (res == born);
catch (ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail(funct); }
catch (StatementException& se){ std::cout << se.toString() << std::endl; fail(funct); }
assert(res == born);
Statement stmt = (session() << "SELECT Born FROM Person", now);
RecordSet rset(stmt);
res = rset["Born"].convert<DateTime>();
assert (res == born);
assert(res == born);
}
@ -2619,7 +2623,7 @@ void SQLExecutor::internalExtraction()
rset.moveFirst();
assert (rset["str0"] == "3");
rset.moveLast();
assert (rset["str0"] == "6");
assert(rset["str0"] == "6");
RecordSet rset2(rset);
assert (3 == rset2.columnCount());
@ -2645,8 +2649,17 @@ void SQLExecutor::internalExtraction()
assert (2.5 == f);
}
s = rset.value<std::string>(2,2);
assert ("5" == s);
try
{
s = rset.value<std::string>(2, 2);
}
catch (BadCastException&)
{
UTF16String us = rset.value<Poco::UTF16String>(2, 2);
Poco::UnicodeConverter::convert(us, s);
}
assert("5" == s);
i = rset.value("str0", 2);
assert (5 == i);
@ -2815,8 +2828,11 @@ void SQLExecutor::internalBulkExtraction()
Statement stmt = (session() << "SELECT * FROM Person", bulk(size), now);
RecordSet rset(stmt);
assert (size == rset.rowCount());
assert ("LN0" == rset["LastName"]);
assert("LN0" == rset["LastName"]);
assert (0 == rset["Age"]);
rset.moveNext();
assert("LN1" == rset["LastName"]);
assert(1 == rset["Age"]);
rset.moveLast();
assert (std::string("LN") + NumberFormatter::format(size - 1) == rset["LastName"]);
assert (size - 1 == rset["Age"]);
@ -2840,6 +2856,68 @@ void SQLExecutor::internalBulkExtraction()
}
void SQLExecutor::internalBulkExtractionUTF16()
{
std::string funct = "internalBulkExtraction()";
int size = 100;
std::vector<UTF16String> lastName(size);
std::vector<UTF16String> firstName(size);
std::vector<UTF16String> address(size);
std::vector<int> age(size);
for (int i = 0; i < size; ++i)
{
lastName[i] = Poco::UnicodeConverter::to<UTF16String>("LN" + NumberFormatter::format(i));
firstName[i] = Poco::UnicodeConverter::to<UTF16String>("FN" + NumberFormatter::format(i));
address[i] = Poco::UnicodeConverter::to<UTF16String>("Addr" + NumberFormatter::format(i));
age[i] = i;
}
try
{
session() << "INSERT INTO Person VALUES (?,?,?,?)",
use(lastName, bulk),
use(firstName, bulk),
use(address, bulk),
use(age, bulk),
now;
}
catch (ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail(funct); }
catch (StatementException& se){ std::cout << se.toString() << std::endl; fail(funct); }
try
{
Statement stmt = (session() << "SELECT * FROM Person", bulk(size), now);
RecordSet rset(stmt);
assert(size == rset.rowCount());
assert(Poco::UnicodeConverter::to<UTF16String>("LN0") == rset["LastName"]);
assert(0 == rset["Age"]);
rset.moveNext();
assert(Poco::UnicodeConverter::to<UTF16String>("LN1") == rset["LastName"]);
assert(1 == rset["Age"]);
rset.moveLast();
assert(std::string("LN") + NumberFormatter::format(size - 1) == rset["LastName"]);
assert(size - 1 == rset["Age"]);
}
catch (ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail(funct); }
catch (StatementException& se){ std::cout << se.toString() << std::endl; fail(funct); }
try
{
Statement stmt = (session() << "SELECT * FROM Person", limit(size), bulk, now);
RecordSet rset(stmt);
assert(size == rset.rowCount());
assert("LN0" == rset["LastName"]);
assert(0 == rset["Age"]);
rset.moveLast();
assert(std::string("LN") + NumberFormatter::format(size - 1) == rset["LastName"]);
assert(size - 1 == rset["Age"]);
}
catch (ConnectionException& ce){ std::cout << ce.toString() << std::endl; fail(funct); }
catch (StatementException& se){ std::cout << se.toString() << std::endl; fail(funct); }
}
void SQLExecutor::internalStorageType()
{
std::string funct = "internalStorageType()";
@ -3247,8 +3325,13 @@ void SQLExecutor::any()
{
Any i = 42;
Any f = 42.5;
Any s = std::string("42");
std::string ss("42");
Any s = ss;
#ifdef POCO_ODBC_UNICODE
UTF16String us;
Poco::UnicodeConverter::convert(ss, us);
s = us;
#endif
Session tmp = session();
tmp << "INSERT INTO Anys VALUES (?, ?, ?)", use(i), use(f), use(s), now;
@ -3263,7 +3346,19 @@ void SQLExecutor::any()
tmp << "SELECT * FROM Anys", into(i), into(f), into(s), now;
assert (AnyCast<int>(i) == 42);
assert (AnyCast<double>(f) == 42.5);
#ifdef POCO_ODBC_UNICODE
// drivers may behave differently here
try
{
assert(AnyCast<UTF16String>(s) == us);
}
catch (BadCastException&)
{
assert (AnyCast<std::string>(s) == "42");
}
#else
assert (AnyCast<std::string>(s) == "42");
#endif
}
@ -3830,7 +3925,7 @@ void SQLExecutor::nullable()
assert (rs.isNull("EmptyInteger"));
assert (rs.isNull("EmptyFloat"));
assert (rs.isNull("EmptyDateTime"));
Var di = 1;
Var df = 1.5;
Var ds = "abc";
@ -3840,8 +3935,8 @@ void SQLExecutor::nullable()
assert (!df.isEmpty());
assert (!ds.isEmpty());
assert (!dd.isEmpty());
session() << "SELECT EmptyString, EmptyInteger, EmptyFloat, EmptyDateTime FROM NullableTest", into(di), into(df), into(ds), into(dd), now;
Statement stmt = (session() << "SELECT EmptyString, EmptyInteger, EmptyFloat, EmptyDateTime FROM NullableTest", into(ds), into(di), into(df), into(dd), now);
assert (di.isEmpty());
assert (df.isEmpty());
@ -3889,3 +3984,19 @@ void SQLExecutor::reconnect()
assert (count == age);
assert (session().isConnected());
}
void SQLExecutor::unicode(const std::string& dbConnString)
{
const unsigned char supp[] = { 0x41, 0x42, 0xf0, 0x90, 0x82, 0xa4, 0xf0, 0xaf, 0xa6, 0xa0, 0xf0, 0xaf, 0xa8, 0x9d, 0x00 };
std::string text((const char*) supp);
UTF16String wtext;
Poco::UnicodeConverter::convert(text, wtext);
session() << "INSERT INTO UnicodeTable VALUES (?)", use(wtext), now;
wtext.clear();
text.clear();
session() << "SELECT str FROM UnicodeTable", into(wtext), now;
Poco::UnicodeConverter::convert(wtext, text);
assert(text == std::string((const char*)supp));
}

View File

@ -479,6 +479,7 @@ public:
const std::string& intFldName = "int0");
void internalBulkExtraction();
void internalBulkExtractionUTF16();
void internalStorageType();
void nulls();
void notNulls(const std::string& sqlState = "23502");
@ -503,6 +504,8 @@ public:
void transactor();
void nullable();
void unicode(const std::string& dbConnString);
void reconnect();
private:

View File

@ -28,6 +28,7 @@
#include "Poco/Nullable.h"
#include "Poco/Any.h"
#include "Poco/Dynamic/Var.h"
#include "Poco/UTFString.h"
#include <vector>
#include <deque>
#include <list>
@ -246,6 +247,18 @@ public:
virtual void bind(std::size_t pos, const std::list<std::string>& val, Direction dir = PD_IN);
/// Binds a string list.
virtual void bind(std::size_t pos, const UTF16String& val, Direction dir = PD_IN) = 0;
/// Binds a string.
virtual void bind(std::size_t pos, const std::vector<UTF16String>& val, Direction dir = PD_IN);
/// Binds a string vector.
virtual void bind(std::size_t pos, const std::deque<UTF16String>& val, Direction dir = PD_IN);
/// Binds a string deque.
virtual void bind(std::size_t pos, const std::list<UTF16String>& val, Direction dir = PD_IN);
/// Binds a string list.
virtual void bind(std::size_t pos, const BLOB& val, Direction dir = PD_IN) = 0;
/// Binds a BLOB.

View File

@ -25,6 +25,7 @@
#include "Poco/Data/AbstractPreparation.h"
#include "Poco/Data/Limit.h"
#include "Poco/RefCountedObject.h"
#include "Poco/UTFString.h"
#include "Poco/AutoPtr.h"
#include <vector>
#include <deque>
@ -144,7 +145,26 @@ public:
/// - string is empty
/// - getEmptyStringIsNull() returns true
bool isValueNull(const Poco::UTF16String& str, bool deflt);
/// Overload for const reference to UTF16String.
///
/// Returns true when folowing conditions are met:
///
/// - string is empty
/// - getEmptyStringIsNull() returns true
private:
template <typename S>
bool isStringNull(const S& str, bool deflt)
{
if (getForceEmptyString()) return false;
if (getEmptyStringIsNull() && str.empty())
return true;
return deflt;
}
ExtractorPtr _pExtractor;
Poco::UInt32 _limit;
Poco::UInt32 _position;
@ -242,6 +262,18 @@ inline bool AbstractExtraction::getForceEmptyString() const
}
inline bool AbstractExtraction::isValueNull(const std::string& str, bool deflt)
{
return isStringNull(str, deflt);
}
inline bool AbstractExtraction::isValueNull(const Poco::UTF16String& str, bool deflt)
{
return isStringNull(str, deflt);
}
} } // namespace Poco::Data

View File

@ -23,6 +23,7 @@
#include "Poco/Data/Data.h"
#include "Poco/Data/Constants.h"
#include "Poco/Data/LOB.h"
#include "Poco/UTFString.h"
#include <vector>
#include <deque>
#include <list>
@ -233,6 +234,18 @@ public:
virtual bool extract(std::size_t pos, std::list<std::string>& val);
/// Extracts a string list.
virtual bool extract(std::size_t pos, UTF16String& val) = 0;
/// Extracts a string. Returns false if null was received.
virtual bool extract(std::size_t pos, std::vector<UTF16String>& val);
/// Extracts a string vector.
virtual bool extract(std::size_t pos, std::deque<UTF16String>& val);
/// Extracts a string deque.
virtual bool extract(std::size_t pos, std::list<UTF16String>& val);
/// Extracts a string list.
virtual bool extract(std::size_t pos, BLOB& val) = 0;
/// Extracts a BLOB. Returns false if null was received.

View File

@ -44,7 +44,7 @@ public:
/// Destroys the AbstractPreparation.
virtual void prepare() = 0;
/// Preparations data.
/// Prepares data.
protected:
AbstractPreparation();

View File

@ -23,6 +23,7 @@
#include "Poco/Data/Data.h"
#include "Poco/RefCountedObject.h"
#include "Poco/Data/LOB.h"
#include "Poco/UTFString.h"
#include <vector>
#include <deque>
#include <list>
@ -237,6 +238,18 @@ public:
/// Prepares a string deque.
virtual void prepare(std::size_t pos, const std::list<std::string>& val);
/// Prepares a character list.
virtual void prepare(std::size_t pos, const UTF16String&) = 0;
/// Prepares a string.
virtual void prepare(std::size_t pos, const std::vector<UTF16String>& val);
/// Prepares a string vector.
virtual void prepare(std::size_t pos, const std::deque<UTF16String>& val);
/// Prepares a string deque.
virtual void prepare(std::size_t pos, const std::list<UTF16String>& val);
/// Prepares a string list.
virtual void prepare(std::size_t pos, const BLOB&) = 0;

View File

@ -46,6 +46,7 @@ public:
FDT_FLOAT,
FDT_DOUBLE,
FDT_STRING,
FDT_WSTRING,
FDT_BLOB,
FDT_CLOB,
FDT_DATE,

View File

@ -50,7 +50,7 @@ public:
}
void prepare()
/// Preparations data.
/// Prepares data.
{
TypeHandler<T>::prepare(_pos, _val, preparation());
}
@ -82,7 +82,7 @@ public:
}
void prepare()
/// Preparations data.
/// Prepares data.
{
TypeHandler<std::vector<T> >::prepare(_pos, _val, preparation());
}
@ -114,7 +114,7 @@ public:
}
void prepare()
/// Preparations data.
/// Prepares data.
{
TypeHandler<std::deque<T> >::prepare(_pos, _val, preparation());
}
@ -146,7 +146,7 @@ public:
}
void prepare()
/// Preparations data.
/// Prepares data.
{
TypeHandler<std::list<T> >::prepare(_pos, _val, preparation());
}

View File

@ -292,6 +292,24 @@ void AbstractBinder::bind(std::size_t pos, const std::list<std::string>& val, Di
}
void AbstractBinder::bind(std::size_t pos, const std::vector<UTF16String>& val, Direction dir)
{
throw NotImplementedException("std::vector binder must be implemented.");
}
void AbstractBinder::bind(std::size_t pos, const std::deque<UTF16String>& val, Direction dir)
{
throw NotImplementedException("std::deque binder must be implemented.");
}
void AbstractBinder::bind(std::size_t pos, const std::list<UTF16String>& val, Direction dir)
{
throw NotImplementedException("std::list binder must be implemented.");
}
void AbstractBinder::bind(std::size_t pos, const std::vector<BLOB>& val, Direction dir)
{
throw NotImplementedException("std::vector binder must be implemented.");
@ -408,6 +426,8 @@ void AbstractBinder::bind(std::size_t pos, const Any& val, Direction dir)
bind(pos, RefAnyCast<Int32>(val), dir);
else if(type == typeid(std::string))
bind(pos, RefAnyCast<std::string>(val), dir);
else if (type == typeid(Poco::UTF16String))
bind(pos, RefAnyCast<Poco::UTF16String>(val), dir);
else if (type == typeid(bool))
bind(pos, RefAnyCast<bool>(val), dir);
else if(type == typeid(char))
@ -457,6 +477,8 @@ void AbstractBinder::bind(std::size_t pos, const Poco::Dynamic::Var& val, Direct
bind(pos, val.extract<Int32>(), dir);
else if(type == typeid(std::string))
bind(pos, val.extract<std::string>(), dir);
else if (type == typeid(Poco::UTF16String))
bind(pos, val.extract<Poco::UTF16String>(), dir);
else if (type == typeid(bool))
bind(pos, val.extract<bool>(), dir);
else if(type == typeid(char))

View File

@ -39,15 +39,4 @@ AbstractExtraction::~AbstractExtraction()
}
bool AbstractExtraction::isValueNull(const std::string& str, bool deflt)
{
if (getForceEmptyString()) return false;
if (getEmptyStringIsNull() && str.empty())
return true;
return deflt;
}
} } // namespace Poco::Data

View File

@ -286,6 +286,24 @@ bool AbstractExtractor::extract(std::size_t pos, std::list<std::string>& val)
}
bool AbstractExtractor::extract(std::size_t pos, std::vector<UTF16String>& val)
{
throw NotImplementedException("std::vector extractor must be implemented.");
}
bool AbstractExtractor::extract(std::size_t pos, std::deque<UTF16String>& val)
{
throw NotImplementedException("std::deque extractor must be implemented.");
}
bool AbstractExtractor::extract(std::size_t pos, std::list<UTF16String>& val)
{
throw NotImplementedException("std::list extractor must be implemented.");
}
bool AbstractExtractor::extract(std::size_t pos, std::vector<BLOB>& val)
{
throw NotImplementedException("std::vector extractor must be implemented.");

View File

@ -287,6 +287,24 @@ void AbstractPreparator::prepare(std::size_t pos, const std::list<std::string>&
}
void AbstractPreparator::prepare(std::size_t pos, const std::vector<UTF16String>& val)
{
throw NotImplementedException("std::vector preparator must be implemented.");
}
void AbstractPreparator::prepare(std::size_t pos, const std::deque<UTF16String>& val)
{
throw NotImplementedException("std::deque preparator must be implemented.");
}
void AbstractPreparator::prepare(std::size_t pos, const std::list<UTF16String>& val)
{
throw NotImplementedException("std::list preparator must be implemented.");
}
void AbstractPreparator::prepare(std::size_t pos, const std::vector<BLOB>& val)
{
throw NotImplementedException("std::vector preparator must be implemented.");

View File

@ -20,10 +20,12 @@
#include "Poco/Data/Time.h"
#include "Poco/Data/DataException.h"
#include "Poco/DateTime.h"
#include "Poco/UTFString.h"
using namespace Poco::Data::Keywords;
using Poco::DateTime;
using Poco::UTF16String;
namespace Poco {
@ -104,6 +106,7 @@ Poco::Dynamic::Var RecordSet::value(std::size_t col, std::size_t row, bool useFi
case MetaColumn::FDT_FLOAT: return value<float>(col, row, useFilter);
case MetaColumn::FDT_DOUBLE: return value<double>(col, row, useFilter);
case MetaColumn::FDT_STRING: return value<std::string>(col, row, useFilter);
case MetaColumn::FDT_WSTRING: return value<UTF16String>(col, row, useFilter);
case MetaColumn::FDT_BLOB: return value<BLOB>(col, row, useFilter);
case MetaColumn::FDT_CLOB: return value<CLOB>(col, row, useFilter);
case MetaColumn::FDT_DATE: return value<Date>(col, row, useFilter);
@ -136,6 +139,7 @@ Poco::Dynamic::Var RecordSet::value(const std::string& name, std::size_t row, bo
case MetaColumn::FDT_FLOAT: return value<float>(name, row, useFilter);
case MetaColumn::FDT_DOUBLE: return value<double>(name, row, useFilter);
case MetaColumn::FDT_STRING: return value<std::string>(name, row, useFilter);
case MetaColumn::FDT_WSTRING: return value<UTF16String>(name, row, useFilter);
case MetaColumn::FDT_BLOB: return value<BLOB>(name, row, useFilter);
case MetaColumn::FDT_DATE: return value<Date>(name, row, useFilter);
case MetaColumn::FDT_TIME: return value<Time>(name, row, useFilter);

View File

@ -327,6 +327,8 @@ void StatementImpl::makeExtractors(std::size_t count)
addInternalExtract<double>(mc); break;
case MetaColumn::FDT_STRING:
addInternalExtract<std::string>(mc); break;
case MetaColumn::FDT_WSTRING:
addInternalExtract<Poco::UTF16String>(mc); break;
case MetaColumn::FDT_BLOB:
addInternalExtract<BLOB>(mc); break;
case MetaColumn::FDT_DATE:

View File

@ -112,6 +112,11 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir)
}
void Binder::bind(std::size_t pos, const Poco::UTF16String& val, Direction dir)
{
}
void Binder::bind(std::size_t pos, const BLOB& val, Direction dir)
{
}

View File

@ -84,6 +84,9 @@ public:
void bind(std::size_t pos, const std::string& val, Direction dir);
/// Binds a string.
void bind(std::size_t pos, const Poco::UTF16String& val, Direction dir);
/// Binds a UTF16String.
void bind(std::size_t pos, const BLOB& val, Direction dir);
/// Binds a BLOB.

View File

@ -137,6 +137,14 @@ bool Extractor::extract(std::size_t pos, std::string& val)
}
bool Extractor::extract(std::size_t pos, Poco::UTF16String& val)
{
std::string str("");
Poco::UnicodeConverter::convert(str, val);
return true;
}
bool Extractor::extract(std::size_t pos, Poco::Data::BLOB& val)
{
return true;

View File

@ -87,6 +87,9 @@ public:
bool extract(std::size_t pos, std::string& val);
/// Extracts a string.
bool extract(std::size_t pos, Poco::UTF16String& val);
/// Extracts a UTF16String.
bool extract(std::size_t pos, Poco::Data::BLOB& val);
/// Extracts a BLOB.

View File

@ -107,6 +107,11 @@ void Preparator::prepare(std::size_t pos, const std::string&)
}
void Preparator::prepare(std::size_t pos, const Poco::UTF16String&)
{
}
void Preparator::prepare(std::size_t pos, const Poco::Data::BLOB&)
{
}

View File

@ -36,72 +36,75 @@ public:
/// Destroys the Preparator.
void prepare(std::size_t pos, const Poco::Int8&);
/// Preparations an Int8.
/// Prepares an Int8.
void prepare(std::size_t pos, const Poco::UInt8&);
/// Preparations an UInt8.
/// Prepares an UInt8.
void prepare(std::size_t pos, const Poco::Int16&);
/// Preparations an Int16.
/// Prepares an Int16.
void prepare(std::size_t pos, const Poco::UInt16&);
/// Preparations an UInt16.
/// Prepares an UInt16.
void prepare(std::size_t pos, const Poco::Int32&);
/// Preparations an Int32.
/// Prepares an Int32.
void prepare(std::size_t pos, const Poco::UInt32&);
/// Preparations an UInt32.
/// Prepares an UInt32.
void prepare(std::size_t pos, const Poco::Int64&);
/// Preparations an Int64.
/// Prepares an Int64.
void prepare(std::size_t pos, const Poco::UInt64&);
/// Preparations an UInt64.
/// Prepares an UInt64.
#ifndef POCO_LONG_IS_64_BIT
void prepare(std::size_t pos, const long&);
/// Preparations a long.
/// Prepares a long.
void prepare(std::size_t pos, const unsigned long&);
/// Preparations an unsigned long.
/// Prepares an unsigned long.
#endif
void prepare(std::size_t pos, const bool&);
/// Preparations a boolean.
/// Prepares a boolean.
void prepare(std::size_t pos, const float&);
/// Preparations a float.
/// Prepares a float.
void prepare(std::size_t pos, const double&);
/// Preparations a double.
/// Prepares a double.
void prepare(std::size_t pos, const char&);
/// Preparations a single character.
/// Prepares a single character.
void prepare(std::size_t pos, const std::string&);
/// Preparations a string.
/// Prepares a string.
void prepare(std::size_t pos, const Poco::UTF16String&);
/// Prepares a UTF16String.
void prepare(std::size_t pos, const Poco::Data::BLOB&);
/// Preparations a BLOB.
/// Prepares a BLOB.
void prepare(std::size_t pos, const Poco::Data::CLOB&);
/// Preparations a CLOB.
/// Prepares a CLOB.
void prepare(std::size_t pos, const Poco::Data::Date&);
/// Preparations a Date.
/// Prepares a Date.
void prepare(std::size_t pos, const Poco::Data::Time&);
/// Preparations a Time.
/// Prepares a Time.
void prepare(std::size_t pos, const Poco::DateTime&);
/// Preparations a DateTime.
/// Prepares a DateTime.
void prepare(std::size_t pos, const Poco::Any&);
/// Preparations an Any.
/// Prepares an Any.
void prepare(std::size_t pos, const Poco::Dynamic::Var&);
/// Preparations a Var.
/// Prepares a Var.
};

View File

@ -1060,6 +1060,7 @@
<ClInclude Include="include\Poco\Types.h" />
<ClInclude Include="include\Poco\UnWindows.h" />
<ClInclude Include="include\Poco\UTF32Encoding.h" />
<ClInclude Include="include\Poco\UTFString.h" />
<ClInclude Include="include\Poco\Version.h" />
<ClInclude Include="include\Poco\Void.h" />
<ClInclude Include="include\Poco\Base32Decoder.h" />

View File

@ -1880,6 +1880,10 @@
<ClInclude Include="include\Poco\UTF32Encoding.h">
<Filter>Text\Header Files</Filter>
</ClInclude>
<ClInclude Include="include\Poco\PBKDF2Engine.h" />
<ClInclude Include="include\Poco\UTFString.h">
<Filter>Text\Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="src\pocomsg.rc">

View File

@ -2027,6 +2027,22 @@ inline bool operator != (const std::string& other, const Var& da)
}
inline bool operator == (const UTF16String& other, const Var& da)
/// Equality operator for comparing Var with UTF16String
{
if (da.isEmpty()) return false;
return other == da.convert<UTF16String>();
}
inline bool operator != (const UTF16String& other, const Var& da)
/// Inequality operator for comparing Var with UTF16String
{
if (da.isEmpty()) return true;
return other != da.convert<UTF16String>();
}
inline bool operator == (const char* other, const Var& da)
/// Equality operator for comparing Var with const char*
{

View File

@ -30,6 +30,9 @@
#include "Poco/DateTimeFormatter.h"
#include "Poco/DateTimeParser.h"
#include "Poco/String.h"
#include "Poco/UnicodeConverter.h"
#include "Poco/UTFString.h"
#include "Poco/UTF8String.h"
#include "Poco/Any.h"
#include "Poco/Exception.h"
#include <vector>
@ -106,7 +109,7 @@ class Foundation_API VarHolder
/// Only data types for which VarHolder specialization exists are supported.
///
/// Provided are specializations for all C++ built-in types with addition of
/// std::string, DateTime, LocalDateTime, Timestamp, std::vector<Var> and DynamicStruct.
/// std::string, Poco::UTF16String, DateTime, LocalDateTime, Timestamp, std::vector<Var> and DynamicStruct.
///
/// Additional types can be supported by adding specializations. When implementing specializations,
/// the only condition is that they reside in Poco namespace and implement the pure virtual functions
@ -208,6 +211,10 @@ public:
/// Throws BadCastException. Must be overriden in a type
/// specialization in order to suport the conversion.
virtual void convert(Poco::UTF16String& val) const;
/// Throws BadCastException. Must be overriden in a type
/// specialization in order to suport the conversion.
virtual bool isArray() const;
/// Returns true.
@ -534,6 +541,12 @@ inline void VarHolder::convert(std::string& /*val*/) const
}
inline void VarHolder::convert(Poco::UTF16String& /*val*/) const
{
throw BadCastException("Can not convert to Poco::UTF16String");
}
inline bool VarHolder::isArray() const
{
return true;
@ -728,6 +741,12 @@ public:
val = NumberFormatter::format(_val);
}
void convert(Poco::UTF16String& val) const
{
std::string str = NumberFormatter::format(_val);
Poco::UnicodeConverter::convert(str, val);
}
VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const
{
return cloneHolder(pVarHolder, _val);
@ -861,6 +880,12 @@ public:
val = NumberFormatter::format(_val);
}
void convert(Poco::UTF16String& val) const
{
std::string str = NumberFormatter::format(_val);
Poco::UnicodeConverter::convert(str, val);
}
VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const
{
return cloneHolder(pVarHolder, _val);
@ -2280,15 +2305,15 @@ private:
};
template <typename T>
class VarHolderImpl<std::basic_string<T> >: public VarHolder
template <>
class VarHolderImpl<std::string>: public VarHolder
{
public:
VarHolderImpl(const char* pVal): _val(pVal)
{
}
VarHolderImpl(const std::string& val): _val(val)
VarHolderImpl(const std::string& val) : _val(val)
{
}
@ -2347,16 +2372,16 @@ public:
void convert(bool& val) const
{
static const std::string VAL_FALSE("false");
static const std::string VAL_INT_FALSE("0");
if (_val.empty() ||
_val == VAL_INT_FALSE ||
(icompare(_val, VAL_FALSE) == 0))
if (_val.empty())
{
val = false;
return;
}
else val = true;
static const std::string VAL_FALSE("false");
static const std::string VAL_INT_FALSE("0");
val = (_val != VAL_INT_FALSE &&
(icompare(_val, VAL_FALSE) != 0));
}
void convert(float& val) const
@ -2383,6 +2408,11 @@ public:
val = _val;
}
void convert(Poco::UTF16String& val) const
{
Poco::UnicodeConverter::convert(_val, val);
}
void convert(DateTime& val) const
{
int tzd = 0;
@ -2415,7 +2445,7 @@ public:
return cloneHolder(pVarHolder, _val);
}
const std::string& value() const
const std:: string& value() const
{
return _val;
}
@ -2430,14 +2460,14 @@ public:
return _val.length();
}
T& operator[](std::string::size_type n)
char& operator[](std::string::size_type n)
{
if (n < size()) return _val.operator[](n);
throw RangeException("String index out of range");
}
const T& operator[](std::string::size_type n) const
const char& operator[](std::string::size_type n) const
{
if (n < size()) return _val.operator[](n);
@ -2449,7 +2479,195 @@ private:
VarHolderImpl(const VarHolderImpl&);
VarHolderImpl& operator = (const VarHolderImpl&);
std::basic_string<T> _val;
std::string _val;
};
template <>
class VarHolderImpl<UTF16String>: public VarHolder
{
public:
VarHolderImpl(const char* pVal) : _val(Poco::UnicodeConverter::to<UTF16String>(pVal))
{
}
VarHolderImpl(const Poco::UTF16String& val) : _val(val)
{
}
~VarHolderImpl()
{
}
const std::type_info& type() const
{
return typeid(Poco::UTF16String);
}
void convert(Int8& val) const
{
int v = NumberParser::parse(toStdString());
convertToSmaller(v, val);
}
void convert(Int16& val) const
{
int v = NumberParser::parse(toStdString());
convertToSmaller(v, val);
}
void convert(Int32& val) const
{
val = NumberParser::parse(toStdString());
}
void convert(Int64& val) const
{
val = NumberParser::parse64(toStdString());
}
void convert(UInt8& val) const
{
unsigned int v = NumberParser::parseUnsigned(toStdString());
convertToSmallerUnsigned(v, val);
}
void convert(UInt16& val) const
{
unsigned int v = NumberParser::parseUnsigned(toStdString());
convertToSmallerUnsigned(v, val);
}
void convert(UInt32& val) const
{
val = NumberParser::parseUnsigned(toStdString());
}
void convert(UInt64& val) const
{
val = NumberParser::parseUnsigned64(toStdString());
}
void convert(bool& val) const
{
static const std::string VAL_FALSE("false");
static const std::string VAL_INT_FALSE("0");
if (_val.empty()) val = false;
std::string str;
UnicodeConverter::convert(_val, str);
val = (str != VAL_INT_FALSE &&
(icompare(str, VAL_FALSE) != 0));
}
void convert(float& val) const
{
double v = NumberParser::parseFloat(toStdString());
convertToSmaller(v, val);
}
void convert(double& val) const
{
val = NumberParser::parseFloat(toStdString());
}
void convert(char& val) const
{
if (_val.empty())
val = '\0';
else
{
std::string s;
UnicodeConverter::convert(_val, s);
val = s[0];
}
}
void convert(Poco::UTF16String& val) const
{
val = _val;
}
void convert(std::string& val) const
{
UnicodeConverter::convert(_val, val);
}
void convert(DateTime& val) const
{
int tzd = 0;
if (!DateTimeParser::tryParse(DateTimeFormat::ISO8601_FORMAT, toStdString(), val, tzd))
throw BadCastException("string -> DateTime");
}
void convert(LocalDateTime& ldt) const
{
int tzd = 0;
DateTime tmp;
if (!DateTimeParser::tryParse(DateTimeFormat::ISO8601_FORMAT, toStdString(), tmp, tzd))
throw BadCastException("string -> LocalDateTime");
ldt = LocalDateTime(tzd, tmp, false);
}
void convert(Timestamp& ts) const
{
int tzd = 0;
DateTime tmp;
if (!DateTimeParser::tryParse(DateTimeFormat::ISO8601_FORMAT, toStdString(), tmp, tzd))
throw BadCastException("string -> Timestamp");
ts = tmp.timestamp();
}
VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const
{
return cloneHolder(pVarHolder, _val);
}
const Poco::UTF16String& value() const
{
return _val;
}
bool isString() const
{
return true;
}
std::size_t size() const
{
return _val.length();
}
UTF16Char& operator[](Poco::UTF16String::size_type n)
{
if (n < size()) return _val.operator[](n);
throw RangeException("String index out of range");
}
const UTF16Char& operator[](Poco::UTF16String::size_type n) const
{
if (n < size()) return _val.operator[](n);
throw RangeException("String index out of range");
}
private:
VarHolderImpl();
VarHolderImpl(const VarHolderImpl&);
VarHolderImpl& operator = (const VarHolderImpl&);
std::string toStdString() const
{
std::string str;
UnicodeConverter::convert(_val, str);
return str;
}
Poco::UTF16String _val;
};

View File

@ -108,6 +108,23 @@ public:
convert(f, l, t);
}
template <typename T>
static T to(const char* pChar)
{
T utfStr;
Poco::UnicodeConverter::convert(pChar, utfStr);
return utfStr;
}
template <typename T>
static T to(const std::string& str)
{
T utfStr;
Poco::UnicodeConverter::convert(str, utfStr);
return utfStr;
}
template <typename T>
static std::size_t UTFStrlen(const T* ptr)
/// Returns the length (in characters) of a zero-terminated UTF string.

View File

@ -175,7 +175,7 @@ void TextConverterTest::testLatin1toUTF8()
int errors = converter.convert(latin1Text, result0);
assert (result0 == utf8Text);
assert (errors == 0);
assertEqual (result0.size(), 7);
assertEqual((long) result0.size(), 7);
std::string result1;
errors = converter.convert(latin1Chars, 6, result1);
@ -200,13 +200,13 @@ void TextConverterTest::testLatin2toUTF8()
int errors = converter.convert(latinText, result0);
assertEqual (result0, utf8Text);
assertEqual (errors, 0);
assertEqual (result0.size(), 49);
assertEqual((long) result0.size(), 49);
std::string result1;
errors = converter.convert(latinChars, 25, result1);
assertEqual (result1, utf8Text);
assertEqual (errors, 0);
assertEqual (result1.size(), 49);
assertEqual((long) result1.size(), 49);
}
@ -226,13 +226,13 @@ void TextConverterTest::testLatin9toUTF8()
int errors = converter.convert(latinText, result0);
assertEqual (result0, utf8Text);
assertEqual (errors, 0);
assertEqual (result0.size(), 43);
assertEqual((long) result0.size(), 43);
std::string result1;
errors = converter.convert(latinChars, 25, result1);
assertEqual (result1, utf8Text);
assertEqual (errors, 0);
assertEqual (result1.size(), 43);
assertEqual(result1, utf8Text);
assertEqual((long) errors, 0);
assertEqual((long) result1.size(), 43);
}
@ -252,13 +252,13 @@ void TextConverterTest::testCP1250toUTF8()
int errors = converter.convert(latinText, result0);
assertEqual (result0, utf8Text);
assertEqual (errors, 0);
assertEqual (result0.size(), 49);
assertEqual((long) result0.size(), 49);
std::string result1;
errors = converter.convert(latinChars, 25, result1);
assertEqual (result1, utf8Text);
assertEqual (errors, 0);
assertEqual (result1.size(), 49);
assertEqual(result1, utf8Text);
assertEqual((long) errors, 0);
assertEqual((long) result1.size(), 49);
}
@ -277,13 +277,13 @@ void TextConverterTest::testCP1251toUTF8()
int errors = converter.convert(latinText, result0);
assertEqual (result0, utf8Text);
assertEqual (errors, 0);
assertEqual (result0.size(), 62);
assertEqual((long) result0.size(), 62);
std::string result1;
errors = converter.convert(latinChars, 31, result1);
assertEqual (result1, utf8Text);
assertEqual (errors, 0);
assertEqual (result1.size(), 62);
assertEqual((long) result1.size(), 62);
}
@ -301,15 +301,15 @@ void TextConverterTest::testCP1252toUTF8()
std::string result0;
int errors = converter.convert(latinText, result0);
assertEqual (result0, utf8Text);
assertEqual (errors, 0);
assertEqual (result0.size(), 43);
assertEqual(result0, utf8Text);
assertEqual(errors, 0);
assertEqual((long) result0.size(), 43);
std::string result1;
errors = converter.convert(latinChars, 25, result1);
assertEqual (result1, utf8Text);
assertEqual (errors, 0);
assertEqual (result1.size(), 43);
assertEqual(result1, utf8Text);
assertEqual(errors, 0);
assertEqual((long) result1.size(), 43);
}

View File

@ -19,6 +19,7 @@
#include "Poco/Foundation.h"
#include "CppUnit/TestCase.h"
#include "Poco/UnicodeConverter.h"
#include "Poco/UTFString.h"
#include <cstring>
@ -46,11 +47,16 @@ private:
// Convert from UTF-8 to wide
T wtext, wtext2, wtext3;
Poco::UnicodeConverter::convert(text, wtext);
if (sizeof(T) == 2)
assert(Poco::utfStringLength(wtext.data()) == 8);
else if (sizeof(T) == 4)
assert(Poco::utfStringLength(wtext.data()) == 5);
Poco::UnicodeConverter::convert((const char*) supp, strlen((const char*) supp), wtext2);
Poco::UnicodeConverter::convert((const char*) supp, wtext3);
Poco::UnicodeConverter::convert((const char*)supp, wtext3);
assert(wtext == wtext2);
assert(wtext == wtext3);
std::string text2, text3, text4;
assert (text != text2);
assert (text != text3);
assert (text != text4);

View File

@ -1115,7 +1115,7 @@ void VarTest::testString()
Poco::UInt64 s9;
float s10;
double s11;
bool s12;
bool s12 = false;
char s13;
a1.convert(s1);
a1.convert(s2);

View File

@ -1,6 +1,83 @@
POCO C++ Libraries Release Notes
AAAIntroduction
!!!Release 1.5.3
!!Summary of Changes
- fixed GH# 316: Poco::DateTimeFormatter::append() gives wrong result for
Poco::LocalDateTime
- Poco::Data::MySQL: added SQLite thread cleanup handler
- Poco::Net::X509Certificate: improved and fixed domain name verification for
wildcard domains
- added Poco::Clock class, which uses a system-provided monotonic clock
(if available) and is thus not affected by system realtime clock changes.
Monotonic Clock is available on Windows, Linux, OS X and on POSIX platforms
supporting clock_gettime() and CLOCK_MONOTONIC.
- Poco::Timer, Poco::Stopwatch, Poco::TimedNotificationQueue and Poco::Util::Timer
have been changed to use Poco::Clock instead of Poco::Timestamp and are now
unaffected by system realtime clock changes.
- fixed GH# 350: Memory leak in Data/ODBC with BLOB
- Correctly set MySQL time_type for Poco::Data::Date.
- fixed GH #352: Removed redundant #includes and fixed spelling mistakes.
- fixed setting of MYSQL_BIND is_unsigned value.
- fixed GH #360: CMakeLists foundation: add Clock.cpp in the list of source files
- Add extern "C" around <net/if.h> on HPUX platform.
- added runtests.sh
- fixed CPPUNIT_IGNORE parsing
- fixed Glob from start path, for platforms not alowing transverse from root (Android)
- added NTPClient (Rangel Reale)
- added PowerShell build script
- added SmartOS build support
- fix warnings in headers
- XMLWriter: removed unnecessary apostrophe escaping (&apos)
- MongoDB: use Int32 for messageLength
- fixed GH #380: SecureSocket+DialogSocket crashes with SIGSEGV when timeout occours
- Improve RSADigestEngine, using Poco::Crypto::DigestEngine to calculate hash before signing
- added Poco::PBKDF2Engine
- Fixed GH #380: SecureSocket+DialogSocket crashes with SIGSEGV when timeout occours
- added support for a 'Priority' attribute on cookies.
- GH #386: fixed bug in MailMessage without content-transfer-encoding header
- GH #384: ew hash algorithms support for RSADigestEngine
- fixed Clock overflow bug on Windows
- Poco::ByteOrder now uses intrinsics, if available
- CMake: added /bigobj option for msvc
- Fix typo to restore Net/TestSuite_x64_vs120 build
- correct path for CONFIGURE_FILE in CMakeLists.txt
- Building Poco 1.5.2 for Synology RS812+ (Intel Atom) (honor POCO_NO_INOTIFY)
- added WEC2013 support to buildwin.cmd and buildwin.ps1
- HTMLForm: in URL encoding, percent-encode more characters
- Fixed #include <linux/if.h> conflict with other libraries
- Poco::Net::X509Certificate::verify() no longer uses DNS reverse lookups to validate host names
- cert hostname validation is case insensitive and stricter for wildcard certificates
- TCPServer: do not reduce the capacity of the default ThreadPool
- added POCO_LOG_DEBUG flag
- Zip: fixed a crash caused by an I/O error
- added runtest script for windows
- added SQlite Full Text Search support
- added Thread::trySleep() and Thread::wakeUp()
- fixed GH #410: Bug in JSON::Object.stringify() in 1.5.2
- fixed GH #362: Defect in Var::parseString when there is no space between value and newline
- fixed GH #314: JSON parsing bug
- added GH #313: MetaColumn additions for Data::ODBC and Data::SQLite
- fixed GH #346: Make Poco::Data::Date and Poco::Data::Time compare functions const.
- fixed GH #341: Compiling poco-1.5.2 for Cygwin
- fixed GH #305: There are bugs in Buffer.h
- fixed GH #321: trivial build fixes (BB QNX build)
- fixed GH #440: MongoDB ObjectId string formatting
- added SevenZip library (Guenter Obiltschnig)
- fixed GH #442: Use correct prefix length field of Windows IP_ADAPTER_PREFIX structure
- improved GH #328: NetworkInterface on Windows XP
- fixed GH #154 Add support for MYSQL_TYPE_NEWDECIMAL to Poco::Data::MySQL
- fixed GH #290: Unicode support
!!Incompatible Changes and Possible Transition Issues
- Data::ODBC: UTF-16 Unicode is now directly mapped and recognized as type by ODBC.
This may cause behavior different from previosu versions, especially with Any and
Dynamic::Var bindings.
!!!Release 1.5.2
!!Summary of Changes