#3318: Data: Support Poco::UUID for data binding

This commit is contained in:
Günter Obiltschnig 2021-06-19 08:40:49 +02:00
parent a95c591e0a
commit 7569ccf82b
56 changed files with 785 additions and 212 deletions

View File

@ -19,7 +19,6 @@
#include "Poco/ActiveRecord/ActiveRecordLib.h" #include "Poco/ActiveRecord/ActiveRecordLib.h"
#include "Poco/ActiveRecord/Context.h" #include "Poco/ActiveRecord/Context.h"
#include "Poco/ActiveRecord/IDTraits.h" #include "Poco/ActiveRecord/IDTraits.h"
#include "Poco/ActiveRecord/TypeHandler.h"
#include "Poco/DateTime.h" #include "Poco/DateTime.h"
#include "Poco/RefCountedObject.h" #include "Poco/RefCountedObject.h"
#include "Poco/AutoPtr.h" #include "Poco/AutoPtr.h"

View File

@ -1,70 +0,0 @@
//
// TypeHandler.h
//
// Library: ActiveRecord
// Package: ActiveRecord
// Module: TypeHandler
//
// Copyright (c) 2020, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef ActiveRecord_TypeHandler_INCLUDED
#define ActiveRecord_TypeHandler_INCLUDED
#include "Poco/Data/TypeHandler.h"
#include "Poco/ThreadLocal.h"
#include "Poco/UUID.h"
#include <map>
namespace Poco {
namespace Data {
template <>
class TypeHandler<Poco::UUID>
{
public:
using UUIDMap = std::map<std::size_t, std::string>;
static std::size_t size()
{
return 1;
}
static void bind(std::size_t pos, const Poco::UUID& uuid, AbstractBinder::Ptr pBinder, AbstractBinder::Direction dir)
{
static Poco::ThreadLocal<UUIDMap> uuidMap;
std::string& uuidString = (*uuidMap)[pos];
uuidString = uuid.toString();
TypeHandler<std::string>::bind(pos++, uuidString, pBinder, dir);
}
static void extract(std::size_t pos, Poco::UUID& uuid, const Poco::UUID& deflt, AbstractExtractor::Ptr pExtr)
{
std::string defltString = deflt.toString();
std::string uuidString;
TypeHandler<std::string>::extract(pos++, uuidString, defltString, pExtr);
uuid.parse(uuidString);
}
static void prepare(std::size_t pos, const Poco::UUID& uuid, AbstractPreparator::Ptr pPrep)
{
static Poco::ThreadLocal<UUIDMap> uuidMap;
std::string& uuidString = (*uuidMap)[pos];
uuidString = uuid.toString();
TypeHandler<std::string>::prepare(pos++, uuidString, pPrep);
}
};
} } // namespace Poco::Data
#endif // ActiveRecord_TypeHandler_INCLUDED

View File

@ -104,6 +104,9 @@ public:
virtual void bind(std::size_t pos, const Time& val, Direction dir); virtual void bind(std::size_t pos, const Time& val, Direction dir);
/// Binds a Time. /// Binds a Time.
virtual void bind(std::size_t pos, const UUID& val, Direction dir);
/// Binds a UUID.
virtual void bind(std::size_t pos, const NullData& val, Direction dir); virtual void bind(std::size_t pos, const NullData& val, Direction dir);
/// Binds a null. /// Binds a null.

View File

@ -110,6 +110,9 @@ public:
virtual bool extract(std::size_t pos, Time& val); virtual bool extract(std::size_t pos, Time& val);
/// Extracts a Time. Returns false if null was received. /// Extracts a Time. Returns false if null was received.
virtual bool extract(std::size_t pos, UUID& val);
/// Extracts a UUID. Returns false if null was received.
virtual bool extract(std::size_t pos, Any& val); virtual bool extract(std::size_t pos, Any& val);
/// Extracts an Any. Returns false if null was received. /// Extracts an Any. Returns false if null was received.

View File

@ -211,6 +211,13 @@ void Binder::bind(std::size_t pos, const Time& val, Direction dir)
} }
void Binder::bind(std::size_t pos, const UUID& val, Direction dir)
{
std::string str = val.toString();
bind(pos, str, dir);
}
void Binder::bind(std::size_t pos, const NullData&, Direction dir) void Binder::bind(std::size_t pos, const NullData&, Direction dir)
{ {
poco_assert(dir == PD_IN); poco_assert(dir == PD_IN);

View File

@ -204,6 +204,18 @@ bool Extractor::extract(std::size_t pos, Time& val)
} }
bool Extractor::extract(std::size_t pos, UUID& val)
{
std::string str;
if (extract(pos, str))
{
val.parse(str);
return true;
}
else return false;
}
bool Extractor::extract(std::size_t pos, Any& val) bool Extractor::extract(std::size_t pos, Any& val)
{ {
return false; return false;

View File

@ -496,6 +496,15 @@ void MySQLTest::testDouble()
} }
void MySQLTest::testUUID()
{
if (!_pSession) fail ("Test not available.");
recreateUUIDsTable();
_pExecutor->uuids();
}
void MySQLTest::testTuple() void MySQLTest::testTuple()
{ {
if (!_pSession) fail ("Test not available."); if (!_pSession) fail ("Test not available.");
@ -779,6 +788,15 @@ void MySQLTest::recreateFloatsTable()
} }
void MySQLTest::recreateUUIDsTable()
{
dropTable("Strings");
try { *_pSession << "CREATE TABLE Strings (str CHAR(36))", now; }
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail ("recreateUUIDsTable()"); }
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail ("recreateUUIDsTable()"); }
}
void MySQLTest::recreateTuplesTable() void MySQLTest::recreateTuplesTable()
{ {
dropTable("Tuples"); dropTable("Tuples");
@ -904,6 +922,7 @@ CppUnit::Test* MySQLTest::suite()
CppUnit_addTest(pSuite, MySQLTest, testUnsignedInts); CppUnit_addTest(pSuite, MySQLTest, testUnsignedInts);
CppUnit_addTest(pSuite, MySQLTest, testFloat); CppUnit_addTest(pSuite, MySQLTest, testFloat);
CppUnit_addTest(pSuite, MySQLTest, testDouble); CppUnit_addTest(pSuite, MySQLTest, testDouble);
CppUnit_addTest(pSuite, MySQLTest, testUUID);
CppUnit_addTest(pSuite, MySQLTest, testTuple); CppUnit_addTest(pSuite, MySQLTest, testTuple);
CppUnit_addTest(pSuite, MySQLTest, testTupleVector); CppUnit_addTest(pSuite, MySQLTest, testTupleVector);
CppUnit_addTest(pSuite, MySQLTest, testInternalExtraction); CppUnit_addTest(pSuite, MySQLTest, testInternalExtraction);

View File

@ -24,7 +24,7 @@
class MySQLTest: public CppUnit::TestCase class MySQLTest: public CppUnit::TestCase
/// MySQL test class /// MySQL test class
/// Tested: /// Tested:
/// ///
/// Driver | DB | OS /// Driver | DB | OS
/// ----------------+---------------------------+------------------------------------------ /// ----------------+---------------------------+------------------------------------------
/// 03.51.12.00 | MySQL 5.0.27-community-nt | MS Windows XP Professional x64 v.2003/SP1 /// 03.51.12.00 | MySQL 5.0.27-community-nt | MS Windows XP Professional x64 v.2003/SP1
@ -84,6 +84,8 @@ public:
void testFloat(); void testFloat();
void testDouble(); void testDouble();
void testUUID();
void testTuple(); void testTuple();
void testTupleVector(); void testTupleVector();
@ -119,6 +121,7 @@ private:
void recreateIntsTable(); void recreateIntsTable();
void recreateUnsignedIntsTable(); void recreateUnsignedIntsTable();
void recreateFloatsTable(); void recreateFloatsTable();
void recreateUUIDsTable();
void recreateTuplesTable(); void recreateTuplesTable();
void recreateVectorsTable(); void recreateVectorsTable();
void recreateNullableIntTable(); void recreateNullableIntTable();

View File

@ -545,6 +545,29 @@ void SQLExecutor::doubles()
} }
void SQLExecutor::uuids()
{
std::string funct = "uuids()";
Poco::UUID data("da8b9c4d-faa0-44e1-b834-ece1e7d31cd5");
Poco::UUID ret;
try { *_pSession << "INSERT INTO Strings VALUES (?)", use(data), now; }
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); }
int count = 0;
try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; }
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); }
assertTrue (count == 1);
try { *_pSession << "SELECT str FROM Strings", into(ret), now; }
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); }
assertTrue (ret == data);
}
void SQLExecutor::insertSingleBulkVec() void SQLExecutor::insertSingleBulkVec()
{ {
std::string funct = "insertSingleBulkVec()"; std::string funct = "insertSingleBulkVec()";

View File

@ -26,7 +26,7 @@ public:
PB_IMMEDIATE, PB_IMMEDIATE,
PB_AT_EXEC PB_AT_EXEC
}; };
enum DataExtraction enum DataExtraction
{ {
DE_MANUAL, DE_MANUAL,
@ -37,7 +37,7 @@ public:
~SQLExecutor(); ~SQLExecutor();
void bareboneMySQLTest(const char* host, const char* user, const char* pwd, const char* db, int port, const char* tableCreateString); void bareboneMySQLTest(const char* host, const char* user, const char* pwd, const char* db, int port, const char* tableCreateString);
/// This function uses "bare bone" MySQL API calls (i.e. calls are not /// This function uses "bare bone" MySQL API calls (i.e. calls are not
/// "wrapped" in PocoData framework structures). /// "wrapped" in PocoData framework structures).
/// The purpose of the function is to verify that driver behaves /// The purpose of the function is to verify that driver behaves
/// correctly. If this test passes, subsequent tests failures are likely ours. /// correctly. If this test passes, subsequent tests failures are likely ours.
@ -86,6 +86,7 @@ public:
void unsignedInts(); void unsignedInts();
void floats(); void floats();
void doubles(); void doubles();
void uuids();
void tuples(); void tuples();
void tupleVector(); void tupleVector();

View File

@ -322,6 +322,9 @@ public:
void bind(std::size_t pos, const std::list<DateTime>& val, Direction dir); void bind(std::size_t pos, const std::list<DateTime>& val, Direction dir);
/// Binds a DateTime list. /// Binds a DateTime list.
void bind(std::size_t pos, const UUID& val, Direction dir);
/// Binds a UUID.
void bind(std::size_t pos, const NullData& val, Direction dir); void bind(std::size_t pos, const NullData& val, Direction dir);
/// Binds a null. In-bound only. /// Binds a null. In-bound only.
@ -367,6 +370,7 @@ private:
typedef std::vector<AnyVec> AnyVecVec; typedef std::vector<AnyVec> AnyVecVec;
typedef std::map<char*, std::string*> StringMap; typedef std::map<char*, std::string*> StringMap;
typedef std::map<UTF16String::value_type*, UTF16String*> UTF16StringMap; typedef std::map<UTF16String::value_type*, UTF16String*> UTF16StringMap;
typedef std::map<char*, UUID*> UUIDMap;
typedef std::map<SQL_DATE_STRUCT*, Date*> DateMap; typedef std::map<SQL_DATE_STRUCT*, Date*> DateMap;
typedef std::map<SQL_TIME_STRUCT*, Time*> TimeMap; typedef std::map<SQL_TIME_STRUCT*, Time*> TimeMap;
typedef std::map<SQL_TIMESTAMP_STRUCT*, DateTime*> TimestampMap; typedef std::map<SQL_TIMESTAMP_STRUCT*, DateTime*> TimestampMap;
@ -998,6 +1002,7 @@ private:
TimestampMap _timestamps; TimestampMap _timestamps;
StringMap _strings; StringMap _strings;
UTF16StringMap _utf16Strings; UTF16StringMap _utf16Strings;
UUIDMap _uuids;
DateVecVec _dateVecVec; DateVecVec _dateVecVec;
TimeVecVec _timeVecVec; TimeVecVec _timeVecVec;

View File

@ -305,6 +305,9 @@ public:
bool extract(std::size_t pos, std::list<Poco::DateTime>& val); bool extract(std::size_t pos, std::list<Poco::DateTime>& val);
/// Extracts a DateTime list. /// Extracts a DateTime list.
bool extract(std::size_t pos, Poco::UUID& val);
/// Extracts a UUID.
bool extract(std::size_t pos, Poco::Any& val); bool extract(std::size_t pos, Poco::Any& val);
/// Extracts an Any. /// Extracts an Any.
@ -567,6 +570,9 @@ private:
case MetaColumn::FDT_TIMESTAMP: case MetaColumn::FDT_TIMESTAMP:
{ return extAny<T, Poco::DateTime>(pos, val); } { return extAny<T, Poco::DateTime>(pos, val); }
case MetaColumn::FDT_UUID:
{ return extAny<T, Poco::UUID>(pos, val); }
default: default:
throw DataFormatException("Unsupported data type."); throw DataFormatException("Unsupported data type.");
} }

