From d4596cf570dac98e13640d7ff63dba932a5bfd0b Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 15 Jan 2015 20:36:27 +0000 Subject: Add the SqlSelectDeserializer for Slicer objects and covering unit tests --- p2pvr/daemon/Jamfile.jam | 3 + p2pvr/daemon/sqlSelectDeserializer.cpp | 171 +++++++++++++++++++++ p2pvr/daemon/sqlSelectDeserializer.h | 26 ++++ p2pvr/daemon/unittests/Jamfile.jam | 15 ++ .../daemon/unittests/testSqlSelectDeserializer.cpp | 81 ++++++++++ 5 files changed, 296 insertions(+) create mode 100644 p2pvr/daemon/sqlSelectDeserializer.cpp create mode 100644 p2pvr/daemon/sqlSelectDeserializer.h create mode 100644 p2pvr/daemon/unittests/testSqlSelectDeserializer.cpp diff --git a/p2pvr/daemon/Jamfile.jam b/p2pvr/daemon/Jamfile.jam index d9894d3..5913af1 100644 --- a/p2pvr/daemon/Jamfile.jam +++ b/p2pvr/daemon/Jamfile.jam @@ -1,4 +1,5 @@ import testing ; +lib slicer : : slicer : : /usr/include/slicer ; cpp-pch pch : pch.hpp : ../ice//p2pvrice @@ -21,9 +22,11 @@ lib p2pvrdaemon : ../devices//p2pvrdevices ../daemonbase//p2pvrdaemonbase ../../libtmdb//tmdb + slicer : : . ../ice//p2pvrice + slicer ; unit-test testEmbedding : diff --git a/p2pvr/daemon/sqlSelectDeserializer.cpp b/p2pvr/daemon/sqlSelectDeserializer.cpp new file mode 100644 index 0000000..0e87a64 --- /dev/null +++ b/p2pvr/daemon/sqlSelectDeserializer.cpp @@ -0,0 +1,171 @@ +#include "sqlSelectDeserializer.h" +#include +#include +#include +#include + +VariableType +operator/(DB::SelectCommand & cmd, unsigned int col) +{ + HandleAsVariableType vt; + cmd[col].apply(vt); + return vt.variable; +} + +class SqlSource : public DB::HandleField, public Slicer::ValueSource, + public Slicer::TValueSource, + public Slicer::TValueSource { + public: + bool isNull() const + { + return vt.isNull(); + } + + void interval(const boost::posix_time::time_duration & d) override + { + vt = d; + } + void null() override + { + vt = Null(); + } + void string(const char * s, size_t l) override + { + vt = Glib::ustring(s, l); + } + void integer(int64_t i) override + { + vt = i; + } + void floatingpoint(double fp) override + { + vt = fp; + } + void timestamp(const boost::posix_time::ptime & t) + { + vt = t; + } +#define SET(Type) \ + void set(Type & b) const override { \ + b = vt.as(); \ + } +#define SETNCONV(Type, CType) \ + void set(Type & b) const override { \ + b = boost::numeric_cast(vt.as()); \ + } + SET(bool); + SET(std::string); + SETNCONV(Ice::Byte, int64_t); + SETNCONV(Ice::Short, int64_t); + SET(Ice::Int); + SET(Ice::Long); + SETNCONV(Ice::Float, double); + SET(Ice::Double); + SET(boost::posix_time::ptime); + SET(boost::posix_time::time_duration); + + private: + VariableType vt; +}; +typedef IceUtil::Handle SqlSourcePtr; + +SqlSelectDeserializer::SqlSelectDeserializer(DB::SelectCommand & c, IceUtil::Optional tc) : + cmd(c), + typeIdColName(tc) +{ +} + +void +SqlSelectDeserializer::Deserialize(Slicer::ModelPartPtr mp) +{ + cmd.execute(); + columnCount = cmd.columnCount(); + if (typeIdColName) { + typeIdColIdx = cmd.getOrdinal(*typeIdColName); + } + switch (mp->GetType()) { + case Slicer::mpt_Sequence: + DeserializeSequence(mp); + return; + case Slicer::mpt_Complex: + DeserializeObject(mp); + return; + case Slicer::mpt_Simple: + DeserializeSimple(mp); + return; + default: + throw std::invalid_argument("Unspported model type"); + } +} + +void +SqlSelectDeserializer::DeserializeSimple(Slicer::ModelPartPtr mp) +{ + auto fmp = mp->GetAnonChild(); + if (!cmd.fetch()) { + std::invalid_argument("No rows returned"); + } + SqlSourcePtr h = new SqlSource(); + const DB::Column & c = cmd[0]; + c.apply(*h); + if (!h->isNull()) { + fmp->Create(); + fmp->SetValue(h); + fmp->Complete(); + } + if (cmd.fetch()) { + std::invalid_argument("Too many rows returned"); + } +} + +void +SqlSelectDeserializer::DeserializeSequence(Slicer::ModelPartPtr mp) +{ + mp = mp->GetAnonChild(); + SqlSourcePtr h = new SqlSource(); + while (cmd.fetch()) { + DeserializeRow(mp); + } +} + +void +SqlSelectDeserializer::DeserializeObject(Slicer::ModelPartPtr mp) +{ + if (!cmd.fetch()) { + std::invalid_argument("No rows returned"); + } + DeserializeRow(mp); + if (cmd.fetch()) { + std::invalid_argument("Too many rows returned"); + } +} + +void +SqlSelectDeserializer::DeserializeRow(Slicer::ModelPartPtr mp) +{ + SqlSourcePtr h = new SqlSource(); + auto rmp = mp->GetAnonChild(); + if (rmp) { + if (typeIdColIdx) { + rmp = rmp->GetSubclassModelPart(cmd / *typeIdColIdx); + } + rmp->Create(); + for (auto col = 0u; col < columnCount; col += 1) { + const DB::Column & c = cmd[col]; + auto fmpr = rmp->GetAnonChildRef([&c](Slicer::HookCommonPtr h) { + return boost::iequals(c.name.raw(), h->PartName()); + }); + if (fmpr) { + auto fmp = fmpr->Child(); + c.apply(*h); + if (!h->isNull()) { + fmp->Create(); + fmp->SetValue(h); + fmp->Complete(); + } + } + } + rmp->Complete(); + } +} + diff --git a/p2pvr/daemon/sqlSelectDeserializer.h b/p2pvr/daemon/sqlSelectDeserializer.h new file mode 100644 index 0000000..008a87c --- /dev/null +++ b/p2pvr/daemon/sqlSelectDeserializer.h @@ -0,0 +1,26 @@ +#ifndef P2PVR_SQL_DESERIALIZER_H +#define P2PVR_SQL_DESERIALIZER_H + +#include +#include + +class SqlSelectDeserializer : public Slicer::Deserializer { + public: + SqlSelectDeserializer(DB::SelectCommand &, IceUtil::Optional typeIdCol = IceUtil::Optional()); + + virtual void Deserialize(Slicer::ModelPartPtr) override; + + protected: + void DeserializeSimple(Slicer::ModelPartPtr); + void DeserializeObject(Slicer::ModelPartPtr); + void DeserializeSequence(Slicer::ModelPartPtr); + void DeserializeRow(Slicer::ModelPartPtr); + + DB::SelectCommand & cmd; + unsigned int columnCount; + IceUtil::Optional typeIdColName; + IceUtil::Optional typeIdColIdx; +}; + +#endif + diff --git a/p2pvr/daemon/unittests/Jamfile.jam b/p2pvr/daemon/unittests/Jamfile.jam index f07338f..c9ebc5a 100644 --- a/p2pvr/daemon/unittests/Jamfile.jam +++ b/p2pvr/daemon/unittests/Jamfile.jam @@ -121,3 +121,18 @@ unit-test testStorage : ROOT=\"$(me)\" ; +unit-test testSqlSelectDeserializer : + testSqlSelectDeserializer.cpp + : + BOOST_TEST_DYN_LINK + ../..//p2common + ../..//p2sql + ../..//p2ut + ../..//p2xml + ..//p2pvrdaemon + IceUtil + Ice + ../..//boost_utf + ROOT=\"$(me)\" + ; + diff --git a/p2pvr/daemon/unittests/testSqlSelectDeserializer.cpp b/p2pvr/daemon/unittests/testSqlSelectDeserializer.cpp new file mode 100644 index 0000000..79990cb --- /dev/null +++ b/p2pvr/daemon/unittests/testSqlSelectDeserializer.cpp @@ -0,0 +1,81 @@ +#define BOOST_TEST_MODULE SqlSelectDeserializer +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef boost::shared_ptr SelectPtr; + +class TestCommonObjects : public CommonObjects { + public: + TestCommonObjects() + { + TestOptionsSource::LoadTestOptions({ + { "common.datasourceRoot", (RootDir / "datasources").string() }, + }); + } +}; +BOOST_FIXTURE_TEST_SUITE ( Sql, TestCommonObjects ); + +BOOST_AUTO_TEST_CASE( listOfEvents ) +{ + auto db = dataSource("postgres")->getReadonly(); + auto sel = SelectPtr(db->newSelectCommand("SELECT * FROM events ORDER BY serviceId, eventId LIMIT 100")); + auto res = Slicer::DeserializeAny(*sel); + BOOST_REQUIRE_EQUAL(res.size(), 100); + BOOST_REQUIRE_EQUAL(res[0]->ServiceId, 4166); + BOOST_REQUIRE_EQUAL(res[0]->EventId, 49741); + BOOST_REQUIRE_EQUAL(res[0]->Title, "Skiing Weatherview"); + BOOST_REQUIRE(!res[0]->Subtitle); + BOOST_REQUIRE_EQUAL(res[0]->Description, "Detailed weather forecast."); + BOOST_REQUIRE_EQUAL(res[0]->StartTime, Common::DateTime({2014, 12, 19, 0, 25})); + BOOST_REQUIRE_EQUAL(res[0]->StopTime, Common::DateTime({2014, 12, 19, 0, 30})); + BOOST_REQUIRE(res[0]->Current); + BOOST_REQUIRE_EQUAL(res[99]->ServiceId, 4166); + BOOST_REQUIRE_EQUAL(res[99]->EventId, 52448); + BOOST_REQUIRE_EQUAL(res[99]->Title, "East Midlands Today"); +} + +BOOST_AUTO_TEST_CASE( singleField ) +{ + auto db = dataSource("postgres")->getReadonly(); + auto sel = SelectPtr(db->newSelectCommand("SELECT EventId FROM events ORDER BY serviceId, eventId LIMIT 1")); + auto res = Slicer::DeserializeAny(*sel); + BOOST_REQUIRE_EQUAL(res, 49741); +} + +BOOST_AUTO_TEST_CASE( singleEvent ) +{ + auto db = dataSource("postgres")->getReadonly(); + auto sel = SelectPtr(db->newSelectCommand("SELECT * FROM events ORDER BY serviceId, eventId LIMIT 1")); + auto res = Slicer::DeserializeAny(*sel); + BOOST_REQUIRE_EQUAL(res->ServiceId, 4166); + BOOST_REQUIRE_EQUAL(res->EventId, 49741); + BOOST_REQUIRE_EQUAL(res->Title, "Skiing Weatherview"); + BOOST_REQUIRE(!res->Subtitle); + BOOST_REQUIRE_EQUAL(res->Description, "Detailed weather forecast."); + BOOST_REQUIRE_EQUAL(res->StartTime, Common::DateTime({2014, 12, 19, 0, 25})); + BOOST_REQUIRE_EQUAL(res->StopTime, Common::DateTime({2014, 12, 19, 0, 30})); +} + +BOOST_AUTO_TEST_CASE( dynamicTypes ) +{ + auto db = dataSource("postgres")->getReadonly(); + auto sel = SelectPtr(db->newSelectCommand("SELECT d.*, '::DVBSI::TerrestrialDelivery' \"typeid\" FROM delivery_dvbt d ORDER BY TransportStreamId")); + auto res = Slicer::DeserializeAny(*sel, "typeid"); + BOOST_REQUIRE_EQUAL(res.size(), 6); + auto dvbt = DVBSI::TerrestrialDeliveryPtr::dynamicCast(res[0]); + BOOST_REQUIRE_EQUAL(dvbt->Frequency, 682000000); + BOOST_REQUIRE_EQUAL(dvbt->TransportStreamId, 4170); + BOOST_REQUIRE_EQUAL(dvbt->CodeRateHP, 2); +} + +BOOST_AUTO_TEST_SUITE_END(); + -- cgit v1.2.3