From d340eddd8966689bf20d7749a01e20aacb12c064 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Thu, 15 Oct 2015 22:20:05 +0100 Subject: Add basic support for insert serialization of objects and sequences of objects --- slicer/db/Jamfile.jam | 21 +++++++++++ slicer/db/slicer.sql | 11 ++++++ slicer/db/sqlBinder.cpp | 70 ++++++++++++++++++++++++++++++++++++ slicer/db/sqlBinder.h | 36 +++++++++++++++++++ slicer/db/sqlInsertSerializer.cpp | 76 +++++++++++++++++++++++++++++++++++++++ slicer/db/sqlInsertSerializer.h | 28 +++++++++++++++ slicer/db/testInsert.cpp | 61 +++++++++++++++++++++++++++++++ slicer/test/preprocessor.cpp | 2 +- slicer/test/types.ice | 1 + 9 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 slicer/db/sqlBinder.cpp create mode 100644 slicer/db/sqlBinder.h create mode 100644 slicer/db/sqlInsertSerializer.cpp create mode 100644 slicer/db/sqlInsertSerializer.h create mode 100644 slicer/db/testInsert.cpp diff --git a/slicer/db/Jamfile.jam b/slicer/db/Jamfile.jam index 9d18558..a6a94e8 100644 --- a/slicer/db/Jamfile.jam +++ b/slicer/db/Jamfile.jam @@ -7,6 +7,7 @@ alias glibmm : : : : lib dbppcore : : : : /usr/include/dbpp ; lib dbpp-postgresql : : : : /usr/include/dbpp-postgresql ; +lib adhocutil : : : : /usr/include/adhocutil ; lib boost_system ; lib boost_filesystem ; lib boost_utf : : boost_unit_test_framework ; @@ -19,6 +20,7 @@ lib slicer-db : IceUtil dbppcore glibmm + adhocutil ../slicer//slicer -fvisibility=hidden release:-flto @@ -47,3 +49,22 @@ run testSelect.cpp testSelect ; +run testInsert.cpp + : : : + ROOT=\"$(me)\" + BOOST_TEST_DYN_LINK + slicer-db + dbpp-postgresql + boost_system + boost_filesystem + boost_utf + ../test//slicer-test + ../test//common + ../slicer//slicer + .. + slicer.sql + ../test//compilation + : + testInsert + ; + diff --git a/slicer/db/slicer.sql b/slicer/db/slicer.sql index 94807f5..d77c15c 100644 --- a/slicer/db/slicer.sql +++ b/slicer/db/slicer.sql @@ -10,3 +10,14 @@ INSERT INTO test VALUES(1, 1.1, 'text one', true, '2015-01-27 23:06:03', '1 day INSERT INTO test VALUES(2, 12.12, 'text two', true, '2015-02-27 23:06:03', '1 day 12:13:12'); INSERT INTO test VALUES(3, 123.123, 'text three', false, '2015-03-27 23:06:03', '1 day 13:13:12'); INSERT INTO test VALUES(4, 1234.1234, 'text four', false, '2015-04-27 23:06:03', '1 day 14:13:12'); + +CREATE TABLE builtins( + mbool boolean, + mbyte smallint, + mshort smallint, + mint int, + mlong bigint, + mfloat numeric(5, 2), + mdouble numeric(8, 5), + mstring text); + diff --git a/slicer/db/sqlBinder.cpp b/slicer/db/sqlBinder.cpp new file mode 100644 index 0000000..6969749 --- /dev/null +++ b/slicer/db/sqlBinder.cpp @@ -0,0 +1,70 @@ +#include "sqlBinder.h" + +namespace Slicer { + SqlBinder::SqlBinder(DB::Command & c, unsigned int i) : + command(c), + idx(i) + { + } + + void + SqlBinder::get(const boost::posix_time::ptime & b) const + { + command.bindParamT(idx, b); + } + + void + SqlBinder::get(const boost::posix_time::time_duration & b) const + { + command.bindParamT(idx, b); + } + + void + SqlBinder::get(const bool & b) const + { + command.bindParamB(idx, b); + } + + void + SqlBinder::get(const Ice::Byte & b) const + { + command.bindParamI(idx, b); + } + + void + SqlBinder::get(const Ice::Short & b) const + { + command.bindParamI(idx, b); + } + + void + SqlBinder::get(const Ice::Int & b) const + { + command.bindParamI(idx, b); + } + + void + SqlBinder::get(const Ice::Long & b) const + { + command.bindParamI(idx, b); + } + + void + SqlBinder::get(const Ice::Float & b) const + { + command.bindParamF(idx, b); + } + + void + SqlBinder::get(const Ice::Double & b) const + { + command.bindParamF(idx, b); + } + + void + SqlBinder::get(const std::string & b) const + { + command.bindParamS(idx, b); + } +} + diff --git a/slicer/db/sqlBinder.h b/slicer/db/sqlBinder.h new file mode 100644 index 0000000..b4d7ac4 --- /dev/null +++ b/slicer/db/sqlBinder.h @@ -0,0 +1,36 @@ +#ifndef SLICER_DB_SQLBINDER_H +#define SLICER_DB_SQLBINDER_H + +#include +#include +#include + +namespace Slicer { + class SqlBinder : public Slicer::ValueTarget, + public Slicer::TValueTarget, + public Slicer::TValueTarget + { + public: + SqlBinder(DB::Command & c, unsigned int idx); + + void get(const boost::posix_time::ptime & b) const override; + void get(const boost::posix_time::time_duration & b) const override; + void get(const bool & b) const override; + void get(const Ice::Byte & b) const override; + void get(const Ice::Short & b) const override; + void get(const Ice::Int & b) const override; + void get(const Ice::Long & b) const override; + void get(const Ice::Float & b) const override; + void get(const Ice::Double & b) const override; + void get(const std::string & b) const override; + + private: + DB::Command & command; + const unsigned int idx; + }; + typedef IceUtil::Handle SqlBinderPtr; +} + +#endif + + diff --git a/slicer/db/sqlInsertSerializer.cpp b/slicer/db/sqlInsertSerializer.cpp new file mode 100644 index 0000000..2f8e88d --- /dev/null +++ b/slicer/db/sqlInsertSerializer.cpp @@ -0,0 +1,76 @@ +#include "sqlInsertSerializer.h" +#include "exceptions.h" +#include "sqlBinder.h" +#include +#include + +namespace Slicer { + SqlInsertSerializer::SqlInsertSerializer(DB::Connection * c, const std::string & t) : + connection(c), + tableName(t) + { + } + + void + SqlInsertSerializer::Serialize(Slicer::ModelPartPtr mp) + { + switch (mp->GetType()) { + case Slicer::mpt_Sequence: + mp->OnEachChild(boost::bind(&SqlInsertSerializer::SerializeSequence, this, _2)); + return; + case Slicer::mpt_Complex: + mp->OnEachChild(boost::bind(&SqlInsertSerializer::SerializeObject, this, _2)); + return; + default: + throw UnsupportedModelType(); + } + } + + void + SqlInsertSerializer::SerializeObject(Slicer::ModelPartPtr mp) const + { + auto ins = createInsert(mp); + int paramNo = 0; + mp->OnEachChild([&ins, ¶mNo](const std::string &, ModelPartPtr cmp, HookCommonPtr) { + cmp->GetValue(new SqlBinder(*ins, paramNo++)); + }); + ins->execute(); + } + + void + SqlInsertSerializer::SerializeSequence(Slicer::ModelPartPtr mp) const + { + ModifyPtr ins; + mp->OnEachChild([&ins, this](const std::string &, ModelPartPtr cmp, HookCommonPtr) { + if (!ins) { + ins = createInsert(cmp); + } + int paramNo = 0; + cmp->OnEachChild([&ins, ¶mNo](const std::string &, ModelPartPtr cmp, HookCommonPtr) { + cmp->GetValue(new SqlBinder(*ins, paramNo++)); + }); + ins->execute(); + }); + } + + SqlInsertSerializer::ModifyPtr + SqlInsertSerializer::createInsert(Slicer::ModelPartPtr mp) const + { + AdHoc::Buffer insert; + insert.appendbf("INSERT INTO %s(", tableName); + int fieldNo = 0; + mp->OnEachChild([&insert, &fieldNo]( const std::string & name, ModelPartPtr, HookCommonPtr) { + if (fieldNo++) { + insert.append(", "); + } + insert.append(name); + }); + insert.append(") VALUES (", AdHoc::Buffer::Use); + for (; fieldNo > 1; --fieldNo) { + insert.append("?, "); + } + insert.append("?)"); + return ModifyPtr(connection->newModifyCommand(insert)); + } +} + diff --git a/slicer/db/sqlInsertSerializer.h b/slicer/db/sqlInsertSerializer.h new file mode 100644 index 0000000..9163f47 --- /dev/null +++ b/slicer/db/sqlInsertSerializer.h @@ -0,0 +1,28 @@ +#ifndef SLICER_DB_SQLINSERTSERIALIZER_H +#define SLICER_DB_SQLINSERTSERIALIZER_H + +#include +#include +#include + +namespace Slicer { + class DLL_PUBLIC SqlInsertSerializer : public Slicer::Serializer { + public: + typedef boost::shared_ptr ModifyPtr; + + SqlInsertSerializer(DB::Connection *, const std::string & tableName); + + virtual void Serialize(Slicer::ModelPartPtr) override; + + protected: + void SerializeObject(Slicer::ModelPartPtr) const; + void SerializeSequence(Slicer::ModelPartPtr) const; + ModifyPtr createInsert(Slicer::ModelPartPtr) const; + + DB::Connection * connection; + const std::string tableName; + }; +} + +#endif + diff --git a/slicer/db/testInsert.cpp b/slicer/db/testInsert.cpp new file mode 100644 index 0000000..9d7bf04 --- /dev/null +++ b/slicer/db/testInsert.cpp @@ -0,0 +1,61 @@ +#define BOOST_TEST_MODULE db_insert +#include +#include +#include +#include +#include +#include "sqlInsertSerializer.h" +#include "sqlSelectDeserializer.h" +#include + +class StandardMockDatabase : public PQ::Mock { + public: + StandardMockDatabase() : PQ::Mock("user=postgres dbname=postgres", "pqmock", { + rootDir / "slicer.sql" }) + { + } +}; + +BOOST_GLOBAL_FIXTURE( StandardMockDatabase ); + +typedef boost::shared_ptr DBPtr; +typedef boost::shared_ptr SelectPtr; + +BOOST_AUTO_TEST_CASE( insert_builtins ) +{ + auto db = DBPtr(DB::MockDatabase::openConnectionTo("pqmock")); + TestModule::BuiltInsPtr bi = new TestModule::BuiltIns(true, 4, 16, 64, 128, 1.2, 3.4, "text"); + Slicer::SerializeAny(bi, db.get(), "builtins"); + auto sel = SelectPtr(db->newSelectCommand("SELECT * FROM builtins")); + auto bi2 = Slicer::DeserializeAny(*sel); + BOOST_REQUIRE_EQUAL(bi->mbool, bi2->mbool); + BOOST_REQUIRE_EQUAL(bi->mbyte, bi2->mbyte); + BOOST_REQUIRE_EQUAL(bi->mshort, bi2->mshort); + BOOST_REQUIRE_EQUAL(bi->mint, bi2->mint); + BOOST_REQUIRE_EQUAL(bi->mlong, bi2->mlong); + BOOST_REQUIRE_EQUAL(bi->mfloat, bi2->mfloat); + BOOST_REQUIRE_EQUAL(bi->mdouble, bi2->mdouble); + BOOST_REQUIRE_EQUAL(bi->mstring, bi2->mstring); +} + +BOOST_AUTO_TEST_CASE( insert_seq_builtins ) +{ + auto db = DBPtr(DB::MockDatabase::openConnectionTo("pqmock")); + TestModule::BuiltInSeq bis = { + TestModule::BuiltInsPtr(new TestModule::BuiltIns(true, 5, 17, 65, 129, 2.3, 4.5, "more text")), + TestModule::BuiltInsPtr(new TestModule::BuiltIns(true, 6, 18, 66, 130, 3.4, 5.6, "even more text")) + }; + Slicer::SerializeAny(bis, db.get(), "builtins"); + auto sel = SelectPtr(db->newSelectCommand("SELECT * FROM builtins ORDER BY mint")); + auto bis2 = Slicer::DeserializeAny(*sel); + BOOST_REQUIRE_EQUAL(3, bis2.size()); + BOOST_REQUIRE_EQUAL(bis.back()->mbool, bis2.back()->mbool); + BOOST_REQUIRE_EQUAL(bis.back()->mbyte, bis2.back()->mbyte); + BOOST_REQUIRE_EQUAL(bis.back()->mshort, bis2.back()->mshort); + BOOST_REQUIRE_EQUAL(bis.back()->mint, bis2.back()->mint); + BOOST_REQUIRE_EQUAL(bis.back()->mlong, bis2.back()->mlong); + BOOST_REQUIRE_EQUAL(bis.back()->mfloat, bis2.back()->mfloat); + BOOST_REQUIRE_EQUAL(bis.back()->mdouble, bis2.back()->mdouble); + BOOST_REQUIRE_EQUAL(bis.back()->mstring, bis2.back()->mstring); +} + diff --git a/slicer/test/preprocessor.cpp b/slicer/test/preprocessor.cpp index bef901e..9a02456 100644 --- a/slicer/test/preprocessor.cpp +++ b/slicer/test/preprocessor.cpp @@ -13,7 +13,7 @@ namespace fs = boost::filesystem; -const unsigned int COMPONENTS_IN_TEST_ICE = 32; +const unsigned int COMPONENTS_IN_TEST_ICE = 33; BOOST_FIXTURE_TEST_SUITE ( preprocessor, FileStructure ); diff --git a/slicer/test/types.ice b/slicer/test/types.ice index 73da2a0..bdb39b2 100644 --- a/slicer/test/types.ice +++ b/slicer/test/types.ice @@ -44,6 +44,7 @@ module TestModule { int a; int b; }; + sequence BuiltInSeq; sequence Classes; sequence Structs; dictionary ClassMap; -- cgit v1.2.3