#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

@ -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

@ -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;
@ -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))
@ -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();

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

@ -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

@ -1022,6 +1022,7 @@ 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
later reuse once it is no longer needed.

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

@ -74,10 +74,10 @@ public:
void testJSONRoundtripStruct();
void testJSONDeserializeComplex();
void testDate();
void testUUID();
void testEmpty();
void testIterator();
void setUp();
void tearDown();
static CppUnit::Test* suite();