#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/Context.h"
#include "Poco/ActiveRecord/IDTraits.h"
#include "Poco/ActiveRecord/TypeHandler.h"
#include "Poco/DateTime.h"
#include "Poco/RefCountedObject.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);
/// 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);
/// Binds a null.

View File

@ -110,6 +110,9 @@ public:
virtual bool extract(std::size_t pos, Time& val);
/// 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);
/// 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)
{
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)
{
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()
{
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()
{
dropTable("Tuples");
@ -904,6 +922,7 @@ CppUnit::Test* MySQLTest::suite()
CppUnit_addTest(pSuite, MySQLTest, testUnsignedInts);
CppUnit_addTest(pSuite, MySQLTest, testFloat);
CppUnit_addTest(pSuite, MySQLTest, testDouble);
CppUnit_addTest(pSuite, MySQLTest, testUUID);
CppUnit_addTest(pSuite, MySQLTest, testTuple);
CppUnit_addTest(pSuite, MySQLTest, testTupleVector);
CppUnit_addTest(pSuite, MySQLTest, testInternalExtraction);

View File

@ -24,7 +24,7 @@
class MySQLTest: public CppUnit::TestCase
/// MySQL test class
/// Tested:
///
///
/// Driver | DB | OS
/// ----------------+---------------------------+------------------------------------------
/// 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 testDouble();
void testUUID();
void testTuple();
void testTupleVector();
@ -119,6 +121,7 @@ private:
void recreateIntsTable();
void recreateUnsignedIntsTable();
void recreateFloatsTable();
void recreateUUIDsTable();
void recreateTuplesTable();
void recreateVectorsTable();
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()
{
std::string funct = "insertSingleBulkVec()";

View File

@ -26,7 +26,7 @@ public:
PB_IMMEDIATE,
PB_AT_EXEC
};
enum DataExtraction
{
DE_MANUAL,
@ -37,7 +37,7 @@ public:
~SQLExecutor();
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).
/// The purpose of the function is to verify that driver behaves
/// correctly. If this test passes, subsequent tests failures are likely ours.
@ -86,6 +86,7 @@ public:
void unsignedInts();
void floats();
void doubles();
void uuids();
void tuples();
void tupleVector();

View File

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

View File

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

View File

@ -353,6 +353,9 @@ public:
void prepare(std::size_t pos, const std::list<Poco::DateTime>& val);
/// 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);
/// Prepares an Any.
@ -548,6 +551,12 @@ private:
else
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:
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)
{
prepareImpl<std::vector<Poco::Any> >(pos);

View File

@ -80,6 +80,10 @@ void Binder::freeMemory()
UTF16CharPtrVec::iterator endUTF16Chr = _utf16CharPtrs.end();
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 endBool = _boolPtrs.end();
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);
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_CHAR,
Connector::stringBoundToLongVarChar() ? SQL_LONGVARCHAR : SQL_VARCHAR,
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_CHAR,
Connector::stringBoundToLongVarChar() ? SQL_LONGVARCHAR : SQL_VARCHAR,
(SQLUINTEGER) colSize,
0,
pVal,
(SQLINTEGER) size,
pVal,
(SQLINTEGER) size,
_lengthIndicator.back())))
{
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;
Utility::dateSync(*pDS, val);
_dates.insert(DateMap::value_type(pDS, const_cast<Date*>(&val)));
SQLINTEGER colSize = 0;
SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, SQL_TYPE_DATE, colSize, decDigits);
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TYPE_DATE,
SQL_TYPE_DATE,
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TYPE_DATE,
SQL_TYPE_DATE,
colSize,
decDigits,
(SQLPOINTER) pDS,
0,
(SQLPOINTER) pDS,
0,
_lengthIndicator.back())))
{
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;
Utility::timeSync(*pTS, val);
_times.insert(TimeMap::value_type(pTS, const_cast<Time*>(&val)));
SQLINTEGER colSize = 0;
SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, SQL_TYPE_TIME, colSize, decDigits);
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TYPE_TIME,
SQL_TYPE_TIME,
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TYPE_TIME,
SQL_TYPE_TIME,
colSize,
decDigits,
(SQLPOINTER) pTS,
0,
(SQLPOINTER) pTS,
0,
_lengthIndicator.back())))
{
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;
getColSizeAndPrecision(pos, SQL_TYPE_TIMESTAMP, colSize, decDigits);
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TYPE_TIMESTAMP,
SQL_TYPE_TIMESTAMP,
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
toODBCDirection(dir),
SQL_C_TYPE_TIMESTAMP,
SQL_TYPE_TIMESTAMP,
colSize,
decDigits,
(SQLPOINTER) pTS,
0,
(SQLPOINTER) pTS,
0,
_lengthIndicator.back())))
{
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)
{
if (isOutBound(dir) || !isInBound(dir))
@ -311,15 +347,15 @@ void Binder::bind(std::size_t pos, const NullData& val, Direction dir)
SQLSMALLINT decDigits = 0;
getColSizeAndPrecision(pos, SQL_C_STINYINT, colSize, decDigits);
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
SQL_PARAM_INPUT,
SQL_C_STINYINT,
Utility::sqlDataType(SQL_C_STINYINT),
if (Utility::isError(SQLBindParameter(_rStmt,
(SQLUSMALLINT) pos + 1,
SQL_PARAM_INPUT,
SQL_C_STINYINT,
Utility::sqlDataType(SQL_C_STINYINT),
colSize,
decDigits,
0,
0,
0,
0,
_lengthIndicator.back())))
{
throw StatementException(_rStmt, "SQLBindParameter()");
@ -334,7 +370,7 @@ std::size_t Binder::parameterSize(SQLPOINTER pAddr) const
it = _outParams.find(pAddr);
if (it != _outParams.end()) return it->second;
throw NotFoundException("Requested data size not found.");
}
@ -350,7 +386,7 @@ SQLSMALLINT Binder::toODBCDirection(Direction dir) const
bool in = isInBound(dir);
bool out = isOutBound(dir);
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(out) ioType = SQL_PARAM_OUTPUT;
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 end = _dates.end();
for(; it != end; ++it)
for(; it != end; ++it)
Utility::dateSync(*it->second, *it->first);
}
@ -373,7 +409,7 @@ void Binder::synchronize()
{
TimeMap::iterator it = _times.begin();
TimeMap::iterator end = _times.end();
for(; it != end; ++it)
for(; it != end; ++it)
Utility::timeSync(*it->second, *it->first);
}
@ -381,7 +417,7 @@ void Binder::synchronize()
{
TimestampMap::iterator it = _timestamps.begin();
TimestampMap::iterator end = _timestamps.end();
for(; it != end; ++it)
for(; it != end; ++it)
Utility::dateTimeSync(*it->second, *it->first);
}
@ -392,6 +428,14 @@ void Binder::synchronize()
for(; it != end; ++it)
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();
_timestamps.clear();
_strings.clear();
_uuids.clear();
_dateVecVec.clear();
_timeVecVec.clear();
_dateTimeVecVec.clear();
@ -415,9 +460,9 @@ void Binder::reset()
}
void Binder::getColSizeAndPrecision(std::size_t pos,
SQLSMALLINT cDataType,
SQLINTEGER& colSize,
void Binder::getColSizeAndPrecision(std::size_t pos,
SQLSMALLINT cDataType,
SQLINTEGER& colSize,
SQLSMALLINT& decDigits,
std::size_t actualSize)
{
@ -448,9 +493,9 @@ void Binder::getColSizeAndPrecision(std::size_t pos,
colSize = (SQLINTEGER) p.columnSize();
decDigits = (SQLSMALLINT) p.decimalDigits();
return;
}
}
catch (StatementException&)
{
{
}
try
@ -459,9 +504,9 @@ void Binder::getColSizeAndPrecision(std::size_t pos,
colSize = (SQLINTEGER) c.length();
decDigits = (SQLSMALLINT) c.precision();
return;
}
catch (StatementException&)
{
}
catch (StatementException&)
{
}
// 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)))
{
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)
{
size = sz;
@ -529,7 +574,7 @@ void Binder::setParamSetSize(std::size_t length)
{
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)))
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<>
bool Extractor::extractBoundImplContainer<std::vector<bool> >(std::size_t pos,
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)
{
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)
{
if (Preparator::DE_MANUAL == _dataExtraction)

View File

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

View File

@ -37,6 +37,9 @@ endif
ifeq (0, $(shell test -e /opt/postgresql/lib$(LIB64SUFFIX); echo $$?))
SYSLIBS += -L/opt/postgresql/lib$(LIB64SUFFIX)
endif
ifeq (0, $(shell test -e /usr/local/opt/libpq/lib; echo $$?))
SYSLIBS += -L/usr/local/opt/libpq/lib$(LIB64SUFFIX)
endif
SYSLIBS += -lpq
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);
/// 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);
/// Binds a null.

View File

@ -109,6 +109,9 @@ public:
virtual bool extract(std::size_t pos, Time& val);
/// 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);
/// 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_TIME:
case Poco::Data::MetaColumn::FDT_TIMESTAMP:
case Poco::Data::MetaColumn::FDT_UUID:
return _stringVersionRepresentation.c_str();
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)
{
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());
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:
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)
{
return extractStringImpl(pos, val);
@ -583,6 +596,14 @@ bool Extractor::extractToDynamic(std::size_t pos, Dynamic::Var& val)
val = dt;
break;
}
case UUIDOID:
{
UUID uuid;
success = extract(pos, uuid);
if (success)
val = uuid;
break;
}
}
return success;