View File

@ -353,6 +353,9 @@ public:
void prepare(std::size_t pos, const std::list<Poco::DateTime>& val); void prepare(std::size_t pos, const std::list<Poco::DateTime>& val);
/// Prepares a DateTime list. /// Prepares a DateTime list.
void prepare(std::size_t pos, const Poco::UUID& val);
/// Prepares a UUID.
void prepare(std::size_t pos, const Poco::Any& val); void prepare(std::size_t pos, const Poco::Any& val);
/// Prepares an Any. /// Prepares an Any.
@ -548,6 +551,12 @@ private:
else else
return prepareFixedSize<DateTime>(pos, SQL_C_TYPE_TIMESTAMP); return prepareFixedSize<DateTime>(pos, SQL_C_TYPE_TIMESTAMP);
case MetaColumn::FDT_UUID:
if (pVal)
return prepareFixedSize<DateTime>(pos, SQL_C_BINARY, 16);
else
return prepareFixedSize<DateTime>(pos, SQL_C_BINARY);
default: default:
throw DataFormatException("Unsupported data type."); throw DataFormatException("Unsupported data type.");
} }
@ -1173,6 +1182,12 @@ inline void Preparator::prepare(std::size_t pos, const std::list<Poco::DateTime>
} }
inline void Preparator::prepare(std::size_t pos, const Poco::UUID&)
{
prepareCharArray<char, DT_CHAR_ARRAY>(pos, SQL_C_BINARY, 16, 16);
}
inline void Preparator::prepare(std::size_t pos, const Poco::Any& val) inline void Preparator::prepare(std::size_t pos, const Poco::Any& val)
{ {
prepareImpl<std::vector<Poco::Any> >(pos); prepareImpl<std::vector<Poco::Any> >(pos);

View File

@ -80,6 +80,10 @@ void Binder::freeMemory()
UTF16CharPtrVec::iterator endUTF16Chr = _utf16CharPtrs.end(); UTF16CharPtrVec::iterator endUTF16Chr = _utf16CharPtrs.end();
for (; itUTF16Chr != endUTF16Chr; ++itUTF16Chr) std::free(*itUTF16Chr); for (; itUTF16Chr != endUTF16Chr; ++itUTF16Chr) std::free(*itUTF16Chr);
UUIDMap::iterator itUUID = _uuids.begin();
UUIDMap::iterator itUUIDEnd = _uuids.end();
for(; itUUID != itUUIDEnd; ++itUUID) std::free(itUUID->first);
BoolPtrVec::iterator itBool = _boolPtrs.begin(); BoolPtrVec::iterator itBool = _boolPtrs.begin();
BoolPtrVec::iterator endBool = _boolPtrs.end(); BoolPtrVec::iterator endBool = _boolPtrs.end();
for (; itBool != endBool; ++itBool) delete [] *itBool; for (; itBool != endBool; ++itBool) delete [] *itBool;
@ -129,15 +133,15 @@ void Binder::bind(std::size_t pos, const std::string& val, Direction dir)
_lengthIndicator.push_back(pLenIn); _lengthIndicator.push_back(pLenIn);
if (Utility::isError(SQLBindParameter(_rStmt, if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1, (SQLUSMALLINT) pos + 1,
toODBCDirection(dir), toODBCDirection(dir),
SQL_C_CHAR, SQL_C_CHAR,
Connector::stringBoundToLongVarChar() ? SQL_LONGVARCHAR : SQL_VARCHAR, Connector::stringBoundToLongVarChar() ? SQL_LONGVARCHAR : SQL_VARCHAR,
(SQLUINTEGER) colSize, (SQLUINTEGER) colSize,
0, 0,
pVal, pVal,
(SQLINTEGER) size, (SQLINTEGER) size,
_lengthIndicator.back()))) _lengthIndicator.back())))
{ {
throw StatementException(_rStmt, "SQLBindParameter(std::string)"); throw StatementException(_rStmt, "SQLBindParameter(std::string)");
@ -206,22 +210,22 @@ void Binder::bind(std::size_t pos, const Date& val, Direction dir)
SQL_DATE_STRUCT* pDS = new SQL_DATE_STRUCT; SQL_DATE_STRUCT* pDS = new SQL_DATE_STRUCT;
Utility::dateSync(*pDS, val); Utility::dateSync(*pDS, val);
_dates.insert(DateMap::value_type(pDS, const_cast<Date*>(&val))); _dates.insert(DateMap::value_type(pDS, const_cast<Date*>(&val)));
SQLINTEGER colSize = 0; SQLINTEGER colSize = 0;
SQLSMALLINT decDigits = 0; SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, SQL_TYPE_DATE, colSize, decDigits); getColSizeAndPrecision(pos, SQL_TYPE_DATE, colSize, decDigits);
if (Utility::isError(SQLBindParameter(_rStmt, if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1, (SQLUSMALLINT) pos + 1,
toODBCDirection(dir), toODBCDirection(dir),
SQL_C_TYPE_DATE, SQL_C_TYPE_DATE,
SQL_TYPE_DATE, SQL_TYPE_DATE,
colSize, colSize,
decDigits, decDigits,
(SQLPOINTER) pDS, (SQLPOINTER) pDS,
0, 0,
_lengthIndicator.back()))) _lengthIndicator.back())))
{ {
throw StatementException(_rStmt, "SQLBindParameter(Date)"); throw StatementException(_rStmt, "SQLBindParameter(Date)");
@ -239,22 +243,22 @@ void Binder::bind(std::size_t pos, const Time& val, Direction dir)
SQL_TIME_STRUCT* pTS = new SQL_TIME_STRUCT; SQL_TIME_STRUCT* pTS = new SQL_TIME_STRUCT;
Utility::timeSync(*pTS, val); Utility::timeSync(*pTS, val);
_times.insert(TimeMap::value_type(pTS, const_cast<Time*>(&val))); _times.insert(TimeMap::value_type(pTS, const_cast<Time*>(&val)));
SQLINTEGER colSize = 0; SQLINTEGER colSize = 0;
SQLSMALLINT decDigits = 0; SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, SQL_TYPE_TIME, colSize, decDigits); getColSizeAndPrecision(pos, SQL_TYPE_TIME, colSize, decDigits);
if (Utility::isError(SQLBindParameter(_rStmt, if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1, (SQLUSMALLINT) pos + 1,
toODBCDirection(dir), toODBCDirection(dir),
SQL_C_TYPE_TIME, SQL_C_TYPE_TIME,
SQL_TYPE_TIME, SQL_TYPE_TIME,
colSize, colSize,
decDigits, decDigits,
(SQLPOINTER) pTS, (SQLPOINTER) pTS,
0, 0,
_lengthIndicator.back()))) _lengthIndicator.back())))
{ {
throw StatementException(_rStmt, "SQLBindParameter(Time)"); throw StatementException(_rStmt, "SQLBindParameter(Time)");
@ -279,15 +283,15 @@ void Binder::bind(std::size_t pos, const Poco::DateTime& val, Direction dir)
SQLSMALLINT decDigits = 0; SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, SQL_TYPE_TIMESTAMP, colSize, decDigits); getColSizeAndPrecision(pos, SQL_TYPE_TIMESTAMP, colSize, decDigits);
if (Utility::isError(SQLBindParameter(_rStmt, if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1, (SQLUSMALLINT) pos + 1,
toODBCDirection(dir), toODBCDirection(dir),
SQL_C_TYPE_TIMESTAMP, SQL_C_TYPE_TIMESTAMP,
SQL_TYPE_TIMESTAMP, SQL_TYPE_TIMESTAMP,
colSize, colSize,
decDigits, decDigits,
(SQLPOINTER) pTS, (SQLPOINTER) pTS,
0, 0,
_lengthIndicator.back()))) _lengthIndicator.back())))
{ {
throw StatementException(_rStmt, "SQLBindParameter(DateTime)"); throw StatementException(_rStmt, "SQLBindParameter(DateTime)");
@ -295,6 +299,38 @@ void Binder::bind(std::size_t pos, const Poco::DateTime& val, Direction dir)
} }
void Binder::bind(std::size_t pos, const UUID& val, Direction dir)
{
SQLINTEGER size = (SQLINTEGER) 16;
SQLLEN* pLenIn = new SQLLEN;
*pLenIn = size;
_lengthIndicator.push_back(pLenIn);
char* pUUID = new char[16];
val.copyTo(pUUID);
_uuids.insert(UUIDMap::value_type(pUUID, const_cast<UUID*>(&val)));
SQLINTEGER colSize = 0;
SQLSMALLINT decDigits = 0;
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_BINARY,
SQL_GUID,
colSize,
decDigits,
(SQLPOINTER) pUUID,
0,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter(UUID)");
}
}
void Binder::bind(std::size_t pos, const NullData& val, Direction dir) void Binder::bind(std::size_t pos, const NullData& val, Direction dir)
{ {
if (isOutBound(dir) || !isInBound(dir)) if (isOutBound(dir) || !isInBound(dir))
@ -311,15 +347,15 @@ void Binder::bind(std::size_t pos, const NullData& val, Direction dir)
SQLSMALLINT decDigits = 0; SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, SQL_C_STINYINT, colSize, decDigits); getColSizeAndPrecision(pos, SQL_C_STINYINT, colSize, decDigits);
if (Utility::isError(SQLBindParameter(_rStmt, if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1, (SQLUSMALLINT) pos + 1,
SQL_PARAM_INPUT, SQL_PARAM_INPUT,
SQL_C_STINYINT, SQL_C_STINYINT,
Utility::sqlDataType(SQL_C_STINYINT), Utility::sqlDataType(SQL_C_STINYINT),
colSize, colSize,
decDigits, decDigits,
0, 0,
0, 0,
_lengthIndicator.back()))) _lengthIndicator.back())))
{ {
throw StatementException(_rStmt, "SQLBindParameter()"); throw StatementException(_rStmt, "SQLBindParameter()");
@ -334,7 +370,7 @@ std::size_t Binder::parameterSize(SQLPOINTER pAddr) const
it = _outParams.find(pAddr); it = _outParams.find(pAddr);
if (it != _outParams.end()) return it->second; if (it != _outParams.end()) return it->second;
throw NotFoundException("Requested data size not found."); throw NotFoundException("Requested data size not found.");
} }
@ -350,7 +386,7 @@ SQLSMALLINT Binder::toODBCDirection(Direction dir) const
bool in = isInBound(dir); bool in = isInBound(dir);
bool out = isOutBound(dir); bool out = isOutBound(dir);
SQLSMALLINT ioType = SQL_PARAM_TYPE_UNKNOWN; SQLSMALLINT ioType = SQL_PARAM_TYPE_UNKNOWN;
if (in && out) ioType = SQL_PARAM_INPUT_OUTPUT; if (in && out) ioType = SQL_PARAM_INPUT_OUTPUT;
else if(in) ioType = SQL_PARAM_INPUT; else if(in) ioType = SQL_PARAM_INPUT;
else if(out) ioType = SQL_PARAM_OUTPUT; else if(out) ioType = SQL_PARAM_OUTPUT;
else throw Poco::IllegalStateException("Binder not bound (must be [in] OR [out])."); else throw Poco::IllegalStateException("Binder not bound (must be [in] OR [out]).");
@ -365,7 +401,7 @@ void Binder::synchronize()
{ {
DateMap::iterator it = _dates.begin(); DateMap::iterator it = _dates.begin();
DateMap::iterator end = _dates.end(); DateMap::iterator end = _dates.end();
for(; it != end; ++it) for(; it != end; ++it)
Utility::dateSync(*it->second, *it->first); Utility::dateSync(*it->second, *it->first);
} }
@ -373,7 +409,7 @@ void Binder::synchronize()
{ {
TimeMap::iterator it = _times.begin(); TimeMap::iterator it = _times.begin();
TimeMap::iterator end = _times.end(); TimeMap::iterator end = _times.end();
for(; it != end; ++it) for(; it != end; ++it)
Utility::timeSync(*it->second, *it->first); Utility::timeSync(*it->second, *it->first);
} }
@ -381,7 +417,7 @@ void Binder::synchronize()
{ {
TimestampMap::iterator it = _timestamps.begin(); TimestampMap::iterator it = _timestamps.begin();
TimestampMap::iterator end = _timestamps.end(); TimestampMap::iterator end = _timestamps.end();
for(; it != end; ++it) for(; it != end; ++it)
Utility::dateTimeSync(*it->second, *it->first); Utility::dateTimeSync(*it->second, *it->first);
} }
@ -392,6 +428,14 @@ void Binder::synchronize()
for(; it != end; ++it) for(; it != end; ++it)
it->second->assign(it->first, std::strlen(it->first)); it->second->assign(it->first, std::strlen(it->first));
} }
if (_uuids.size())
{
UUIDMap::iterator it = _uuids.begin();
UUIDMap::iterator end = _uuids.end();
for(; it != end; ++it)
it->second->copyFrom(it->first);
}
} }
@ -405,6 +449,7 @@ void Binder::reset()
_times.clear(); _times.clear();
_timestamps.clear(); _timestamps.clear();
_strings.clear(); _strings.clear();
_uuids.clear();
_dateVecVec.clear(); _dateVecVec.clear();
_timeVecVec.clear(); _timeVecVec.clear();
_dateTimeVecVec.clear(); _dateTimeVecVec.clear();
@ -415,9 +460,9 @@ void Binder::reset()
} }
void Binder::getColSizeAndPrecision(std::size_t pos, void Binder::getColSizeAndPrecision(std::size_t pos,
SQLSMALLINT cDataType, SQLSMALLINT cDataType,
SQLINTEGER& colSize, SQLINTEGER& colSize,
SQLSMALLINT& decDigits, SQLSMALLINT& decDigits,
std::size_t actualSize) std::size_t actualSize)
{ {
@ -448,9 +493,9 @@ void Binder::getColSizeAndPrecision(std::size_t pos,
colSize = (SQLINTEGER) p.columnSize(); colSize = (SQLINTEGER) p.columnSize();
decDigits = (SQLSMALLINT) p.decimalDigits(); decDigits = (SQLSMALLINT) p.decimalDigits();
return; return;
} }
catch (StatementException&) catch (StatementException&)
{ {
} }
try try
@ -459,9 +504,9 @@ void Binder::getColSizeAndPrecision(std::size_t pos,
colSize = (SQLINTEGER) c.length(); colSize = (SQLINTEGER) c.length();
decDigits = (SQLSMALLINT) c.precision(); decDigits = (SQLSMALLINT) c.precision();
return; return;
} }
catch (StatementException&) catch (StatementException&)
{ {
} }
// last check, just in case // last check, just in case
@ -505,7 +550,7 @@ void Binder::getColumnOrParameterSize(std::size_t pos, SQLINTEGER& size)
if (!Utility::isError(SQLGetStmtAttr(_rStmt, SQL_ATTR_IMP_PARAM_DESC, &hIPD, SQL_IS_POINTER, 0))) if (!Utility::isError(SQLGetStmtAttr(_rStmt, SQL_ATTR_IMP_PARAM_DESC, &hIPD, SQL_IS_POINTER, 0)))
{ {
SQLUINTEGER sz = 0; SQLUINTEGER sz = 0;
if (!Utility::isError(SQLGetDescField(hIPD, (SQLSMALLINT) pos + 1, SQL_DESC_LENGTH, &sz, SQL_IS_UINTEGER, 0)) && if (!Utility::isError(SQLGetDescField(hIPD, (SQLSMALLINT) pos + 1, SQL_DESC_LENGTH, &sz, SQL_IS_UINTEGER, 0)) &&
sz > 0) sz > 0)
{ {
size = sz; size = sz;
@ -529,7 +574,7 @@ void Binder::setParamSetSize(std::size_t length)
{ {
if (0 == _paramSetSize) if (0 == _paramSetSize)
{ {
if (Utility::isError(Poco::Data::ODBC::SQLSetStmtAttr(_rStmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, SQL_IS_UINTEGER)) || if (Utility::isError(Poco::Data::ODBC::SQLSetStmtAttr(_rStmt, SQL_ATTR_PARAM_BIND_TYPE, SQL_PARAM_BIND_BY_COLUMN, SQL_IS_UINTEGER)) ||
Utility::isError(Poco::Data::ODBC::SQLSetStmtAttr(_rStmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) length, SQL_IS_UINTEGER))) Utility::isError(Poco::Data::ODBC::SQLSetStmtAttr(_rStmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER) length, SQL_IS_UINTEGER)))
throw StatementException(_rStmt, "SQLSetStmtAttr()"); throw StatementException(_rStmt, "SQLSetStmtAttr()");

View File

@ -224,6 +224,20 @@ bool Extractor::extractBoundImplContainer<std::list<Poco::DateTime> >(std::size_
} }
template<>
bool Extractor::extractBoundImpl<Poco::UUID>(std::size_t pos, Poco::UUID& val)
{
if (isNull(pos)) return false;
std::size_t dataSize = _pPreparator->actualDataSize(pos);
checkDataSize(dataSize);
char* pBuffer = *AnyCast<char*>(&_pPreparator->at(pos));
val.copyFrom(pBuffer);
return true;
}
template<> template<>
bool Extractor::extractBoundImplContainer<std::vector<bool> >(std::size_t pos, bool Extractor::extractBoundImplContainer<std::vector<bool> >(std::size_t pos,
std::vector<bool>& val) std::vector<bool>& val)
@ -504,6 +518,33 @@ bool Extractor::extractManualImpl<Poco::DateTime>(std::size_t pos,
} }
template<>
bool Extractor::extractManualImpl<Poco::UUID>(std::size_t pos,
Poco::UUID& val,
SQLSMALLINT cType)
{
char buffer[16];
resizeLengths(pos);
SQLRETURN rc = SQLGetData(_rStmt,
(SQLUSMALLINT) pos + 1,
cType, //C data type
&buffer, //returned value
sizeof(buffer), //buffer length
&_lengths[pos]); //length indicator
if (Utility::isError(rc))
throw StatementException(_rStmt, "SQLGetData()");
if (isNullLengthIndicator(_lengths[pos]))
return false;
else
val.copyFrom(buffer);
return true;
}
bool Extractor::extract(std::size_t pos, Poco::Int32& val) bool Extractor::extract(std::size_t pos, Poco::Int32& val)
{ {
if (Preparator::DE_MANUAL == _dataExtraction) if (Preparator::DE_MANUAL == _dataExtraction)
@ -911,6 +952,15 @@ bool Extractor::extract(std::size_t pos, std::list<Poco::DateTime>& val)
} }
bool Extractor::extract(std::size_t pos, Poco::UUID& val)
{
if (Preparator::DE_MANUAL == _dataExtraction)
return extractManualImpl(pos, val, SQL_C_BINARY);
else
return extractBoundImpl(pos, val);
}
bool Extractor::extract(std::size_t pos, Poco::Int8& val) bool Extractor::extract(std::size_t pos, Poco::Int8& val)
{ {
if (Preparator::DE_MANUAL == _dataExtraction) if (Preparator::DE_MANUAL == _dataExtraction)

View File

@ -102,9 +102,6 @@ void ODBCMetaColumn::init()
case SQL_CHAR: case SQL_CHAR:
case SQL_VARCHAR: case SQL_VARCHAR:
case SQL_LONGVARCHAR: case SQL_LONGVARCHAR:
#ifdef SQL_GUID
case SQL_GUID:
#endif
setType(MetaColumn::FDT_STRING); break; setType(MetaColumn::FDT_STRING); break;
case SQL_WCHAR: case SQL_WCHAR:
@ -168,6 +165,9 @@ void ODBCMetaColumn::init()
case SQL_TYPE_TIMESTAMP: case SQL_TYPE_TIMESTAMP:
setType(MetaColumn::FDT_TIMESTAMP); break; setType(MetaColumn::FDT_TIMESTAMP); break;
case SQL_GUID:
setType(MetaColumn::FDT_UUID); break;
default: default:
throw DataFormatException("Unsupported data type."); throw DataFormatException("Unsupported data type.");
} }

View File

@ -37,6 +37,9 @@ endif
ifeq (0, $(shell test -e /opt/postgresql/lib$(LIB64SUFFIX); echo $$?)) ifeq (0, $(shell test -e /opt/postgresql/lib$(LIB64SUFFIX); echo $$?))
SYSLIBS += -L/opt/postgresql/lib$(LIB64SUFFIX) SYSLIBS += -L/opt/postgresql/lib$(LIB64SUFFIX)
endif endif
ifeq (0, $(shell test -e /usr/local/opt/libpq/lib; echo $$?))
SYSLIBS += -L/usr/local/opt/libpq/lib$(LIB64SUFFIX)
endif
SYSLIBS += -lpq SYSLIBS += -lpq
objects = Extractor Binder SessionImpl Connector \ objects = Extractor Binder SessionImpl Connector \

View File

@ -108,6 +108,9 @@ public:
virtual void bind(std::size_t pos, const Time& val, Direction dir = PD_IN); virtual void bind(std::size_t pos, const Time& val, Direction dir = PD_IN);
/// Binds a Time. /// Binds a Time.
virtual void bind(std::size_t pos, const UUID& val, Direction dir = PD_IN);
/// Binds a UUID.
virtual void bind(std::size_t pos, const NullData& val, Direction dir = PD_IN); virtual void bind(std::size_t pos, const NullData& val, Direction dir = PD_IN);
/// Binds a null. /// Binds a null.

View File

@ -109,6 +109,9 @@ public:
virtual bool extract(std::size_t pos, Time& val); virtual bool extract(std::size_t pos, Time& val);
/// Extracts a Time. Returns false if null was received. /// Extracts a Time. Returns false if null was received.
virtual bool extract(std::size_t pos, UUID& val);
/// Extracts a UUID. Returns false if null was received.
virtual bool extract(std::size_t pos, Any& val); virtual bool extract(std::size_t pos, Any& val);
/// Extracts an Any. Returns false if null was received. /// Extracts an Any. Returns false if null was received.

View File

@ -271,6 +271,7 @@ inline const void* InputParameter::pInternalRepresentation() const
case Poco::Data::MetaColumn::FDT_DATE: case Poco::Data::MetaColumn::FDT_DATE:
case Poco::Data::MetaColumn::FDT_TIME: case Poco::Data::MetaColumn::FDT_TIME:
case Poco::Data::MetaColumn::FDT_TIMESTAMP: case Poco::Data::MetaColumn::FDT_TIMESTAMP:
case Poco::Data::MetaColumn::FDT_UUID:
return _stringVersionRepresentation.c_str(); return _stringVersionRepresentation.c_str();
case Poco::Data::MetaColumn::FDT_BLOB: case Poco::Data::MetaColumn::FDT_BLOB:

View File

@ -176,6 +176,13 @@ void Binder::bind(std::size_t pos, const Time& val, Direction dir)
} }
void Binder::bind(std::size_t pos, const UUID& val, Direction dir)
{
poco_assert(dir == PD_IN);
realBind(pos, Poco::Data::MetaColumn::FDT_UUID, &val, sizeof(UUID));
}
void Binder::bind(std::size_t pos, const NullData&, Direction dir) void Binder::bind(std::size_t pos, const NullData&, Direction dir)
{ {
poco_assert(dir == PD_IN); poco_assert(dir == PD_IN);
@ -293,6 +300,14 @@ void Binder::updateBindVectorToCurrentValues()
const Poco::Data::CLOB& clob = * static_cast<const Poco::Data::CLOB*>(itr->pData()); const Poco::Data::CLOB& clob = * static_cast<const Poco::Data::CLOB*>(itr->pData());
itr->setNonStringVersionRepresentation(static_cast<const void*> (clob.rawContent()), clob.size()); itr->setNonStringVersionRepresentation(static_cast<const void*> (clob.rawContent()), clob.size());
} }
break;
case Poco::Data::MetaColumn::FDT_UUID:
{
const Poco::UUID& uuid = * static_cast<const Poco::UUID*>(itr->pData());
itr->setStringVersionRepresentation(uuid.toString());
}
break;
case Poco::Data::MetaColumn::FDT_UNKNOWN: case Poco::Data::MetaColumn::FDT_UNKNOWN:
default: default:

View File

@ -432,6 +432,19 @@ bool Extractor::extract(std::size_t pos, Time& val)
} }
bool Extractor::extract(std::size_t pos, UUID& val)
{
OutputParameter outputParameter = extractPreamble(pos);
if (isColumnNull(outputParameter))
{
return false;
}
return val.tryParse(outputParameter.pData());
}
bool Extractor::extract(std::size_t pos, Any& val) bool Extractor::extract(std::size_t pos, Any& val)
{ {
return extractStringImpl(pos, val); return extractStringImpl(pos, val);
@ -583,6 +596,14 @@ bool Extractor::extractToDynamic(std::size_t pos, Dynamic::Var& val)
val = dt; val = dt;
break; break;
} }
case UUIDOID:
{
UUID uuid;
success = extract(pos, uuid);
if (success)
val = uuid;
break;
}
} }
return success; return success;

