From 4c8f043f65a29bbd4ca743398950aea0126343e3 Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Sun, 18 Oct 2015 01:33:49 +0100 Subject: Add insert serializers that don't insert fields marked as auto and optionally retrieve the value applied by the database back into the model --- slicer/db/slicer.sql | 2 +- slicer/db/sqlInsertSerializer.cpp | 89 ++++++++++++++++++++++++++++++++++----- slicer/db/sqlInsertSerializer.h | 24 ++++++++++- slicer/db/testInsert.cpp | 48 +++++++++++++++++++++ slicer/test/types.ice | 3 +- 5 files changed, 153 insertions(+), 13 deletions(-) diff --git a/slicer/db/slicer.sql b/slicer/db/slicer.sql index e526db3..9d649bb 100644 --- a/slicer/db/slicer.sql +++ b/slicer/db/slicer.sql @@ -15,7 +15,7 @@ CREATE TABLE builtins( mbool boolean, mbyte smallint, mshort smallint, - mint int, + mint serial, mlong bigint, mfloat numeric(5, 2), mdouble numeric(8, 5), diff --git a/slicer/db/sqlInsertSerializer.cpp b/slicer/db/sqlInsertSerializer.cpp index eccc083..e8d44f5 100644 --- a/slicer/db/sqlInsertSerializer.cpp +++ b/slicer/db/sqlInsertSerializer.cpp @@ -3,8 +3,12 @@ #include "sqlBinder.h" #include #include +#include +#include namespace Slicer { + const std::string md_auto = "db:auto"; + SqlInsertSerializer::SqlInsertSerializer(DB::Connection * c, const std::string & t) : connection(c), tableName(t) @@ -46,27 +50,72 @@ namespace Slicer { } void - SqlInsertSerializer::bindObjectAndExecute(Slicer::ModelPartPtr cmp, DB::ModifyCommand * ins) + SqlInsertSerializer::bindObjectAndExecute(Slicer::ModelPartPtr cmp, DB::ModifyCommand * ins) const { int paramNo = 0; - cmp->OnEachChild([&ins, ¶mNo](const std::string &, ModelPartPtr cmp, HookCommonPtr) { - cmp->GetValue(new SqlBinder(*ins, paramNo++)); - }); + cmp->OnEachChild(boost::bind(&SqlInsertSerializer::bindObjectAndExecuteField, this, boost::ref(paramNo), ins, _2, _3)); ins->execute(); } + class IdSave : public Slicer::ValueSource { + public: + IdSave(DB::Connection * c) : + connection(c) + { + } + +#define NonNumType(T) \ + void set(T &) const override { throw std::runtime_error("Can't store Id in " #T " type field"); } + +#define NumType(T) \ + void set(T & v) const override { v = boost::numeric_cast(connection->insertId()); } + + NonNumType(bool); + NonNumType(std::string); + + NumType(Ice::Byte); + NumType(Ice::Short); + NumType(Ice::Int); + NumType(Ice::Long); + NumType(Ice::Float); + NumType(Ice::Double); + + private: + DB::Connection * connection; + }; + + void + SqlFetchIdInsertSerializer::bindObjectAndExecute(Slicer::ModelPartPtr cmp, DB::ModifyCommand * ins) const + { + SqlAutoIdInsertSerializer::bindObjectAndExecute(cmp, ins); + cmp->OnEachChild([&ins, this](const std::string &, ModelPartPtr cmp, HookCommonPtr h) { + if (metaDataFlagSet(h->GetMetadata(), md_auto)) { + cmp->SetValue(new IdSave(connection)); + } + }); + } + + void + SqlInsertSerializer::bindObjectAndExecuteField(int & paramNo, DB::ModifyCommand * ins, Slicer::ModelPartPtr cmp, HookCommonPtr) const + { + cmp->GetValue(new SqlBinder(*ins, paramNo++)); + } + + void + SqlAutoIdInsertSerializer::bindObjectAndExecuteField(int & paramNo, DB::ModifyCommand * ins, Slicer::ModelPartPtr cmp, HookCommonPtr h) const + { + if (metaDataFlagNotSet(h->GetMetadata(), md_auto)) { + cmp->GetValue(new SqlBinder(*ins, paramNo++)); + } + } + 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); - }); + mp->OnEachChild(boost::bind(&SqlInsertSerializer::createInsertField, this, boost::ref(fieldNo), boost::ref(insert), _1, _3)); insert.append(") VALUES (", AdHoc::Buffer::Use); for (; fieldNo > 1; --fieldNo) { insert.append("?, "); @@ -74,5 +123,25 @@ namespace Slicer { insert.append("?)"); return ModifyPtr(connection->newModifyCommand(insert)); } + + void + SqlInsertSerializer::createInsertField(int & fieldNo, AdHoc::Buffer & insert, const std::string & name, HookCommonPtr) const + { + if (fieldNo++) { + insert.append(", "); + } + insert.append(name); + } + + void + SqlAutoIdInsertSerializer::createInsertField(int & fieldNo, AdHoc::Buffer & insert, const std::string & name, HookCommonPtr h) const + { + if (metaDataFlagNotSet(h->GetMetadata(), md_auto)) { + if (fieldNo++) { + insert.append(", "); + } + insert.append(name); + } + } } diff --git a/slicer/db/sqlInsertSerializer.h b/slicer/db/sqlInsertSerializer.h index 843be93..359c655 100644 --- a/slicer/db/sqlInsertSerializer.h +++ b/slicer/db/sqlInsertSerializer.h @@ -4,6 +4,7 @@ #include #include #include +#include namespace Slicer { class DLL_PUBLIC SqlInsertSerializer : public Slicer::Serializer { @@ -18,11 +19,32 @@ namespace Slicer { void SerializeObject(Slicer::ModelPartPtr) const; void SerializeSequence(Slicer::ModelPartPtr) const; ModifyPtr createInsert(Slicer::ModelPartPtr) const; - static void bindObjectAndExecute(Slicer::ModelPartPtr, DB::ModifyCommand *); + virtual void createInsertField(int & fieldNo, AdHoc::Buffer & insert, const std::string & name, HookCommonPtr h) const; + virtual void bindObjectAndExecute(Slicer::ModelPartPtr, DB::ModifyCommand *) const; + virtual void bindObjectAndExecuteField(int & paramNo, DB::ModifyCommand *, Slicer::ModelPartPtr, HookCommonPtr) const; DB::Connection * connection; const std::string tableName; }; + + class DLL_PUBLIC SqlAutoIdInsertSerializer : public SqlInsertSerializer { + public: + template + SqlAutoIdInsertSerializer(const P & ... p) : SqlInsertSerializer(p...) { } + + protected: + virtual void createInsertField(int & fieldNo, AdHoc::Buffer & insert, const std::string & name, HookCommonPtr h) const; + virtual void bindObjectAndExecuteField(int & paramNo, DB::ModifyCommand *, Slicer::ModelPartPtr, HookCommonPtr) const; + }; + + class DLL_PUBLIC SqlFetchIdInsertSerializer : public SqlAutoIdInsertSerializer { + public: + template + SqlFetchIdInsertSerializer(const P & ... p) : SqlAutoIdInsertSerializer(p...) { } + + protected: + virtual void bindObjectAndExecute(Slicer::ModelPartPtr, DB::ModifyCommand *) const; + }; } #endif diff --git a/slicer/db/testInsert.cpp b/slicer/db/testInsert.cpp index 2a167c0..74ad537 100644 --- a/slicer/db/testInsert.cpp +++ b/slicer/db/testInsert.cpp @@ -66,6 +66,54 @@ BOOST_AUTO_TEST_CASE( insert_seq_builtins ) BOOST_REQUIRE_EQUAL(bis.back()->mstring, bis2.back()->mstring); } +BOOST_AUTO_TEST_CASE( autoinsert_seq_builtins ) +{ + auto db = DBPtr(DB::MockDatabase::openConnectionTo("pqmock")); + TestModule::BuiltInSeq bis = { + TestModule::BuiltInsPtr(new TestModule::BuiltIns(true, 5, 17, 0, 129, 2.3, 4.5, "more text")), + TestModule::BuiltInsPtr(new TestModule::BuiltIns(true, 6, 18, 0, 130, 3.4, 5.6, "even more text")) + }; + Slicer::SerializeAny(bis, db.get(), "builtins"); + auto sel = SelectPtr(db->newSelectCommand("SELECT * FROM builtins WHERE mint IN (1, 2) ORDER BY mint")); + auto bis2 = Slicer::DeserializeAny(*sel); + BOOST_REQUIRE_EQUAL(2, bis2.size()); + BOOST_REQUIRE_EQUAL(bis.front()->mint, 0); + BOOST_REQUIRE_EQUAL(bis.back()->mint, 0); + BOOST_REQUIRE_EQUAL(bis2.front()->mint, 1); + BOOST_REQUIRE_EQUAL(bis2.back()->mint, 2); + 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()->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); +} + +BOOST_AUTO_TEST_CASE( fetchinsert_seq_builtins ) +{ + auto db = DBPtr(DB::MockDatabase::openConnectionTo("pqmock")); + TestModule::BuiltInSeq bis = { + TestModule::BuiltInsPtr(new TestModule::BuiltIns(true, 5, 17, 0, 129, 2.3, 4.5, "more text")), + TestModule::BuiltInsPtr(new TestModule::BuiltIns(true, 6, 18, 0, 130, 3.4, 5.6, "even more text")) + }; + Slicer::SerializeAny(bis, db.get(), "builtins"); + auto sel = SelectPtr(db->newSelectCommand("SELECT * FROM builtins WHERE mint IN (3, 4) ORDER BY mint")); + auto bis2 = Slicer::DeserializeAny(*sel); + BOOST_REQUIRE_EQUAL(2, bis2.size()); + BOOST_REQUIRE_EQUAL(bis.front()->mint, 3); + BOOST_REQUIRE_EQUAL(bis.back()->mint, 4); + BOOST_REQUIRE_EQUAL(bis2.front()->mint, 3); + BOOST_REQUIRE_EQUAL(bis2.back()->mint, 4); + 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()->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); +} + BOOST_AUTO_TEST_CASE( insert_converted ) { auto db = DBPtr(DB::MockDatabase::openConnectionTo("pqmock")); diff --git a/slicer/test/types.ice b/slicer/test/types.ice index 4cd6531..41637a0 100644 --- a/slicer/test/types.ice +++ b/slicer/test/types.ice @@ -30,7 +30,8 @@ module TestModule { bool mbool; byte mbyte; short mshort; - ["slicer:db:pkey"] + ["slicer:db:pkey", + "slicer:db:auto"] int mint; ["slicer:db:pkey"] long mlong; -- cgit v1.2.3