View File

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

View File

@ -37,6 +37,9 @@ endif
ifeq (0, $(shell test -e /opt/postgresql/lib$(LIB64SUFFIX); echo $$?))
SYSLIBS += -L/opt/postgresql/lib$(LIB64SUFFIX)
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.
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()
{
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()
{
dropTable("Tuples");
@ -1110,6 +1127,7 @@ CppUnit::Test* PostgreSQLTest::suite()
CppUnit_addTest(pSuite, PostgreSQLTest, testUnsignedInts);
CppUnit_addTest(pSuite, PostgreSQLTest, testFloat);
CppUnit_addTest(pSuite, PostgreSQLTest, testDouble);
CppUnit_addTest(pSuite, PostgreSQLTest, testUUID);
CppUnit_addTest(pSuite, PostgreSQLTest, testTuple);
CppUnit_addTest(pSuite, PostgreSQLTest, testTupleVector);
CppUnit_addTest(pSuite, PostgreSQLTest, testInternalExtraction);

View File

@ -82,6 +82,7 @@ public:
void testUnsignedInts();
void testFloat();
void testDouble();
void testUUID();
void testTuple();
void testTupleVector();
@ -118,6 +119,7 @@ private:
void recreateIntsTable();
void recreateUnsignedIntsTable();
void recreateFloatsTable();
void recreateUUIDsTable();
void recreateTuplesTable();
void recreateVectorsTable();
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()
{
std::string funct = "floats()";
@ -1611,7 +1634,7 @@ void SQLExecutor::blobStmt()
std::string lastName("lastname");
std::string firstName("firstname");
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);
int count = 0;

View File

@ -25,7 +25,7 @@ public:
PB_IMMEDIATE,
PB_AT_EXEC
};
enum DataExtraction
{
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[]);
/// 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);
/// This function uses "bare bone" API calls (i.e. calls are not
/// "wrapped" in PocoSQL framework structures).
@ -89,6 +89,7 @@ public:
void unsignedInts();
void floats();
void doubles();
void uuids();
void tuples();
void tupleVector();

View File

@ -106,6 +106,9 @@ public:
void bind(std::size_t pos, const DateTime& val, Direction dir);
/// 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);
/// Binds a null.

View File

@ -116,6 +116,9 @@ public:
bool extract(std::size_t pos, Poco::DateTime& val);
/// Extracts a DateTime.
bool extract(std::size_t pos, Poco::UUID& val);
/// Extracts a Time.
bool extract(std::size_t pos, Poco::Any& val);
/// Extracts an Any.
@ -264,6 +267,13 @@ private:
val = dt;
break;
}
case MetaColumn::FDT_UUID:
{
UUID uuid;
ret = extract(pos, uuid);
val = uuid;
break;
}
default:
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)
{
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)
{
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("DATETIME", 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/Tuple.h"
#include "Poco/Any.h"
#include "Poco/UUIDGenerator.h"
#include "Poco/SharedPtr.h"
#include "Poco/DynamicAny.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()
{
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, testTupleVector1);
CppUnit_addTest(pSuite, SQLiteTest, testDateTime);
CppUnit_addTest(pSuite, SQLiteTest, testUUID);
CppUnit_addTest(pSuite, SQLiteTest, testInternalExtraction);
CppUnit_addTest(pSuite, SQLiteTest, testPrimaryKeyConstraint);
CppUnit_addTest(pSuite, SQLiteTest, testNullable);

View File

@ -100,6 +100,8 @@ public:
void testDateTime();
void testUUID();
void testInternalExtraction();
void testPrimaryKeyConstraint();
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 <vector>
#include <iostream>
using namespace Poco::Data::Keywords;
using Poco::Data::Session;
using Poco::Data::Statement;
struct Person
{
std::string name;
std::string address;
int age;
};
int main(int argc, char** argv)
{
// register SQLite connector
Poco::Data::SQLite::Connector::registerConnector();
// create a session
Session session("SQLite", "sample.db");
// drop sample table, if it exists
session << "DROP TABLE IF EXISTS Person", now;
// (re)create table
session << "CREATE TABLE Person (Name VARCHAR(30), Address VARCHAR, Age INTEGER(3))", now;
// insert some rows
Person person =
Person person =
{
"Bart Simpson",
"Springfield",
12
};
Statement insert(session);
insert << "INSERT INTO Person VALUES(?, ?, ?)",
use(person.name),
use(person.address),
use(person.age);
insert.execute();
person.name = "Lisa Simpson";
person.address = "Springfield";
person.age = 10;
insert.execute();
// a simple query
Statement select(session);
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.age),
range(0, 1); // iterate over result set one row at a time
while (!select.done())
{
select.execute();
std::cout << person.name << " " << person.address << " " << person.age << std::endl;
}
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
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
for full namespace qualification; this document uses convention introduced in
for full namespace qualification; this document uses convention introduced in
the example above.
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.
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
(for details, consult your ODBC driver documentation).
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
(for details, consult your ODBC driver documentation).
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
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
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
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:
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
<[Peter]> value. Note that apart from the nicer syntax, the real benefits of
placeholders -- which are performance and protection against SQL injection
<[Peter]> value. Note that apart from the nicer syntax, the real benefits of
placeholders -- which are performance and protection against SQL injection
attacks -- don't show here. Check the <[Statements]> section to find out more.
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, 0, "default"), now;
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 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
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::Dynamic::Var, which will be set as empty for null values when used as query
output target.
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 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
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::Dynamic::Var, which will be set as empty for null values when used as query
output target.
----
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()) { ... }
----
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.
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
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
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
tweak it to user's needs through folowing <[Session]> features:
*emptyStringIsNull
*forceEmptyString
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:
@ -259,7 +259,7 @@ set it belongs to:
std::vector<Person> people;
Person pHomer, pLisa;
int aHomer(42), aLisa(10), aBart(0);
session << "SELECT * FROM Person WHERE Age = ?; "
"SELECT Age FROM Person WHERE FirstName = 'Bart'; "
"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
functions. Does Poco::Data provide any support there? You bet.
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
best to provide reasonable access to such functionality through <[in]>,
<[out]> and <[io]> binding functions. As their names imply, these
@ -306,7 +306,7 @@ here's an Oracle ODBC example:
" temp NUMBER := param1; "
" BEGIN param1 := param2; param2 := temp; RETURN(param1+param2); "
" END storedFunction;" , now;
int i = 1, j = 2, result = 0;
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; "
"BEGIN "
" OPEN ret FOR "
" SELECT * FROM Person WHERE Age < ageLimit; "
" SELECT * FROM Person WHERE Age < ageLimit; "
" RETURN ret; "
"END storedCursorFunction;" , 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
the value was inserted. The check to <[stmt.done()]> simply guarantees that the
statement was fully completed.
the value was inserted. The check to <[stmt.done()]> simply guarantees that the
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
number of returned rows (remember, asynchronous <[execute()]> call
returns <[immediately]> and does not wait for the completion of the
execution).
execution).
!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
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 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,
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 ("%%"):
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:
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
poco_assert (!stmt.done());
stmt.execute(); //names.size() == 100
poco_assert (!stmt.done());
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
@ -750,14 +750,14 @@ off.
The <[bulk]> keyword allows to boost performance for the connectors that
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:
std::vector<int> ints(100, 1);
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:
@ -827,7 +827,7 @@ feature.
!!! RecordSets, Iterators and Rows
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, 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
select << "SELECT * FROM Person", now;
// create a RecordSet
// create a RecordSet
RecordSet rs(select);
std::size_t cols = rs.columnCount();
// print all column names
for (std::size_t col = 0; col < cols; ++col)
std::cout << rs.columnName(col) << std::endl;
// 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 << " ";
----
@ -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
(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
----
@ -914,7 +914,7 @@ Valid storage type manipulators are:
So, if neither data storage, nor storage type are explicitly specified,
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
@ -930,19 +930,19 @@ Assume you have a class Person:
// default constructor+destr.
// getter and setter methods for all members
// ...
bool operator <(const Person&amp; p) const
/// we need this for set and multiset support
{
return _socialSecNr < p._socialSecNr;
}
Poco::UInt64 operator()() const
/// we need this operator to return the key for the map and multimap
{
return _socialSecNr;
}
private:
std::string _firstName;
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.
All that is needed is a template specialization of the <[TypeHandler]>
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:
namespace Poco {
namespace Data {
template <>
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<Poco::UInt64>::bind(pos++, obj.getSocialSecNr(), pBinder, dir);
}
static std::size_t size()
{
return 3; // we handle three columns of the Table!
}
static void prepare(std::size_t pos, const Person&amp; obj, AbstractPreparator::Ptr pPrepare)
{
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<Poco::UInt64>::prepare(pos++, obj.getSocialSecNr(), pPrepare);
}
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
{
@ -1002,14 +1002,14 @@ The template specialization must implement the following methods:
obj.setLastName(lastName);
obj.setSocialSecNr(socialSecNr);
}
private:
TypeHandler();
~TypeHandler();
TypeHandler(const TypeHandler&amp;);
TypeHandler&amp; operator=(const TypeHandler&amp;);
};
} } // namespace Poco::Data
----
@ -1022,8 +1022,9 @@ working with a string:
!!!Session Pooling
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.
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.
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 spc;
@ -1060,15 +1061,15 @@ SessionPoolContainer:
This document provides an overview of the most important features
offered by the POCO Data framework. The framework also supports LOB
(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
(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
did not go into details here.
The great deal of <[RecordSet]> and <[Row]> runtime "magic" is achieved
through employment of Poco::Dynamic::Var, which is the POCO
equivalent of dynamic language data type. Obviously, due to its nature,
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,
with configurable efficiency/convenience ratio, providing a solid

View File

@ -24,6 +24,7 @@
#include "Poco/Data/LOB.h"
#include "Poco/DateTime.h"
#include "Poco/Nullable.h"
#include "Poco/UUID.h"
#include "Poco/Any.h"
#include "Poco/Dynamic/Var.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);
/// 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;
/// Binds a null.

View File

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

View File

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

View File

@ -50,6 +50,7 @@ public:
FDT_DATE,
FDT_TIME,
FDT_TIMESTAMP,
FDT_UUID,
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)
{
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)
{
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)
{
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_TIME: return value<Time>(col, row, useFilter);
case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(col, row);
case MetaColumn::FDT_UUID: return value<UUID>(col, row);
default:
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_TIME: return value<Time>(name, row, useFilter);
case MetaColumn::FDT_TIMESTAMP: return value<DateTime>(name, row, useFilter);
case MetaColumn::FDT_UUID: return value<UUID>(name, row, useFilter);
default:
throw UnknownTypeException("Data type not supported.");
}

View File

@ -351,6 +351,8 @@ void StatementImpl::makeExtractors(std::size_t count)
addInternalExtract<Time>(mc); break;
case MetaColumn::FDT_TIMESTAMP:
addInternalExtract<DateTime>(mc); break;
case MetaColumn::FDT_UUID:
addInternalExtract<UUID>(mc); break;
default:
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)
{
}