View File

@ -94,6 +94,11 @@ Poco::Data::MetaColumn::ColumnDataType oidToColumnDataType(const Oid anOID)
cdt = Poco::Data::MetaColumn::FDT_TIMESTAMP; cdt = Poco::Data::MetaColumn::FDT_TIMESTAMP;
break; break;
//uuid
case UUIDOID:
cdt = Poco::Data::MetaColumn::FDT_BLOB;
break;
// everything else is a string // everything else is a string
default: default:
cdt = Poco::Data::MetaColumn::FDT_STRING; cdt = Poco::Data::MetaColumn::FDT_STRING;

View File

@ -37,6 +37,9 @@ endif
ifeq (0, $(shell test -e /opt/postgresql/lib$(LIB64SUFFIX); echo $$?)) ifeq (0, $(shell test -e /opt/postgresql/lib$(LIB64SUFFIX); echo $$?))
SYSLIBS += -L/opt/postgresql/lib$(LIB64SUFFIX) SYSLIBS += -L/opt/postgresql/lib$(LIB64SUFFIX)
endif endif
ifeq (0, $(shell test -e /usr/local/opt/libpq/lib; echo $$?))
SYSLIBS += -L/usr/local/opt/libpq/lib$(LIB64SUFFIX)
endif
# Note: linking order is important, do not change it. # Note: linking order is important, do not change it.
SYSLIBS += -lpq -lz -lpthread -ldl SYSLIBS += -lpq -lz -lpthread -ldl

