From 9b3ad688a611852c1086fda8a082d4f174b980f7 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sat, 17 Oct 2015 20:33:50 +0100 Subject: Add SQL update serializer --- slicer/db/Jamfile.jam | 19 +++++++ slicer/db/sqlUpdateSerializer.cpp | 99 +++++++++++++++++++++++++++++++++++ slicer/db/sqlUpdateSerializer.h | 34 ++++++++++++ slicer/db/testUpdate.cpp | 107 ++++++++++++++++++++++++++++++++++++++ slicer/test/types.ice | 2 + 5 files changed, 261 insertions(+) create mode 100644 slicer/db/sqlUpdateSerializer.cpp create mode 100644 slicer/db/sqlUpdateSerializer.h create mode 100644 slicer/db/testUpdate.cpp diff --git a/slicer/db/Jamfile.jam b/slicer/db/Jamfile.jam index a6a94e8..cbe7927 100644 --- a/slicer/db/Jamfile.jam +++ b/slicer/db/Jamfile.jam @@ -68,3 +68,22 @@ run testInsert.cpp testInsert ; +run testUpdate.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 + : + testUpdate + ; + diff --git a/slicer/db/sqlUpdateSerializer.cpp b/slicer/db/sqlUpdateSerializer.cpp new file mode 100644 index 0000000..75cbaae --- /dev/null +++ b/slicer/db/sqlUpdateSerializer.cpp @@ -0,0 +1,99 @@ +#include "sqlUpdateSerializer.h" +#include "exceptions.h" +#include "sqlBinder.h" +#include +#include +#include + +namespace Slicer { + NoRowsFound::NoRowsFound() : std::runtime_error("No rows found") { } + + const std::string md_pkey = "db:pkey"; + + SqlUpdateSerializer::SqlUpdateSerializer(DB::Connection * c, const std::string & t) : + connection(c), + tableName(t) + { + } + + void + SqlUpdateSerializer::Serialize(Slicer::ModelPartPtr mp) + { + switch (mp->GetType()) { + case Slicer::mpt_Sequence: + mp->OnEachChild(boost::bind(&SqlUpdateSerializer::SerializeSequence, this, _2)); + return; + case Slicer::mpt_Complex: + mp->OnEachChild(boost::bind(&SqlUpdateSerializer::SerializeObject, this, _2)); + return; + default: + throw UnsupportedModelType(); + } + } + + void + SqlUpdateSerializer::SerializeObject(Slicer::ModelPartPtr mp) const + { + auto ins = createUpdate(mp); + bindObjectAndExecute(mp, ins.get()); + } + + void + SqlUpdateSerializer::SerializeSequence(Slicer::ModelPartPtr mp) const + { + ModifyPtr ins; + mp->OnEachChild([&ins, this](const std::string &, ModelPartPtr cmp, HookCommonPtr) { + if (!ins) { + ins = createUpdate(cmp); + } + bindObjectAndExecute(cmp, ins.get()); + }); + } + + void + SqlUpdateSerializer::bindObjectAndExecute(Slicer::ModelPartPtr cmp, DB::ModifyCommand * upd) + { + int paramNo = 0; + cmp->OnEachChild([&upd, ¶mNo](const std::string &, ModelPartPtr cmp, HookCommonPtr h) { + if (metaDataFlagNotSet(h->GetMetadata(), md_pkey)) { + cmp->GetValue(new SqlBinder(*upd, paramNo++)); + } + }); + cmp->OnEachChild([&upd, ¶mNo](const std::string &, ModelPartPtr cmp, HookCommonPtr h) { + if (metaDataFlagSet(h->GetMetadata(), md_pkey)) { + cmp->GetValue(new SqlBinder(*upd, paramNo++)); + } + }); + if (upd->execute() == 0) { + throw NoRowsFound(); + } + } + + SqlUpdateSerializer::ModifyPtr + SqlUpdateSerializer::createUpdate(Slicer::ModelPartPtr mp) const + { + AdHoc::Buffer update; + update.appendbf("UPDATE %s SET ", tableName); + int fieldNo = 0; + mp->OnEachChild([&update, &fieldNo]( const std::string & name, ModelPartPtr, HookCommonPtr h) { + if (metaDataFlagNotSet(h->GetMetadata(), md_pkey)) { + if (fieldNo++) { + update.append(", "); + } + update.appendbf("%s = ?", name); + } + }); + update.append(" WHERE ", AdHoc::Buffer::Use); + fieldNo = 0; + mp->OnEachChild([&update, &fieldNo]( const std::string & name, ModelPartPtr, HookCommonPtr h) { + if (metaDataFlagSet(h->GetMetadata(), md_pkey)) { + if (fieldNo++) { + update.append(" AND "); + } + update.appendbf("%s = ?", name); + } + }); + return ModifyPtr(connection->newModifyCommand(update)); + } +} + diff --git a/slicer/db/sqlUpdateSerializer.h b/slicer/db/sqlUpdateSerializer.h new file mode 100644 index 0000000..6d9ef04 --- /dev/null +++ b/slicer/db/sqlUpdateSerializer.h @@ -0,0 +1,34 @@ +#ifndef SLICER_DB_SQLUPDATESERIALIZER_H +#define SLICER_DB_SQLUPDATESERIALIZER_H + +#include +#include +#include + +namespace Slicer { + class NoRowsFound : public std::runtime_error { + public: + NoRowsFound(); + }; + + class DLL_PUBLIC SqlUpdateSerializer : public Slicer::Serializer { + public: + typedef boost::shared_ptr ModifyPtr; + + SqlUpdateSerializer(DB::Connection *, const std::string & tableName); + + virtual void Serialize(Slicer::ModelPartPtr) override; + + protected: + void SerializeObject(Slicer::ModelPartPtr) const; + void SerializeSequence(Slicer::ModelPartPtr) const; + ModifyPtr createUpdate(Slicer::ModelPartPtr) const; + static void bindObjectAndExecute(Slicer::ModelPartPtr, DB::ModifyCommand *); + + DB::Connection * connection; + const std::string tableName; + }; +} + +#endif + diff --git a/slicer/db/testUpdate.cpp b/slicer/db/testUpdate.cpp new file mode 100644 index 0000000..518021c --- /dev/null +++ b/slicer/db/testUpdate.cpp @@ -0,0 +1,107 @@ +#define BOOST_TEST_MODULE db_insert +#include +#include +#include +#include +#include +#include "sqlInsertSerializer.h" +#include "sqlSelectDeserializer.h" +#include "sqlUpdateSerializer.h" +#include +#include "exceptions.h" + +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( update_builtinsNotFound ) +{ + auto db = DBPtr(DB::MockDatabase::openConnectionTo("pqmock")); + TestModule::BuiltInsPtr ubi = new TestModule::BuiltIns(false, 5, 17, 64, 129, -1.2, -1.4, "string"); + BOOST_REQUIRE_THROW(Slicer::SerializeAny(ubi, db.get(), "builtins"), Slicer::NoRowsFound); +} + +BOOST_AUTO_TEST_CASE( update_builtins ) +{ + auto db = DBPtr(DB::MockDatabase::openConnectionTo("pqmock")); + TestModule::BuiltInsPtr bi1 = new TestModule::BuiltIns(true, 4, 16, 64, 128, 1.2, 3.4, "text1"); + TestModule::BuiltInsPtr bi2 = new TestModule::BuiltIns(true, 3, 15, 63, 127, 5.2, 5.4, "text2"); + Slicer::SerializeAny(bi1, db.get(), "builtins"); + Slicer::SerializeAny(bi2, db.get(), "builtins"); + + TestModule::BuiltInsPtr ubi = new TestModule::BuiltIns(false, 5, 17, 64, 128, -1.2, -1.4, "string"); + Slicer::SerializeAny(ubi, db.get(), "builtins"); + + auto sel = SelectPtr(db->newSelectCommand("SELECT * FROM builtins ORDER BY mint DESC")); + auto bis = Slicer::DeserializeAny(*sel); + BOOST_REQUIRE_EQUAL(2, bis.size()); + BOOST_REQUIRE_EQUAL(bis.front()->mbool, ubi->mbool); + BOOST_REQUIRE_EQUAL(bis.front()->mbyte, ubi->mbyte); + BOOST_REQUIRE_EQUAL(bis.front()->mshort, ubi->mshort); + BOOST_REQUIRE_EQUAL(bis.front()->mint, ubi->mint); + BOOST_REQUIRE_EQUAL(bis.front()->mlong, ubi->mlong); + BOOST_REQUIRE_EQUAL(bis.front()->mfloat, ubi->mfloat); + BOOST_REQUIRE_EQUAL(bis.front()->mdouble, ubi->mdouble); + BOOST_REQUIRE_EQUAL(bis.front()->mstring, ubi->mstring); + BOOST_REQUIRE_EQUAL(bis.back()->mbool, bi2->mbool); + BOOST_REQUIRE_EQUAL(bis.back()->mbyte, bi2->mbyte); + BOOST_REQUIRE_EQUAL(bis.back()->mshort, bi2->mshort); + BOOST_REQUIRE_EQUAL(bis.back()->mint, bi2->mint); + BOOST_REQUIRE_EQUAL(bis.back()->mlong, bi2->mlong); + BOOST_REQUIRE_EQUAL(bis.back()->mfloat, bi2->mfloat); + BOOST_REQUIRE_EQUAL(bis.back()->mdouble, bi2->mdouble); + BOOST_REQUIRE_EQUAL(bis.back()->mstring, bi2->mstring); +} + +BOOST_AUTO_TEST_CASE( update_builtins_seq ) +{ + auto db = DBPtr(DB::MockDatabase::openConnectionTo("pqmock")); + TestModule::BuiltInSeq bis { + TestModule::BuiltInsPtr(new TestModule::BuiltIns(true, 4, 16, 64, 128, 1.2, 3.4, "text1")), + TestModule::BuiltInsPtr(new TestModule::BuiltIns(true, 3, 15, 63, 127, 5.2, 5.4, "text2")), + }; + Slicer::SerializeAny(bis, db.get(), "builtins"); + + TestModule::BuiltInSeq ubis { + TestModule::BuiltInsPtr(new TestModule::BuiltIns(false, 5, 17, 64, 128, -1.2, -1.4, "string")), + TestModule::BuiltInsPtr(new TestModule::BuiltIns(false, 5, 21, 63, 127, -4.2, -5.4, "string updated")) + }; + Slicer::SerializeAny(ubis, db.get(), "builtins"); + + auto sel = SelectPtr(db->newSelectCommand("SELECT * FROM builtins ORDER BY mint")); + auto ubis2 = Slicer::DeserializeAny(*sel); + BOOST_REQUIRE_EQUAL(2, bis.size()); + BOOST_REQUIRE_EQUAL(ubis.front()->mbool, ubis2.back()->mbool); + BOOST_REQUIRE_EQUAL(ubis.front()->mbyte, ubis2.back()->mbyte); + BOOST_REQUIRE_EQUAL(ubis.front()->mshort, ubis2.back()->mshort); + BOOST_REQUIRE_EQUAL(ubis.front()->mint, ubis2.back()->mint); + BOOST_REQUIRE_EQUAL(ubis.front()->mlong, ubis2.back()->mlong); + BOOST_REQUIRE_EQUAL(ubis.front()->mfloat, ubis2.back()->mfloat); + BOOST_REQUIRE_EQUAL(ubis.front()->mdouble, ubis2.back()->mdouble); + BOOST_REQUIRE_EQUAL(ubis.front()->mstring, ubis2.back()->mstring); + BOOST_REQUIRE_EQUAL(ubis.back()->mbool, ubis2.front()->mbool); + BOOST_REQUIRE_EQUAL(ubis.back()->mbyte, ubis2.front()->mbyte); + BOOST_REQUIRE_EQUAL(ubis.back()->mshort, ubis2.front()->mshort); + BOOST_REQUIRE_EQUAL(ubis.back()->mint, ubis2.front()->mint); + BOOST_REQUIRE_EQUAL(ubis.back()->mlong, ubis2.front()->mlong); + BOOST_REQUIRE_EQUAL(ubis.back()->mfloat, ubis2.front()->mfloat); + BOOST_REQUIRE_EQUAL(ubis.back()->mdouble, ubis2.front()->mdouble); + BOOST_REQUIRE_EQUAL(ubis.back()->mstring, ubis2.front()->mstring); +} + +BOOST_AUTO_TEST_CASE( insert_unsupportedModel ) +{ + auto db = DBPtr(DB::MockDatabase::openConnectionTo("pqmock")); + TestModule::ClassMap cm; + BOOST_REQUIRE_THROW(Slicer::SerializeAny(cm, db.get(), "converted"), Slicer::UnsupportedModelType); +} + diff --git a/slicer/test/types.ice b/slicer/test/types.ice index bdb39b2..4cd6531 100644 --- a/slicer/test/types.ice +++ b/slicer/test/types.ice @@ -30,7 +30,9 @@ module TestModule { bool mbool; byte mbyte; short mshort; + ["slicer:db:pkey"] int mint; + ["slicer:db:pkey"] long mlong; float mfloat; double mdouble; -- cgit v1.2.3