mirror of
https://github.com/pocoproject/poco.git
synced 2025-01-18 00:15:27 +01:00
GH #290: Unicode support
This commit is contained in:
parent
8b39a87fd6
commit
1aa28e1491
@ -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)
|
||||
==========================
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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]);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -69,6 +69,7 @@ private:
|
||||
void recreateBoolTable();
|
||||
void recreateMiscTable();
|
||||
void recreateLogTable();
|
||||
void recreateUnicodeTable();
|
||||
|
||||
void configurePLPgSQL();
|
||||
/// Configures PL/pgSQL in the database. A reasonable defaults
|
||||
|
@ -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;
|
||||
|
@ -74,6 +74,7 @@ private:
|
||||
void recreateBoolTable();
|
||||
void recreateMiscTable();
|
||||
void recreateLogTable();
|
||||
void recreateUnicodeTable();
|
||||
|
||||
static SessionPtr _pSession;
|
||||
static ExecPtr _pExecutor;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
@ -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:
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -44,7 +44,7 @@ public:
|
||||
/// Destroys the AbstractPreparation.
|
||||
|
||||
virtual void prepare() = 0;
|
||||
/// Preparations data.
|
||||
/// Prepares data.
|
||||
|
||||
protected:
|
||||
AbstractPreparation();
|
||||
|
@ -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;
|
||||
|
@ -46,6 +46,7 @@ public:
|
||||
FDT_FLOAT,
|
||||
FDT_DOUBLE,
|
||||
FDT_STRING,
|
||||
FDT_WSTRING,
|
||||
FDT_BLOB,
|
||||
FDT_CLOB,
|
||||
FDT_DATE,
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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.");
|
||||
|
@ -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.");
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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&)
|
||||
{
|
||||
}
|
||||
|
@ -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.
|
||||
};
|
||||
|
||||
|
||||
|
@ -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" />
|
||||
|
@ -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">
|
||||
|
@ -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*
|
||||
{
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user