From aad6e2aac27dbde485137a93340336da198a542f Mon Sep 17 00:00:00 2001 From: Dan Goodliffe Date: Tue, 18 Oct 2016 19:26:08 +0100 Subject: Add wrappers for binding anything that can be optional (pointer or optional type) --- libdbpp/command.cpp | 19 ++++++++ libdbpp/command.h | 16 +++++++ libdbpp/unittests/Jamfile.jam | 2 + libdbpp/unittests/testUtils.cpp | 102 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 139 insertions(+) diff --git a/libdbpp/command.cpp b/libdbpp/command.cpp index abbb53f..97a6518 100644 --- a/libdbpp/command.cpp +++ b/libdbpp/command.cpp @@ -17,3 +17,22 @@ DB::ParameterOutOfRange::ParameterOutOfRange() { } +void +DB::Command::bindParamS(unsigned int i, const char * const o) +{ + if (o) + bindParamS(i, Glib::ustring(o)); + else + bindNull(i); +} + + +void +DB::Command::bindParamS(unsigned int i, char * const o) +{ + if (o) + bindParamS(i, Glib::ustring(o)); + else + bindNull(i); +} + diff --git a/libdbpp/command.h b/libdbpp/command.h index 3305342..ae18158 100644 --- a/libdbpp/command.h +++ b/libdbpp/command.h @@ -61,6 +61,22 @@ namespace DB { /// The SQL statement. const std::string sql; + +#define OPTWRAPPER(func) \ + template \ + inline void func(unsigned int i, const O & o) { \ + if (o) \ + func(i, *o); \ + else \ + bindNull(i); \ + } + OPTWRAPPER(bindParamI); + OPTWRAPPER(bindParamF); + OPTWRAPPER(bindParamS); + OPTWRAPPER(bindParamB); + OPTWRAPPER(bindParamT); + void bindParamS(unsigned int, const char * const); + void bindParamS(unsigned int, char * const); }; typedef boost::shared_ptr CommandPtr; } diff --git a/libdbpp/unittests/Jamfile.jam b/libdbpp/unittests/Jamfile.jam index 08bd027..93b26b3 100644 --- a/libdbpp/unittests/Jamfile.jam +++ b/libdbpp/unittests/Jamfile.jam @@ -3,6 +3,7 @@ import testing ; path-constant me : . ; lib boost_utf : : boost_unit_test_framework ; +lib IceUtil ; run testConnection.cpp @@ -62,6 +63,7 @@ run ..//adhocutil ../../libpqpp//dbpp-postgresql boost_utf + IceUtil util.sql : testUtils diff --git a/libdbpp/unittests/testUtils.cpp b/libdbpp/unittests/testUtils.cpp index ac9b7c1..cb660cc 100644 --- a/libdbpp/unittests/testUtils.cpp +++ b/libdbpp/unittests/testUtils.cpp @@ -3,11 +3,14 @@ #include #include +#include #include #include #include #include #include +#include +#include class StandardMockDatabase : public PQ::Mock { public: @@ -148,3 +151,102 @@ BOOST_AUTO_TEST_CASE( bulkLoadFile ) }); } +BOOST_AUTO_TEST_CASE( nullBind ) +{ + auto db = DB::ConnectionPtr(DB::MockDatabase::openConnectionTo("pqmock")); + auto ins = db->modify("INSERT INTO forEachRow VALUES(?, ?, ?, ?, ?, ?)"); + ins->bindParamI(0, boost::optional()); + ins->bindParamF(1, boost::optional()); + ins->bindParamS(2, boost::optional()); + ins->bindParamT(3, boost::optional()); + ins->bindParamT(4, boost::optional()); + ins->bindParamB(5, boost::optional()); + ins->execute(); + auto sel = DB::SelectCommandPtr(db->newSelectCommand("SELECT a, b, c, d, e, f FROM forEachRow WHERE a IS NULL AND b IS NULL AND c IS NULL AND d IS NULL AND e IS NULL AND f IS NULL")); + unsigned int count = 0; + for (const auto & row : sel->as<>()) { + (void)row; + count += 1; + } + BOOST_REQUIRE_EQUAL(1, count); +} + +BOOST_AUTO_TEST_CASE( iceNullBind ) +{ + auto db = DB::ConnectionPtr(DB::MockDatabase::openConnectionTo("pqmock")); + auto ins = db->modify("INSERT INTO forEachRow VALUES(?, ?, ?, ?, ?, ?)"); + ins->bindParamI(0, IceUtil::Optional()); + ins->bindParamF(1, IceUtil::Optional()); + ins->bindParamS(2, IceUtil::Optional()); + ins->bindParamT(3, IceUtil::Optional()); + ins->bindParamT(4, IceUtil::Optional()); + ins->bindParamB(5, IceUtil::Optional()); + ins->execute(); + auto sel = DB::SelectCommandPtr(db->newSelectCommand("SELECT a, b, c, d, e, f FROM forEachRow WHERE a IS NULL AND b IS NULL AND c IS NULL AND d IS NULL AND e IS NULL AND f IS NULL")); + unsigned int count = 0; + for (const auto & row : sel->as<>()) { + (void)row; + count += 1; + } + BOOST_REQUIRE_EQUAL(2, count); +} + +BOOST_AUTO_TEST_CASE( charStarBindNull ) +{ + auto db = DB::ConnectionPtr(DB::MockDatabase::openConnectionTo("pqmock")); + db->modify("DELETE FROM forEachRow")->execute(); + auto ins = db->modify("INSERT INTO forEachRow(a, c) VALUES(?, ?)"); + char * cs = NULL; + char * cs2 = strdup("a thing"); + ins->bindParamS(0, cs); + ins->bindParamS(1, cs2); + ins->execute(); + const char * ccs = cs; + const char * ccs2 = cs2; + ins->bindParamS(0, ccs); + ins->bindParamS(1, ccs2); + ins->execute(); + const char * const ccsc = ccs; + const char * const ccsc2 = ccs2; + ins->bindParamS(0, ccsc); + ins->bindParamS(1, ccsc2); + ins->execute(); + free(cs2); + auto sel = DB::SelectCommandPtr(db->newSelectCommand("SELECT a, c FROM forEachRow")); + for (const auto & row : sel->as, boost::optional>()) { + BOOST_REQUIRE(row[0].isNull()); + BOOST_REQUIRE(!row[1].isNull()); + } +} + +BOOST_AUTO_TEST_CASE( bindIntPtr ) +{ + auto db = DB::ConnectionPtr(DB::MockDatabase::openConnectionTo("pqmock")); + db->modify("DELETE FROM forEachRow")->execute(); + auto ins = db->modify("INSERT INTO forEachRow(a, b) VALUES(?, ?)"); + int * is = NULL; + int * is2 = new int(53); + ins->bindParamI(0, is); + ins->bindParamI(1, is2); + ins->execute(); + const int * cis = is; + const int * cis2 = is2; + ins->bindParamI(0, cis); + ins->bindParamI(1, cis2); + ins->execute(); + const int * const cisc = cis; + const int * const cisc2 = cis2; + ins->bindParamI(0, cisc); + ins->bindParamI(1, cisc2); + ins->execute(); + delete is2; + auto sel = DB::SelectCommandPtr(db->newSelectCommand("SELECT a, b FROM forEachRow")); + unsigned int total = 0; + for (const auto & row : sel->as, boost::optional>()) { + BOOST_REQUIRE(row[0].isNull()); + BOOST_REQUIRE(!row[1].isNull()); + total += *row.value<1>(); + } + BOOST_REQUIRE_EQUAL(159, total); +} + -- cgit v1.2.3