diff options
| author | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-10-17 20:33:50 +0100 | 
|---|---|---|
| committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2015-10-17 20:33:50 +0100 | 
| commit | 9b3ad688a611852c1086fda8a082d4f174b980f7 (patch) | |
| tree | 836db5db38383d35b0f31df65e945c7deb6b8ff9 | |
| parent | Dedupe insert bind and execute (diff) | |
| download | slicer-9b3ad688a611852c1086fda8a082d4f174b980f7.tar.bz2 slicer-9b3ad688a611852c1086fda8a082d4f174b980f7.tar.xz slicer-9b3ad688a611852c1086fda8a082d4f174b980f7.zip | |
Add SQL update serializer
| -rw-r--r-- | slicer/db/Jamfile.jam | 19 | ||||
| -rw-r--r-- | slicer/db/sqlUpdateSerializer.cpp | 99 | ||||
| -rw-r--r-- | slicer/db/sqlUpdateSerializer.h | 34 | ||||
| -rw-r--r-- | slicer/db/testUpdate.cpp | 107 | ||||
| -rw-r--r-- | slicer/test/types.ice | 2 | 
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, ¶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 <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; | 