View File

@ -690,6 +690,14 @@ void PostgreSQLTest::testDouble()
} }
void PostgreSQLTest::testUUID()
{
if (!_pSession) fail ("Test not available.");
recreateUUIDsTable();
_pExecutor->uuids();
}
void PostgreSQLTest::testTuple() void PostgreSQLTest::testTuple()
{ {
if (!_pSession) fail ("Test not available."); if (!_pSession) fail ("Test not available.");
@ -986,6 +994,15 @@ void PostgreSQLTest::recreateFloatsTable()
} }
void PostgreSQLTest::recreateUUIDsTable()
{
dropTable("Strings");
try { *_pSession << "CREATE TABLE Strings (str UUID)", now; }
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail ("recreateUUIDsTable()"); }
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail ("recreateUUIDsTable()"); }
}
void PostgreSQLTest::recreateTuplesTable() void PostgreSQLTest::recreateTuplesTable()
{ {
dropTable("Tuples"); dropTable("Tuples");
@ -1110,6 +1127,7 @@ CppUnit::Test* PostgreSQLTest::suite()
CppUnit_addTest(pSuite, PostgreSQLTest, testUnsignedInts); CppUnit_addTest(pSuite, PostgreSQLTest, testUnsignedInts);
CppUnit_addTest(pSuite, PostgreSQLTest, testFloat); CppUnit_addTest(pSuite, PostgreSQLTest, testFloat);
CppUnit_addTest(pSuite, PostgreSQLTest, testDouble); CppUnit_addTest(pSuite, PostgreSQLTest, testDouble);
CppUnit_addTest(pSuite, PostgreSQLTest, testUUID);
CppUnit_addTest(pSuite, PostgreSQLTest, testTuple); CppUnit_addTest(pSuite, PostgreSQLTest, testTuple);
CppUnit_addTest(pSuite, PostgreSQLTest, testTupleVector); CppUnit_addTest(pSuite, PostgreSQLTest, testTupleVector);
CppUnit_addTest(pSuite, PostgreSQLTest, testInternalExtraction); CppUnit_addTest(pSuite, PostgreSQLTest, testInternalExtraction);

View File

@ -82,6 +82,7 @@ public:
void testUnsignedInts(); void testUnsignedInts();
void testFloat(); void testFloat();
void testDouble(); void testDouble();
void testUUID();
void testTuple(); void testTuple();
void testTupleVector(); void testTupleVector();
@ -118,6 +119,7 @@ private:
void recreateIntsTable(); void recreateIntsTable();
void recreateUnsignedIntsTable(); void recreateUnsignedIntsTable();
void recreateFloatsTable(); void recreateFloatsTable();
void recreateUUIDsTable();
void recreateTuplesTable(); void recreateTuplesTable();
void recreateVectorsTable(); void recreateVectorsTable();
void recreateNullableIntTable(); void recreateNullableIntTable();

View File

@ -712,6 +712,29 @@ void SQLExecutor::floats()
} }
void SQLExecutor::uuids()
{
std::string funct = "uuids()";
Poco::UUID data("264a1c6f-7af5-43a5-a593-377aff3d2d7d");
Poco::UUID ret;
try { *_pSession << "INSERT INTO Strings VALUES ($1)", use(data), now; }
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); }
int count = 0;
try { *_pSession << "SELECT COUNT(*) FROM Strings", into(count), now; }
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); }
assertTrue (count == 1);
try { *_pSession << "SELECT str FROM Strings", into(ret), now; }
catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); }
catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); }
assertTrue (ret == data);
}
void SQLExecutor::doubles() void SQLExecutor::doubles()
{ {
std::string funct = "floats()"; std::string funct = "floats()";
@ -1611,7 +1634,7 @@ void SQLExecutor::blobStmt()
std::string lastName("lastname"); std::string lastName("lastname");
std::string firstName("firstname"); std::string firstName("firstname");
std::string address("Address"); std::string address("Address");
unsigned char BLOBData[ 10 ] = { 0,1,2,3,4,5,6,7,14,15 }; unsigned char BLOBData[ 10 ] = { 0,1,2,3,4,5,6,7,14,15 };
Poco::Data::BLOB blob(BLOBData, 10); Poco::Data::BLOB blob(BLOBData, 10);
int count = 0; int count = 0;

View File

@ -25,7 +25,7 @@ public:
PB_IMMEDIATE, PB_IMMEDIATE,
PB_AT_EXEC PB_AT_EXEC
}; };
enum DataExtraction enum DataExtraction
{ {
DE_MANUAL, DE_MANUAL,
@ -37,7 +37,7 @@ public:
void oidPostgreSQLTest(std::string host, std::string user, std::string pwd, std::string db, std::string port, const char* tableCreateString, const Oid anOIDArray[]); void oidPostgreSQLTest(std::string host, std::string user, std::string pwd, std::string db, std::string port, const char* tableCreateString, const Oid anOIDArray[]);
/// This function verifies the PostgreSQL Column type OIDs are consistent between releases /// This function verifies the PostgreSQL Column type OIDs are consistent between releases
void barebonePostgreSQLTest(std::string host, std::string user, std::string pwd, std::string db, std::string port, const char* tableCreateString); void barebonePostgreSQLTest(std::string host, std::string user, std::string pwd, std::string db, std::string port, const char* tableCreateString);
/// This function uses "bare bone" API calls (i.e. calls are not /// This function uses "bare bone" API calls (i.e. calls are not
/// "wrapped" in PocoSQL framework structures). /// "wrapped" in PocoSQL framework structures).
@ -89,6 +89,7 @@ public:
void unsignedInts(); void unsignedInts();
void floats(); void floats();
void doubles(); void doubles();
void uuids();
void tuples(); void tuples();
void tupleVector(); void tupleVector();

View File

@ -106,6 +106,9 @@ public:
void bind(std::size_t pos, const DateTime& val, Direction dir); void bind(std::size_t pos, const DateTime& val, Direction dir);
/// Binds a DateTime. /// Binds a DateTime.
void bind(std::size_t pos, const UUID& val, Direction dir);
/// Binds a UUID.
void bind(std::size_t pos, const NullData& val, Direction dir); void bind(std::size_t pos, const NullData& val, Direction dir);
/// Binds a null. /// Binds a null.

View File

@ -116,6 +116,9 @@ public:
bool extract(std::size_t pos, Poco::DateTime& val); bool extract(std::size_t pos, Poco::DateTime& val);
/// Extracts a DateTime. /// Extracts a DateTime.
bool extract(std::size_t pos, Poco::UUID& val);
/// Extracts a Time.
bool extract(std::size_t pos, Poco::Any& val); bool extract(std::size_t pos, Poco::Any& val);
/// Extracts an Any. /// Extracts an Any.
@ -264,6 +267,13 @@ private:
val = dt; val = dt;
break; break;
} }
case MetaColumn::FDT_UUID:
{
UUID uuid;
ret = extract(pos, uuid);
val = uuid;
break;
}
default: default:
throw Poco::Data::UnknownTypeException("Unknown type during extraction"); throw Poco::Data::UnknownTypeException("Unknown type during extraction");
} }

View File

@ -111,6 +111,13 @@ void Binder::bind(std::size_t pos, const DateTime& val, Direction dir)
} }
void Binder::bind(std::size_t pos, const UUID& val, Direction dir)
{
std::string str(val.toString());
bind(pos, str, dir);
}
void Binder::bind(std::size_t pos, const NullData&, Direction) void Binder::bind(std::size_t pos, const NullData&, Direction)
{ {
sqlite3_bind_null(_pStmt, static_cast<int>(pos)); sqlite3_bind_null(_pStmt, static_cast<int>(pos));

View File

@ -208,6 +208,16 @@ bool Extractor::extract(std::size_t pos, DateTime& val)
} }
bool Extractor::extract(std::size_t pos, UUID& val)
{
if (isNull(pos)) return false;
std::string str;
extract(pos, str);
val.parse(str);
return true;
}
bool Extractor::extract(std::size_t pos, Poco::Any& val) bool Extractor::extract(std::size_t pos, Poco::Any& val)
{ {
return extractImpl(pos, val); return extractImpl(pos, val);

View File

@ -134,6 +134,8 @@ Utility::Utility()
_types.insert(TypeMap::value_type("TIME", MetaColumn::FDT_TIME)); _types.insert(TypeMap::value_type("TIME", MetaColumn::FDT_TIME));
_types.insert(TypeMap::value_type("DATETIME", MetaColumn::FDT_TIMESTAMP)); _types.insert(TypeMap::value_type("DATETIME", MetaColumn::FDT_TIMESTAMP));
_types.insert(TypeMap::value_type("TIMESTAMP", MetaColumn::FDT_TIMESTAMP)); _types.insert(TypeMap::value_type("TIMESTAMP", MetaColumn::FDT_TIMESTAMP));
_types.insert(TypeMap::value_type("UUID", MetaColumn::FDT_UUID));
_types.insert(TypeMap::value_type("GUID", MetaColumn::FDT_UUID));
} }
} }