View File

@ -100,8 +100,11 @@ public:
void bind(std::size_t pos, const DateTime& val, Direction dir);
/// 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);
/// Binds a DateTime.
/// Binds a NullData.
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)
{
return true;
}
bool Extractor::extract(std::size_t pos, Poco::UUID& val)
{
return true;
}
bool Extractor::extract(std::size_t pos, Poco::Any& val)
{
return true;

View File

@ -100,9 +100,12 @@ public:
bool extract(std::size_t pos, Time& val);
/// Extracts a Time.
bool extract(std::size_t pos, Poco::DateTime& val);
bool extract(std::size_t pos, DateTime& val);
/// 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);
/// 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&)
{
}

View File

@ -98,6 +98,9 @@ public:
void prepare(std::size_t pos, const Poco::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&);
/// Prepares an Any.

View File

@ -523,6 +523,9 @@ public:
bool isDateTime() const;
/// 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;
/// Returns the size of this Var.
/// 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
{
VarHolder* pHolder = content();

View File

@ -31,6 +31,7 @@
#include "Poco/UnicodeConverter.h"
#include "Poco/UTFString.h"
#include "Poco/UTF8String.h"
#include "Poco/UUID.h"
#include "Poco/Any.h"
#include "Poco/Exception.h"
#include <vector>
@ -178,6 +179,10 @@ public:
/// Throws BadCastException. Must be overridden in a type
/// 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
void convert(long& val) const;
@ -277,6 +282,10 @@ public:
/// Returns false. Must be properly overridden in a type
/// 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;
/// 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");
}
inline void VarHolder::convert(UUID& /*val*/) const
{
throw BadCastException("Can not convert to UUID");
}
#ifndef POCO_INT64_IS_LONG
inline void VarHolder::convert(long& val) const
{
Int32 tmp;
@ -536,8 +553,10 @@ inline void VarHolder::convert(unsigned long& val) const
val = tmp;
}
#else
inline void VarHolder::convert(long long& /*val*/) const
{
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");
}
#endif
inline void VarHolder::convert(bool& /*val*/) const
{
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
{
return 1u;
@ -1026,7 +1053,6 @@ public:
return std::numeric_limits<Int16>::is_specialized;
}
bool isString() const
{
return false;
@ -2750,6 +2776,11 @@ public:
ts = tmp.timestamp();
}
void convert(UUID& uuid) const
{
uuid.parse(_val);
}
VarHolder* clone(Placeholder<VarHolder>* pVarHolder = 0) const
{
return cloneHolder(pVarHolder, _val);
@ -3917,6 +3948,11 @@ public:
return true;
}
bool isUUID() const
{
return false;
}
private:
VarHolderImpl();
VarHolderImpl(const VarHolderImpl&);
@ -4047,6 +4083,11 @@ public:
return true;
}
bool isUUID() const
{
return false;
}
private:
VarHolderImpl();
VarHolderImpl(const VarHolderImpl&);
@ -4177,6 +4218,11 @@ public:
return true;
}
bool isUUID() const
{
return false;
}
private:
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::deque<Var> Deque;
typedef std::list<Var> List;

View File

@ -47,7 +47,8 @@ bool isJSONString(const Var& any)
any.type() == typeid(char*) ||
any.type() == typeid(Poco::DateTime) ||
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)
{
Var val1 = a1[n];
@ -3104,6 +3130,7 @@ CppUnit::Test* VarTest::suite()
CppUnit_addTest(pSuite, VarTest, testJSONDeserializeComplex);
CppUnit_addTest(pSuite, VarTest, testJSONRoundtripStruct);
CppUnit_addTest(pSuite, VarTest, testDate);
CppUnit_addTest(pSuite, VarTest, testUUID);
CppUnit_addTest(pSuite, VarTest, testEmpty);
CppUnit_addTest(pSuite, VarTest, testIterator);

View File

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