summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--slicer/db/Jamfile.jam19
-rw-r--r--slicer/db/sqlUpdateSerializer.cpp99
-rw-r--r--slicer/db/sqlUpdateSerializer.h34
-rw-r--r--slicer/db/testUpdate.cpp107
-rw-r--r--slicer/test/types.ice2
5 files changed, 261 insertions, 0 deletions
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
+ : : :
+ <define>ROOT=\"$(me)\"
+ <define>BOOST_TEST_DYN_LINK
+ <library>slicer-db
+ <library>dbpp-postgresql
+ <library>boost_system
+ <library>boost_filesystem
+ <library>boost_utf
+ <library>../test//slicer-test
+ <library>../test//common
+ <library>../slicer//slicer
+ <include>..
+ <dependency>slicer.sql
+ <dependency>../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 <buffer.h>
+#include <modifycommand.h>
+#include <slicer/metadata.h>
+
+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, &paramNo](const std::string &, ModelPartPtr cmp, HookCommonPtr h) {
+ if (metaDataFlagNotSet(h->GetMetadata(), md_pkey)) {
+ cmp->GetValue(new SqlBinder(*upd, paramNo++));
+ }
+ });
+ cmp->OnEachChild([&upd, &paramNo](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 <slicer/serializer.h>
+#include <connection.h>
+#include <visibility.h>
+
+namespace Slicer {
+ class NoRowsFound : public std::runtime_error {
+ public:
+ NoRowsFound();
+ };
+
+ class DLL_PUBLIC SqlUpdateSerializer : public Slicer::Serializer {
+ public:
+ typedef boost::shared_ptr<DB::ModifyCommand> 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 <boost/test/unit_test.hpp>
+#include <boost/date_time/posix_time/posix_time_io.hpp>
+#include <mock.h>
+#include <slicer/slicer.h>
+#include <definedDirs.h>
+#include "sqlInsertSerializer.h"
+#include "sqlSelectDeserializer.h"
+#include "sqlUpdateSerializer.h"
+#include <types.h>
+#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<DB::Connection> DBPtr;
+typedef boost::shared_ptr<DB::SelectCommand> 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<Slicer::SqlUpdateSerializer>(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<Slicer::SqlInsertSerializer>(bi1, db.get(), "builtins");
+ Slicer::SerializeAny<Slicer::SqlInsertSerializer>(bi2, db.get(), "builtins");
+
+ TestModule::BuiltInsPtr ubi = new TestModule::BuiltIns(false, 5, 17, 64, 128, -1.2, -1.4, "string");
+ Slicer::SerializeAny<Slicer::SqlUpdateSerializer>(ubi, db.get(), "builtins");
+
+ auto sel = SelectPtr(db->newSelectCommand("SELECT * FROM builtins ORDER BY mint DESC"));
+ auto bis = Slicer::DeserializeAny<Slicer::SqlSelectDeserializer, TestModule::BuiltInSeq>(*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<Slicer::SqlInsertSerializer>(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<Slicer::SqlUpdateSerializer>(ubis, db.get(), "builtins");
+
+ auto sel = SelectPtr(db->newSelectCommand("SELECT * FROM builtins ORDER BY mint"));
+ auto ubis2 = Slicer::DeserializeAny<Slicer::SqlSelectDeserializer, TestModule::BuiltInSeq>(*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<Slicer::SqlUpdateSerializer>(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;