View File

@ -30,6 +30,7 @@
#include "Poco/Data/SQLite/SQLiteException.h" #include "Poco/Data/SQLite/SQLiteException.h"
#include "Poco/Tuple.h" #include "Poco/Tuple.h"
#include "Poco/Any.h" #include "Poco/Any.h"
#include "Poco/UUIDGenerator.h"
#include "Poco/SharedPtr.h" #include "Poco/SharedPtr.h"
#include "Poco/DynamicAny.h" #include "Poco/DynamicAny.h"
#include "Poco/DateTime.h" #include "Poco/DateTime.h"
@ -1916,6 +1917,21 @@ void SQLiteTest::testDateTime()
} }
void SQLiteTest::testUUID()
{
Session tmp (Poco::Data::SQLite::Connector::KEY, "dummy.db");
tmp << "DROP TABLE IF EXISTS Ids", now;
tmp << "CREATE TABLE Ids (id0 UUID)", now;
Poco::UUID uuid = Poco::UUIDGenerator::defaultGenerator().createRandom();
tmp << "INSERT INTO Ids VALUES (?)", use(uuid), now;
Poco::UUID ruuid;
tmp << "SELECT * FROM Ids", into(ruuid), now;
assertTrue (ruuid == uuid);
}
void SQLiteTest::testInternalExtraction() void SQLiteTest::testInternalExtraction()
{ {
Session tmp (Poco::Data::SQLite::Connector::KEY, "dummy.db"); Session tmp (Poco::Data::SQLite::Connector::KEY, "dummy.db");
@ -3453,6 +3469,7 @@ CppUnit::Test* SQLiteTest::suite()
CppUnit_addTest(pSuite, SQLiteTest, testTuple1); CppUnit_addTest(pSuite, SQLiteTest, testTuple1);
CppUnit_addTest(pSuite, SQLiteTest, testTupleVector1); CppUnit_addTest(pSuite, SQLiteTest, testTupleVector1);
CppUnit_addTest(pSuite, SQLiteTest, testDateTime); CppUnit_addTest(pSuite, SQLiteTest, testDateTime);
CppUnit_addTest(pSuite, SQLiteTest, testUUID);
CppUnit_addTest(pSuite, SQLiteTest, testInternalExtraction); CppUnit_addTest(pSuite, SQLiteTest, testInternalExtraction);
CppUnit_addTest(pSuite, SQLiteTest, testPrimaryKeyConstraint); CppUnit_addTest(pSuite, SQLiteTest, testPrimaryKeyConstraint);
CppUnit_addTest(pSuite, SQLiteTest, testNullable); CppUnit_addTest(pSuite, SQLiteTest, testNullable);

View File

@ -100,6 +100,8 @@ public:
void testDateTime(); void testDateTime();
void testUUID();
void testInternalExtraction(); void testInternalExtraction();
void testPrimaryKeyConstraint(); void testPrimaryKeyConstraint();
void testNullable(); void testNullable();

View File

@ -16,54 +16,54 @@ The following complete example shows how to use POCO Data:
#include "Poco/Data/SQLite/Connector.h" #include "Poco/Data/SQLite/Connector.h"
#include <vector> #include <vector>
#include <iostream> #include <iostream>
using namespace Poco::Data::Keywords; using namespace Poco::Data::Keywords;
using Poco::Data::Session; using Poco::Data::Session;
using Poco::Data::Statement; using Poco::Data::Statement;
struct Person struct Person
{ {
std::string name; std::string name;
std::string address; std::string address;
int age; int age;
}; };
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
// register SQLite connector // register SQLite connector
Poco::Data::SQLite::Connector::registerConnector(); Poco::Data::SQLite::Connector::registerConnector();
// create a session // create a session
Session session("SQLite", "sample.db"); Session session("SQLite", "sample.db");
// drop sample table, if it exists // drop sample table, if it exists
session << "DROP TABLE IF EXISTS Person", now; session << "DROP TABLE IF EXISTS Person", now;
// (re)create table // (re)create table
session << "CREATE TABLE Person (Name VARCHAR(30), Address VARCHAR, Age INTEGER(3))", now; session << "CREATE TABLE Person (Name VARCHAR(30), Address VARCHAR, Age INTEGER(3))", now;
// insert some rows // insert some rows
Person person = Person person =
{ {
"Bart Simpson", "Bart Simpson",
"Springfield", "Springfield",
12 12
}; };
Statement insert(session); Statement insert(session);
insert << "INSERT INTO Person VALUES(?, ?, ?)", insert << "INSERT INTO Person VALUES(?, ?, ?)",
use(person.name), use(person.name),
use(person.address), use(person.address),
use(person.age); use(person.age);
insert.execute(); insert.execute();
person.name = "Lisa Simpson"; person.name = "Lisa Simpson";
person.address = "Springfield"; person.address = "Springfield";
person.age = 10; person.age = 10;
insert.execute(); insert.execute();
// a simple query // a simple query
Statement select(session); Statement select(session);
select << "SELECT Name, Address, Age FROM Person", select << "SELECT Name, Address, Age FROM Person",
@ -71,24 +71,24 @@ The following complete example shows how to use POCO Data:
into(person.address), into(person.address),
into(person.age), into(person.age),
range(0, 1); // iterate over result set one row at a time range(0, 1); // iterate over result set one row at a time
while (!select.done()) while (!select.done())
{ {
select.execute(); select.execute();
std::cout << person.name << " " << person.address << " " << person.age << std::endl; std::cout << person.name << " " << person.address << " " << person.age << std::endl;
} }
return 0; return 0;
} }
---- ----
The above example is pretty much self explanatory. The above example is pretty much self explanatory.
The <[using namespace Poco::Data ]> is for convenience only but highly The <[using namespace Poco::Data ]> is for convenience only but highly
recommended for good readable code. While <[ses << "SELECT COUNT(*) recommended for good readable code. While <[ses << "SELECT COUNT(*)
FROM PERSON", Poco::Data::Keywords::into(count), Poco::Data::Keywords::now;]> FROM PERSON", Poco::Data::Keywords::into(count), Poco::Data::Keywords::now;]>
is valid, the aesthetic aspect of the code is improved by eliminating the need is valid, the aesthetic aspect of the code is improved by eliminating the need
for full namespace qualification; this document uses convention introduced in for full namespace qualification; this document uses convention introduced in
the example above. the example above.
The remainder of this tutorial is split up into the following parts: The remainder of this tutorial is split up into the following parts:
@ -117,12 +117,12 @@ parameter contains the connection string.
In the case of SQLite, the path of the database file is sufficient as connection string. In the case of SQLite, the path of the database file is sufficient as connection string.
For ODBC, the connection string may be a simple "DSN=MyDSNName" when a DSN is configured or For ODBC, the connection string may be a simple "DSN=MyDSNName" when a DSN is configured or
a complete ODBC driver-specific connection string defining all the necessary connection parameters a complete ODBC driver-specific connection string defining all the necessary connection parameters
(for details, consult your ODBC driver documentation). (for details, consult your ODBC driver documentation).
For MySQL, the connection string is a semicolon-delimited list of name-value pairs For MySQL, the connection string is a semicolon-delimited list of name-value pairs
specifying various parameters like host, port, user, password, database, compression and specifying various parameters like host, port, user, password, database, compression and
automatic reconnect. Example: <["host=localhost;port=3306;db=mydb;user=alice;password=s3cr3t;compress=true;auto-reconnect=true"]> automatic reconnect. Example: <["host=localhost;port=3306;db=mydb;user=alice;password=s3cr3t;compress=true;auto-reconnect=true"]>
@ -130,7 +130,7 @@ automatic reconnect. Example: <["host=localhost;port=3306;db=mydb;user=alice;pas
!!Single Data Sets !!Single Data Sets
Inserting data works by <[using]> the content of other variables. Inserting data works by <[using]> the content of other variables.
Assume we have a table that stores only forenames: Assume we have a table that stores only forenames:
ForeName (Name VARCHAR(30)) ForeName (Name VARCHAR(30))
@ -159,8 +159,8 @@ Rewriting the above code now simply gives:
---- ----
In this example the <[use]> expression matches the placeholder with the In this example the <[use]> expression matches the placeholder with the
<[Peter]> value. Note that apart from the nicer syntax, the real benefits of <[Peter]> value. Note that apart from the nicer syntax, the real benefits of
placeholders -- which are performance and protection against SQL injection placeholders -- which are performance and protection against SQL injection
attacks -- don't show here. Check the <[Statements]> section to find out more. attacks -- don't show here. Check the <[Statements]> section to find out more.
Retrieving data from the Database works similar. The <[into]> Retrieving data from the Database works similar. The <[into]>
@ -172,14 +172,14 @@ database:
ses << "SELECT NAME FROM FORENAME", into(aName), now; ses << "SELECT NAME FROM FORENAME", into(aName), now;
ses << "SELECT NAME FROM FORENAME", into(aName, 0, "default"), now; ses << "SELECT NAME FROM FORENAME", into(aName, 0, "default"), now;
You'll note the integer zero argument in the second into() call. The reason for You'll note the integer zero argument in the second into() call. The reason for
that is that Poco::Data supports multiple result sets for those databases/drivers that is that Poco::Data supports multiple result sets for those databases/drivers
that have such capbility and we have to indicate the resultset we are referring to. that have such capbility and we have to indicate the resultset we are referring to.
Attempting to create sufficient overloads of <[into()]> creates more trouble than Attempting to create sufficient overloads of <[into()]> creates more trouble than
what it's worth and null values can effectively be dealt with through use of either what it's worth and null values can effectively be dealt with through use of either
Poco::Nullable wrapper (see Handling Null Entries later in this document) or Poco::Nullable wrapper (see Handling Null Entries later in this document) or
Poco::Dynamic::Var, which will be set as empty for null values when used as query Poco::Dynamic::Var, which will be set as empty for null values when used as query
output target. output target.
---- ----
It is also possible to combine into and use expressions: It is also possible to combine into and use expressions:
@ -224,18 +224,18 @@ To accomodate for NULL, use the Poco::Nullable template:
if (!lastName.isNull()) { ... } if (!lastName.isNull()) { ... }
---- ----
The above used Poco::Nullable is a lightweight template class, wrapping any type The above used Poco::Nullable is a lightweight template class, wrapping any type
for the purpose of allowing it to have null value. for the purpose of allowing it to have null value.
If the returned value was null, age.isNull() will return true. Whether empty If the returned value was null, age.isNull() will return true. Whether empty
string is null or not is more of a philosophical question (a topic for discussion string is null or not is more of a philosophical question (a topic for discussion
in some other time and place); for the purpose of this document, suffice it to say in some other time and place); for the purpose of this document, suffice it to say
that different databases handle it differently and Poco::Data provides a way to that different databases handle it differently and Poco::Data provides a way to
tweak it to user's needs through folowing <[Session]> features: tweak it to user's needs through folowing <[Session]> features:
*emptyStringIsNull *emptyStringIsNull
*forceEmptyString *forceEmptyString
So, if your database does not treat empty strings as null but you want Poco::Data So, if your database does not treat empty strings as null but you want Poco::Data
to emulate such behavior, modify the session like this: to emulate such behavior, modify the session like this:
@ -259,7 +259,7 @@ set it belongs to:
std::vector<Person> people; std::vector<Person> people;
Person pHomer, pLisa; Person pHomer, pLisa;
int aHomer(42), aLisa(10), aBart(0); int aHomer(42), aLisa(10), aBart(0);
session << "SELECT * FROM Person WHERE Age = ?; " session << "SELECT * FROM Person WHERE Age = ?; "
"SELECT Age FROM Person WHERE FirstName = 'Bart'; " "SELECT Age FROM Person WHERE FirstName = 'Bart'; "
"SELECT * FROM Person WHERE Age = ?", "SELECT * FROM Person WHERE Age = ?",
@ -293,7 +293,7 @@ More on statements and manipulators in the chapters that follow.
Most of the modern database systems support stored procedures and/or Most of the modern database systems support stored procedures and/or
functions. Does Poco::Data provide any support there? You bet. functions. Does Poco::Data provide any support there? You bet.
While the specifics on what exactly is possible (e.g. the data types While the specifics on what exactly is possible (e.g. the data types
passed in and out, automatic or manual data binding, binding direction, passed in and out, automatic or manual data binding, binding direction,
etc.) is ultimately database dependent, POCO Data does it's etc.) is ultimately database dependent, POCO Data does it's
best to provide reasonable access to such functionality through <[in]>, best to provide reasonable access to such functionality through <[in]>,
<[out]> and <[io]> binding functions. As their names imply, these <[out]> and <[io]> binding functions. As their names imply, these
@ -306,7 +306,7 @@ here's an Oracle ODBC example:
" temp NUMBER := param1; " " temp NUMBER := param1; "
" BEGIN param1 := param2; param2 := temp; RETURN(param1+param2); " " BEGIN param1 := param2; param2 := temp; RETURN(param1+param2); "
" END storedFunction;" , now; " END storedFunction;" , now;
int i = 1, j = 2, result = 0; int i = 1, j = 2, result = 0;
session << "{? = call storedFunction(?, ?)}", out(result), io(i), io(j), now; // i = 2, j = 1, result = 3 session << "{? = call storedFunction(?, ?)}", out(result), io(i), io(j), now; // i = 2, j = 1, result = 3
---- ----
@ -322,10 +322,10 @@ Stored procedures are allowed to return data sets (a.k.a. cursors):
" ret SYS_REFCURSOR; " " ret SYS_REFCURSOR; "
"BEGIN " "BEGIN "
" OPEN ret FOR " " OPEN ret FOR "
" SELECT * FROM Person WHERE Age < ageLimit; " " SELECT * FROM Person WHERE Age < ageLimit; "
" RETURN ret; " " RETURN ret; "
"END storedCursorFunction;" , now; "END storedCursorFunction;" , now;
session << "{call storedCursorFunction(?)}", in(age), into(people), now; session << "{call storedCursorFunction(?)}", in(age), into(people), now;
---- ----
@ -387,8 +387,8 @@ Here's how we control when to actually execute the statement:
---- ----
By calling <[execute]> we asserted that our query was executed and that By calling <[execute]> we asserted that our query was executed and that
the value was inserted. The check to <[stmt.done()]> simply guarantees that the the value was inserted. The check to <[stmt.done()]> simply guarantees that the
statement was fully completed. statement was fully completed.
@ -479,7 +479,7 @@ return value is because, for asynchronous statements, <[execute()]>
always returns zero. This makes sense, because it does not know the always returns zero. This makes sense, because it does not know the
number of returned rows (remember, asynchronous <[execute()]> call number of returned rows (remember, asynchronous <[execute()]> call
returns <[immediately]> and does not wait for the completion of the returns <[immediately]> and does not wait for the completion of the
execution). execution).
!A Word of Warning !A Word of Warning
@ -513,7 +513,7 @@ later during execution. Thus, one should never pass temporaries to <[use()]>:
---- ----
It is possible to use <[bind()]> instead of <[use()]>. The <[bind()]> call will always create a It is possible to use <[bind()]> instead of <[use()]>. The <[bind()]> call will always create a
copy of the supplied argument. Also, it is possible to execute a statement returning copy of the supplied argument. Also, it is possible to execute a statement returning
data without supplying the storage and have the statement itself store the returned data without supplying the storage and have the statement itself store the returned
data for later retrieval through <[RecordSet]>. For details, see <[RecordSet]> chapter. data for later retrieval through <[RecordSet]>. For details, see <[RecordSet]> chapter.
@ -538,7 +538,7 @@ well-known source of many security problems in C and C++ code, do not
worry. Poco::format() family of functions is <[safe]> (and, admittedly, worry. Poco::format() family of functions is <[safe]> (and, admittedly,
slower than printf). slower than printf).
For cases where this type of formatting is used with queries containing For cases where this type of formatting is used with queries containing
the percent sign, use double percent ("%%"): the percent sign, use double percent ("%%"):
Statement stmt = (ses << "SELECT * FROM %s WHERE Name LIKE 'Simp%%'", "Person"); Statement stmt = (ses << "SELECT * FROM %s WHERE Name LIKE 'Simp%%'", "Person");
@ -696,13 +696,13 @@ object until <[statement.done()]> returns true.
For the next example, we assume that our system knows about 101 forenames: For the next example, we assume that our system knows about 101 forenames:
std::vector<std::string> names; std::vector<std::string> names;
Statement stmt = (ses << "SELECT NAME FROM FORENAME", into(names), limit(50)); Statement stmt = (ses << "SELECT NAME FROM FORENAME", into(names), limit(50));
stmt.execute(); //names.size() == 50 stmt.execute(); //names.size() == 50
poco_assert (!stmt.done()); poco_assert (!stmt.done());
stmt.execute(); //names.size() == 100 stmt.execute(); //names.size() == 100
poco_assert (!stmt.done()); poco_assert (!stmt.done());
stmt.execute(); //names.size() == 101 stmt.execute(); //names.size() == 101
poco_assert (stmt.done()); poco_assert (stmt.done());
---- ----
We previously stated that if no data is returned this is valid too. Thus, executing the following statement on an We previously stated that if no data is returned this is valid too. Thus, executing the following statement on an
@ -750,14 +750,14 @@ off.
The <[bulk]> keyword allows to boost performance for the connectors that The <[bulk]> keyword allows to boost performance for the connectors that
support column-wise operation and arrays of values and/or parameters support column-wise operation and arrays of values and/or parameters
(e.g. ODBC). (e.g. ODBC).
Here's how to signal bulk insertion to the statement: Here's how to signal bulk insertion to the statement:
std::vector<int> ints(100, 1); std::vector<int> ints(100, 1);
session << "INSERT INTO Test VALUES (?)", use(ints, bulk), now; session << "INSERT INTO Test VALUES (?)", use(ints, bulk), now;
---- ----
The above code will execute a "one-shot" insertion into the target table. The above code will execute a "one-shot" insertion into the target table.
Selection in bulk mode looks like this: Selection in bulk mode looks like this:
@ -827,7 +827,7 @@ feature.
!!! RecordSets, Iterators and Rows !!! RecordSets, Iterators and Rows
In all the examples so far the programmer had to supply the storage for In all the examples so far the programmer had to supply the storage for
data to be inserted or retrieved from a database. data to be inserted or retrieved from a database.
It is usually desirable to avoid that and let the framework take care of It is usually desirable to avoid that and let the framework take care of
it, something like this: it, something like this:
@ -840,16 +840,16 @@ No worries -- that's what the RecordSet class does:
Statement select(session); // we need a Statement for later RecordSet creation Statement select(session); // we need a Statement for later RecordSet creation
select << "SELECT * FROM Person", now; select << "SELECT * FROM Person", now;
// create a RecordSet // create a RecordSet
RecordSet rs(select); RecordSet rs(select);
std::size_t cols = rs.columnCount(); std::size_t cols = rs.columnCount();
// print all column names // print all column names
for (std::size_t col = 0; col < cols; ++col) for (std::size_t col = 0; col < cols; ++col)
std::cout << rs.columnName(col) << std::endl; std::cout << rs.columnName(col) << std::endl;
// iterate over all rows and columns // iterate over all rows and columns
for (RecordSet::Iterator it = rs.begin(); it != rs.end(); ++it) for (RecordSet::Iterator it = rs.begin(); it != rs.end(); ++it)
std::cout << *it << " "; std::cout << *it << " ";
---- ----
@ -889,7 +889,7 @@ used for sorting purposes. However, the sort criteria can be modified at
runtime. For example, an additional field may be added to sort fields runtime. For example, an additional field may be added to sort fields
(think "... ORDER BY Name ASC, Age DESC"): (think "... ORDER BY Name ASC, Age DESC"):
row.addSortField("Field1"); // now Field0 and Field1 are used for sorting row.addSortField("Field1"); // now Field0 and Field1 are used for sorting
row.replaceSortField("Field0", "Field2");// now Field1 and Field2 are used for sorting row.replaceSortField("Field0", "Field2");// now Field1 and Field2 are used for sorting
---- ----
@ -914,7 +914,7 @@ Valid storage type manipulators are:
So, if neither data storage, nor storage type are explicitly specified, So, if neither data storage, nor storage type are explicitly specified,
the data will internally be kept in standard deques. This can be changed the data will internally be kept in standard deques. This can be changed
through use of storage type manipulators. through use of storage type manipulators.
!!!Complex Data Types !!!Complex Data Types
@ -930,19 +930,19 @@ Assume you have a class Person:
// default constructor+destr. // default constructor+destr.
// getter and setter methods for all members // getter and setter methods for all members
// ... // ...
bool operator <(const Person&amp; p) const bool operator <(const Person&amp; p) const
/// we need this for set and multiset support /// we need this for set and multiset support
{ {
return _socialSecNr < p._socialSecNr; return _socialSecNr < p._socialSecNr;
} }
Poco::UInt64 operator()() const Poco::UInt64 operator()() const
/// we need this operator to return the key for the map and multimap /// we need this operator to return the key for the map and multimap
{ {
return _socialSecNr; return _socialSecNr;
} }
private: private:
std::string _firstName; std::string _firstName;
std::string _lastName; std::string _lastName;
@ -953,12 +953,12 @@ Assume you have a class Person:
Ideally, one would like to use a Person as simple as one used a string. Ideally, one would like to use a Person as simple as one used a string.
All that is needed is a template specialization of the <[TypeHandler]> All that is needed is a template specialization of the <[TypeHandler]>
template. Note that template specializations must be declared in the template. Note that template specializations must be declared in the
<*same namespace*> as the original template, i.e. <[Poco::Data]>. <*same namespace*> as the original template, i.e. <[Poco::Data]>.
The template specialization must implement the following methods: The template specialization must implement the following methods:
namespace Poco { namespace Poco {
namespace Data { namespace Data {
template <> template <>
class TypeHandler<class Person> class TypeHandler<class Person>
{ {
@ -972,12 +972,12 @@ The template specialization must implement the following methods:
TypeHandler<std::string>::bind(pos++, obj.getLastName(), pBinder, dir); TypeHandler<std::string>::bind(pos++, obj.getLastName(), pBinder, dir);
TypeHandler<Poco::UInt64>::bind(pos++, obj.getSocialSecNr(), pBinder, dir); TypeHandler<Poco::UInt64>::bind(pos++, obj.getSocialSecNr(), pBinder, dir);
} }
static std::size_t size() static std::size_t size()
{ {
return 3; // we handle three columns of the Table! return 3; // we handle three columns of the Table!
} }
static void prepare(std::size_t pos, const Person&amp; obj, AbstractPreparator::Ptr pPrepare) static void prepare(std::size_t pos, const Person&amp; obj, AbstractPreparator::Ptr pPrepare)
{ {
poco_assert_dbg (!pPrepare.isNull()); poco_assert_dbg (!pPrepare.isNull());
@ -987,7 +987,7 @@ The template specialization must implement the following methods:
TypeHandler<std::string>::prepare(pos++, obj.getLastName(), pPrepare); TypeHandler<std::string>::prepare(pos++, obj.getLastName(), pPrepare);
TypeHandler<Poco::UInt64>::prepare(pos++, obj.getSocialSecNr(), pPrepare); TypeHandler<Poco::UInt64>::prepare(pos++, obj.getSocialSecNr(), pPrepare);
} }
static void extract(std::size_t pos, Person&amp; obj, const Person&amp; defVal, AbstractExtractor::Ptr pExt) static void extract(std::size_t pos, Person&amp; obj, const Person&amp; defVal, AbstractExtractor::Ptr pExt)
/// obj will contain the result, defVal contains values we should use when one column is NULL /// obj will contain the result, defVal contains values we should use when one column is NULL
{ {
@ -1002,14 +1002,14 @@ The template specialization must implement the following methods:
obj.setLastName(lastName); obj.setLastName(lastName);
obj.setSocialSecNr(socialSecNr); obj.setSocialSecNr(socialSecNr);
} }
private: private:
TypeHandler(); TypeHandler();
~TypeHandler(); ~TypeHandler();
TypeHandler(const TypeHandler&amp;); TypeHandler(const TypeHandler&amp;);
TypeHandler&amp; operator=(const TypeHandler&amp;); TypeHandler&amp; operator=(const TypeHandler&amp;);
}; };
} } // namespace Poco::Data } } // namespace Poco::Data
---- ----
@ -1022,8 +1022,9 @@ working with a string:
!!!Session Pooling !!!Session Pooling
Creating a connection to a database is often a time consuming Creating a connection to a database is often a time consuming
operation. Therefore it makes sense to save a session object for operation. Therefore it makes sense to save a session object for
later reuse once it is no longer needed. later reuse once it is no longer needed.
A Poco::Data::SessionPool manages a collection of sessions. A Poco::Data::SessionPool manages a collection of sessions.
@ -1046,7 +1047,7 @@ Pooled sessions are automatically returned to the pool when the
Session variable holding them is destroyed. Session variable holding them is destroyed.
One session pool, of course, holds sessions for one database One session pool, of course, holds sessions for one database
connection. For sessions to multiple databases, there is connection. For sessions to multiple databases, there is
SessionPoolContainer: SessionPoolContainer:
SessionPoolContainer spc; SessionPoolContainer spc;
@ -1060,15 +1061,15 @@ SessionPoolContainer:
This document provides an overview of the most important features This document provides an overview of the most important features
offered by the POCO Data framework. The framework also supports LOB offered by the POCO Data framework. The framework also supports LOB
(specialized to BLOB and CLOB) type as well as Poco::DateTime binding. (specialized to BLOB and CLOB) type as well as Poco::DateTime binding.
The usage of these data types is no different than any C++ type, so we The usage of these data types is no different than any C++ type, so we
did not go into details here. did not go into details here.
The great deal of <[RecordSet]> and <[Row]> runtime "magic" is achieved The great deal of <[RecordSet]> and <[Row]> runtime "magic" is achieved
through employment of Poco::Dynamic::Var, which is the POCO through employment of Poco::Dynamic::Var, which is the POCO
equivalent of dynamic language data type. Obviously, due to its nature, equivalent of dynamic language data type. Obviously, due to its nature,
there is a run time performance penalty associated with Poco::Dynamic::Var, there is a run time performance penalty associated with Poco::Dynamic::Var,
but the internal details are beyond the scope of this document. but the internal details are beyond the scope of this document.
POCO Data tries to provide a broad spectrum of functionality, POCO Data tries to provide a broad spectrum of functionality,
with configurable efficiency/convenience ratio, providing a solid with configurable efficiency/convenience ratio, providing a solid

View File

@ -24,6 +24,7 @@
#include "Poco/Data/LOB.h" #include "Poco/Data/LOB.h"
#include "Poco/DateTime.h" #include "Poco/DateTime.h"
#include "Poco/Nullable.h" #include "Poco/Nullable.h"
#include "Poco/UUID.h"
#include "Poco/Any.h" #include "Poco/Any.h"
#include "Poco/Dynamic/Var.h" #include "Poco/Dynamic/Var.h"
#include "Poco/UTFString.h" #include "Poco/UTFString.h"
@ -317,6 +318,18 @@ public:
virtual void bind(std::size_t pos, const std::list<Time>& val, Direction dir = PD_IN); virtual void bind(std::size_t pos, const std::list<Time>& val, Direction dir = PD_IN);
/// Binds a Time list. /// Binds a Time list.
virtual void bind(std::size_t pos, const UUID& val, Direction dir = PD_IN) = 0;
/// Binds a UUID.
virtual void bind(std::size_t pos, const std::vector<UUID>& val, Direction dir = PD_IN);
/// Binds a UUID vector.
virtual void bind(std::size_t pos, const std::deque<UUID>& val, Direction dir = PD_IN);
/// Binds a UUID deque.
virtual void bind(std::size_t pos, const std::list<UUID>& val, Direction dir = PD_IN);
/// Binds a UUID list.
virtual void bind(std::size_t pos, const NullData& val, Direction dir = PD_IN) = 0; virtual void bind(std::size_t pos, const NullData& val, Direction dir = PD_IN) = 0;
/// Binds a null. /// Binds a null.

View File

@ -21,6 +21,7 @@
#include "Poco/Data/Data.h" #include "Poco/Data/Data.h"
#include "Poco/Data/Constants.h" #include "Poco/Data/Constants.h"
#include "Poco/Data/LOB.h" #include "Poco/Data/LOB.h"
#include "Poco/UUID.h"
#include "Poco/UTFString.h" #include "Poco/UTFString.h"
#include <vector> #include <vector>
#include <deque> #include <deque>
@ -304,6 +305,18 @@ public:
virtual bool extract(std::size_t pos, std::list<Time>& val); virtual bool extract(std::size_t pos, std::list<Time>& val);
/// Extracts a Time list. /// Extracts a Time list.
virtual bool extract(std::size_t pos, UUID& val) = 0;
/// Extracts a UUID. Returns false if null was received.
virtual bool extract(std::size_t pos, std::vector<UUID>& val);
/// Extracts a UUID vector.
virtual bool extract(std::size_t pos, std::deque<UUID>& val);
/// Extracts a UUID deque.
virtual bool extract(std::size_t pos, std::list<UUID>& val);
/// Extracts a UUID list.
virtual bool extract(std::size_t pos, Any& val) = 0; virtual bool extract(std::size_t pos, Any& val) = 0;
/// Extracts an Any. Returns false if null was received. /// Extracts an Any. Returns false if null was received.

View File

@ -21,6 +21,7 @@
#include "Poco/Data/Data.h" #include "Poco/Data/Data.h"
#include "Poco/RefCountedObject.h" #include "Poco/RefCountedObject.h"
#include "Poco/Data/LOB.h" #include "Poco/Data/LOB.h"
#include "Poco/UUID.h"
#include "Poco/UTFString.h" #include "Poco/UTFString.h"
#include <vector> #include <vector>
#include <deque> #include <deque>
@ -310,6 +311,18 @@ public:
virtual void prepare(std::size_t pos, const std::list<Time>& val); virtual void prepare(std::size_t pos, const std::list<Time>& val);
/// Prepares a Time list. /// Prepares a Time list.
virtual void prepare(std::size_t pos, const UUID&) = 0;
/// Prepares a UUID.
virtual void prepare(std::size_t pos, const std::vector<UUID>& val);
/// Prepares a UUID vector.
virtual void prepare(std::size_t pos, const std::deque<UUID>& val);
/// Prepares a UUID deque.
virtual void prepare(std::size_t pos, const std::list<UUID>& val);
/// Prepares a UUID list.
virtual void prepare(std::size_t pos, const Any&) = 0; virtual void prepare(std::size_t pos, const Any&) = 0;
/// Prepares an Any. /// Prepares an Any.

View File

@ -50,6 +50,7 @@ public:
FDT_DATE, FDT_DATE,
FDT_TIME, FDT_TIME,
FDT_TIMESTAMP, FDT_TIMESTAMP,
FDT_UUID,
FDT_UNKNOWN FDT_UNKNOWN
}; };

