mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-12 10:13:51 +01:00
#3318: Data: Support Poco::UUID for data binding
This commit is contained in:
parent
a95c591e0a
commit
7569ccf82b
@ -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"
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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()";
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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()");
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -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 \
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -100,6 +100,8 @@ public:
|
||||
|
||||
void testDateTime();
|
||||
|
||||
void testUUID();
|
||||
|
||||
void testInternalExtraction();
|
||||
void testPrimaryKeyConstraint();
|
||||
void testNullable();
|
||||
|
@ -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& 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& 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& obj, const Person& 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&);
|
||||
TypeHandler& operator=(const TypeHandler&);
|
||||
};
|
||||
|
||||
|
||||
} } // 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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -50,6 +50,7 @@ public:
|
||||
FDT_DATE,
|
||||
FDT_TIME,
|
||||
FDT_TIMESTAMP,
|
||||
FDT_UUID,
|
||||
FDT_UNKNOWN
|
||||
};
|
||||
|
||||
|
@ -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.");
|
||||
|
@ -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.");
|
||||
|
@ -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.");
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -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.");
|
||||
}
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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&)
|
||||
{
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user