View File

@ -404,6 +404,24 @@ void AbstractBinder::bind(std::size_t pos, const std::list<Time>& val, Direction
} }
void AbstractBinder::bind(std::size_t pos, const std::vector<UUID>& val, Direction dir)
{
throw NotImplementedException("std::vector binder must be implemented.");
}
void AbstractBinder::bind(std::size_t pos, const std::deque<UUID>& val, Direction dir)
{
throw NotImplementedException("std::deque binder must be implemented.");
}
void AbstractBinder::bind(std::size_t pos, const std::list<UUID>& val, Direction dir)
{
throw NotImplementedException("std::list binder must be implemented.");
}
void AbstractBinder::bind(std::size_t pos, const std::vector<NullData>& val, Direction dir) void AbstractBinder::bind(std::size_t pos, const std::vector<NullData>& val, Direction dir)
{ {
throw NotImplementedException("std::vector binder must be implemented."); throw NotImplementedException("std::vector binder must be implemented.");

View File

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

View File

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

View File

@ -159,6 +159,7 @@ Poco::Dynamic::Var RecordSet::value(std::size_t col, std::size_t row, bool useFi
case MetaColumn::FDT_DATE: return value<Date>(col, row, useFilter); case MetaColumn::FDT_DATE: return value<Date>(col, row, useFilter);
case MetaColumn::FDT_TIME: return value<Time>(col, row, useFilter); case MetaColumn::FDT_TIME: return value<Time>(col, row, useFilter);
case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(col, row); case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(col, row);
case MetaColumn::FDT_UUID: return value<UUID>(col, row);
default: default:
throw UnknownTypeException("Data type not supported."); throw UnknownTypeException("Data type not supported.");
} }
@ -192,6 +193,7 @@ Poco::Dynamic::Var RecordSet::value(const std::string& name, std::size_t row, bo
case MetaColumn::FDT_DATE: return value<Date>(name, row, useFilter); case MetaColumn::FDT_DATE: return value<Date>(name, row, useFilter);
case MetaColumn::FDT_TIME: return value<Time>(name, row, useFilter); case MetaColumn::FDT_TIME: return value<Time>(name, row, useFilter);
case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(name, row, useFilter); case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(name, row, useFilter);
case MetaColumn::FDT_UUID: return value<UUID>(name, row, useFilter);
default: default:
throw UnknownTypeException("Data type not supported."); throw UnknownTypeException("Data type not supported.");
} }

View File

@ -351,6 +351,8 @@ void StatementImpl::makeExtractors(std::size_t count)
addInternalExtract<Time>(mc); break; addInternalExtract<Time>(mc); break;
case MetaColumn::FDT_TIMESTAMP: case MetaColumn::FDT_TIMESTAMP:
addInternalExtract<DateTime>(mc); break; addInternalExtract<DateTime>(mc); break;
case MetaColumn::FDT_UUID:
addInternalExtract<UUID>(mc); break;
default: default:
throw Poco::InvalidArgumentException("Data type not supported."); throw Poco::InvalidArgumentException("Data type not supported.");
} }

View File

@ -140,6 +140,11 @@ void Binder::bind(std::size_t pos, const DateTime& val, Direction dir)
} }
void Binder::bind(std::size_t pos, const UUID& val, Direction dir)
{
}
void Binder::bind(std::size_t pos, const NullData& val, Direction dir) void Binder::bind(std::size_t pos, const NullData& val, Direction dir)
{ {
} }

View File

@ -100,8 +100,11 @@ public:
void bind(std::size_t pos, const DateTime& val, Direction dir); void bind(std::size_t pos, const DateTime& val, Direction dir);
/// Binds a DateTime. /// Binds a DateTime.
void bind(std::size_t pos, const UUID& val, Direction dir);
/// Binds a UUID.
void bind(std::size_t pos, const NullData& val, Direction dir); void bind(std::size_t pos, const NullData& val, Direction dir);
/// Binds a DateTime. /// Binds a NullData.
void reset(); void reset();
}; };

View File

@ -166,13 +166,18 @@ bool Extractor::extract(std::size_t pos, Poco::Data::Time& val)
} }
bool Extractor::extract(std::size_t pos, Poco::DateTime& val) bool Extractor::extract(std::size_t pos, Poco::DateTime& val)
{ {
return true; return true;
} }
bool Extractor::extract(std::size_t pos, Poco::UUID& val)
{
return true;
}
bool Extractor::extract(std::size_t pos, Poco::Any& val) bool Extractor::extract(std::size_t pos, Poco::Any& val)
{ {
return true; return true;

View File

@ -100,9 +100,12 @@ public:
bool extract(std::size_t pos, Time& val); bool extract(std::size_t pos, Time& val);
/// Extracts a Time. /// Extracts a Time.
bool extract(std::size_t pos, Poco::DateTime& val); bool extract(std::size_t pos, DateTime& val);
/// Extracts a DateTime. /// Extracts a DateTime.
bool extract(std::size_t pos, UUID& val);
/// Extracts a UUID.
bool isNull(std::size_t col, std::size_t row = -1); bool isNull(std::size_t col, std::size_t row = -1);
/// Returns true if the current row value at pos column is null. /// Returns true if the current row value at pos column is null.

View File

@ -135,6 +135,11 @@ void Preparator::prepare(std::size_t pos, const Poco::DateTime&)
} }
void Preparator::prepare(std::size_t pos, const Poco::UUID&)
{
}
void Preparator::prepare(std::size_t pos, const Poco::Any&) void Preparator::prepare(std::size_t pos, const Poco::Any&)
{ {
} }

View File

@ -98,6 +98,9 @@ public:
void prepare(std::size_t pos, const Poco::DateTime&); void prepare(std::size_t pos, const Poco::DateTime&);
/// Prepares a DateTime. /// Prepares a DateTime.
void prepare(std::size_t pos, const Poco::UUID&);
/// Prepares a UUID.
void prepare(std::size_t pos, const Poco::Any&); void prepare(std::size_t pos, const Poco::Any&);
/// Prepares an Any. /// Prepares an Any.

View File

@ -523,6 +523,9 @@ public:
bool isDateTime() const; bool isDateTime() const;
/// Returns true if stored value represents a date/time. /// Returns true if stored value represents a date/time.
bool isUUID() const;
/// Returns true if stored value is a Poco::UUID.
std::size_t size() const; std::size_t size() const;
/// Returns the size of this Var. /// Returns the size of this Var.
/// This function returns 0 when Var is empty, 1 for POD or the size (i.e. length) /// This function returns 0 when Var is empty, 1 for POD or the size (i.e. length)
@ -896,6 +899,13 @@ inline bool Var::isDateTime() const
} }
inline bool Var::isUUID() const
{
VarHolder* pHolder = content();
return pHolder ? pHolder->isUUID() : false;
}
inline std::size_t Var::size() const inline std::size_t Var::size() const
{ {
VarHolder* pHolder = content(); VarHolder* pHolder = content();

View File

@ -31,6 +31,7 @@
#include "Poco/UnicodeConverter.h" #include "Poco/UnicodeConverter.h"
#include "Poco/UTFString.h" #include "Poco/UTFString.h"
#include "Poco/UTF8String.h" #include "Poco/UTF8String.h"
#include "Poco/UUID.h"
#include "Poco/Any.h" #include "Poco/Any.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
#include <vector> #include <vector>
@ -178,6 +179,10 @@ public:
/// Throws BadCastException. Must be overridden in a type /// Throws BadCastException. Must be overridden in a type
/// specialization in order to support the conversion. /// specialization in order to support the conversion.
virtual void convert(UUID& val) const;
/// Throws BadCastException. Must be overridden in a type
/// specialization in order to support the conversion.
#ifndef POCO_INT64_IS_LONG #ifndef POCO_INT64_IS_LONG
void convert(long& val) const; void convert(long& val) const;
@ -277,6 +282,10 @@ public:
/// Returns false. Must be properly overridden in a type /// Returns false. Must be properly overridden in a type
/// specialization in order to support the diagnostic. /// specialization in order to support the diagnostic.
virtual bool isUUID() const;
/// Returns false. Must be properly overridden in a type
/// specialization in order to support the diagnostic.
virtual std::size_t size() const; virtual std::size_t size() const;
/// Returns 1 iff Var is not empty or this function overridden. /// Returns 1 iff Var is not empty or this function overridden.
@ -519,8 +528,16 @@ inline void VarHolder::convert(Timestamp& /*val*/) const
throw BadCastException("Can not convert to Timestamp"); throw BadCastException("Can not convert to Timestamp");
} }
inline void VarHolder::convert(UUID& /*val*/) const
{
throw BadCastException("Can not convert to UUID");
}
#ifndef POCO_INT64_IS_LONG #ifndef POCO_INT64_IS_LONG
inline void VarHolder::convert(long& val) const inline void VarHolder::convert(long& val) const
{ {
Int32 tmp; Int32 tmp;
@ -536,8 +553,10 @@ inline void VarHolder::convert(unsigned long& val) const
val = tmp; val = tmp;
} }
#else #else
inline void VarHolder::convert(long long& /*val*/) const inline void VarHolder::convert(long long& /*val*/) const
{ {
throw BadCastException("Can not convert to long long"); throw BadCastException("Can not convert to long long");
@ -549,8 +568,10 @@ inline void VarHolder::convert(unsigned long long& /*val*/) const
throw BadCastException("Can not convert to unsigned long long"); throw BadCastException("Can not convert to unsigned long long");
} }
#endif #endif
inline void VarHolder::convert(bool& /*val*/) const inline void VarHolder::convert(bool& /*val*/) const
{ {
throw BadCastException("Can not convert to bool"); throw BadCastException("Can not convert to bool");
@ -671,6 +692,12 @@ inline bool VarHolder::isDateTime() const
} }
inline bool VarHolder::isUUID() const
{
return false;
}
inline std::size_t VarHolder::size() const inline std::size_t VarHolder::size() const
{ {
return 1u; return 1u;
@ -1026,7 +1053,6 @@ public:
return std::numeric_limits<Int16>::is_specialized; return std::numeric_limits<Int16>::is_specialized;
} }
bool isString() const bool isString() const
{ {
return false; return false;
@ -2750,6 +2776,11 @@ public:
ts = tmp.timestamp(); ts = tmp.timestamp();
} }
void convert(UUID& uuid) const
{
uuid.parse(_val);
}
VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const
{ {
return cloneHolder(pVarHolder, _val); return cloneHolder(pVarHolder, _val);
@ -3917,6 +3948,11 @@ public:
return true; return true;
} }
bool isUUID() const
{
return false;
}
private: private:
VarHolderImpl(); VarHolderImpl();
VarHolderImpl(const VarHolderImpl&); VarHolderImpl(const VarHolderImpl&);
@ -4047,6 +4083,11 @@ public:
return true; return true;
} }
bool isUUID() const
{
return false;
}
private: private:
VarHolderImpl(); VarHolderImpl();
VarHolderImpl(const VarHolderImpl&); VarHolderImpl(const VarHolderImpl&);
@ -4177,6 +4218,11 @@ public:
return true; return true;
} }
bool isUUID() const
{
return false;
}
private: private:
VarHolderImpl(); VarHolderImpl();
VarHolderImpl(const VarHolderImpl&); VarHolderImpl(const VarHolderImpl&);
@ -4186,6 +4232,102 @@ private:
}; };
template <>
class VarHolderImpl<UUID>: public VarHolder
{
public:
VarHolderImpl(const UUID& val): _val(val)
{
}
~VarHolderImpl()
{
}
const std::type_info& type() const
{
return typeid(UUID);
}
void convert(std::string& val) const
{
val = _val.toString();
}
VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const
{
return cloneHolder(pVarHolder, _val);
}
const UUID& value() const
{
return _val;
}
bool isArray() const
{
return false;
}
bool isStruct() const
{
return false;
}
bool isInteger() const
{
return false;
}
bool isSigned() const
{
return false;
}
bool isNumeric() const
{
return false;
}
bool isBoolean() const
{
return false;
}
bool isString() const
{
return false;
}
bool isDate() const
{
return false;
}
bool isTime() const
{
return false;
}
bool isDateTime() const
{
return false;
}
bool isUUID() const
{
return true;
}
private:
VarHolderImpl();
VarHolderImpl(const VarHolderImpl&);
VarHolderImpl& operator = (const VarHolderImpl&);
Poco::UUID _val;
};
typedef std::vector<Var> Vector; typedef std::vector<Var> Vector;
typedef std::deque<Var> Deque; typedef std::deque<Var> Deque;
typedef std::list<Var> List; typedef std::list<Var> List;

View File

@ -47,7 +47,8 @@ bool isJSONString(const Var& any)
any.type() == typeid(char*) || any.type() == typeid(char*) ||
any.type() == typeid(Poco::DateTime) || any.type() == typeid(Poco::DateTime) ||
any.type() == typeid(Poco::LocalDateTime) || any.type() == typeid(Poco::LocalDateTime) ||
any.type() == typeid(Poco::Timestamp); any.type() == typeid(Poco::Timestamp) ||
any.type() == typeid(Poco::UUID);
} }

View File

@ -2859,6 +2859,32 @@ void VarTest::testDate()
} }
void VarTest::testUUID()
{
Poco::UUID uuid("f1881be4-c3b7-4a47-9619-5169db5108a7");
Var vuuid(uuid);
assertTrue (vuuid.isUUID());
assert (vuuid.convert<std::string>() == "f1881be4-c3b7-4a47-9619-5169db5108a7");
assert (vuuid.extract<Poco::UUID>() == uuid);
Var vstr(std::string("f1881be4-c3b7-4a47-9619-5169db5108a7"));
assert (vstr.convert<Poco::UUID>() == uuid);
Var vstr2(std::string("notAnUUID"));
try
{
Poco::UUID uuid2 = vstr2.convert<Poco::UUID>();
fail("not a valid UUID, must fail");
}
catch (Poco::SyntaxException&)
{
}
}
void VarTest::testGetIdxNoThrow(Var& a1, std::vector<Var>::size_type n) void VarTest::testGetIdxNoThrow(Var& a1, std::vector<Var>::size_type n)
{ {
Var val1 = a1[n]; Var val1 = a1[n];
@ -3104,6 +3130,7 @@ CppUnit::Test* VarTest::suite()
CppUnit_addTest(pSuite, VarTest, testJSONDeserializeComplex); CppUnit_addTest(pSuite, VarTest, testJSONDeserializeComplex);
CppUnit_addTest(pSuite, VarTest, testJSONRoundtripStruct); CppUnit_addTest(pSuite, VarTest, testJSONRoundtripStruct);
CppUnit_addTest(pSuite, VarTest, testDate); CppUnit_addTest(pSuite, VarTest, testDate);
CppUnit_addTest(pSuite, VarTest, testUUID);
CppUnit_addTest(pSuite, VarTest, testEmpty); CppUnit_addTest(pSuite, VarTest, testEmpty);
CppUnit_addTest(pSuite, VarTest, testIterator); CppUnit_addTest(pSuite, VarTest, testIterator);

View File

@ -71,13 +71,13 @@ public:
void testJSONDeserializePrimitives(); void testJSONDeserializePrimitives();
void testJSONDeserializeArray(); void testJSONDeserializeArray();
void testJSONDeserializeStruct(); void testJSONDeserializeStruct();
void testJSONRoundtripStruct(); void testJSONRoundtripStruct();
void testJSONDeserializeComplex(); void testJSONDeserializeComplex();
void testDate(); void testDate();
void testUUID();
void testEmpty(); void testEmpty();
void testIterator(); void testIterator();
void setUp(); void setUp();
void tearDown(); void tearDown();
static CppUnit::Test* suite(); static CppUnit::Test* suite();