diff options
-rw-r--r-- | libdbpp/column.cpp | 51 | ||||
-rw-r--r-- | libdbpp/column.h | 75 | ||||
-rw-r--r-- | libdbpp/error.h | 9 | ||||
-rw-r--r-- | libdbpp/unittests/testUtils.cpp | 13 |
4 files changed, 79 insertions, 69 deletions
diff --git a/libdbpp/column.cpp b/libdbpp/column.cpp index b660cd3..2028f03 100644 --- a/libdbpp/column.cpp +++ b/libdbpp/column.cpp @@ -22,53 +22,20 @@ InvalidConversion::message() const throw() return InvalidConversionMsg::get(from, to); } +UnexpectedNullValue::UnexpectedNullValue(const char * const t) : to(t) { } + +AdHocFormatter(UnexpectedNullValueMsg, "Unexpected null value in column expecting type (%?)"); +std::string +UnexpectedNullValue::message() const throw() +{ + return InvalidConversionMsg::get(to, to); +} + void DB::HandleField::blob(const DB::Blob &) { throw DB::ColumnTypeNotSupported(); } -template<typename T> -class Extract : public DB::HandleField { - public: - Extract(T & t) : target(t) { } - - void floatingpoint(double v) override { (*this)(v); } - void integer(int64_t v) override { (*this)(v); } - void boolean(bool v) override { (*this)(v); } - void string(const char * v, size_t len) override { (*this)(std::string(v, len)); } - void timestamp(const boost::posix_time::ptime & v) override { (*this)(v); } - void interval(const boost::posix_time::time_duration & v) override { (*this)(v); } - void blob(const Blob & v) override { (*this)(v); } - void null() override { } - - template <typename D> - void operator()(const D & v) { - if constexpr (std::is_convertible<D, T>::value) { - target = (T)v; - } - else { - throw InvalidConversion(typeid(D).name(), typeid(T).name()); - } - } - - T & target; -}; - -#define COLUMNINTO(Type) \ -void \ -Column::operator>>(Type & v) const \ -{ \ - Extract<Type> e(v); \ - apply(e); \ -} - -COLUMNINTO(bool); -COLUMNINTO(int64_t); -COLUMNINTO(double); -COLUMNINTO(std::string); -COLUMNINTO(boost::posix_time::ptime); -COLUMNINTO(boost::posix_time::time_duration); -COLUMNINTO(Blob); } diff --git a/libdbpp/column.h b/libdbpp/column.h index 393736a..3cc974e 100644 --- a/libdbpp/column.h +++ b/libdbpp/column.h @@ -45,30 +45,59 @@ namespace DB { /// Apply a field handler (any sub-class of HandleField) virtual void apply(HandleField &) const = 0; + template<typename T> + class Extract : public DB::HandleField { + public: + template <typename> struct is_optional { + static constexpr bool value = false; + }; + template <typename X> struct is_optional<std::optional<X>> { + static constexpr bool value = true; + }; + + Extract(T & t) : target(t) { } + + void floatingpoint(double v) override { (*this)(v); } + void integer(int64_t v) override { (*this)(v); } + void boolean(bool v) override { (*this)(v); } + void string(const char * v, size_t len) override { (*this)(std::string_view(v, len)); } + void timestamp(const boost::posix_time::ptime & v) override { (*this)(v); } + void interval(const boost::posix_time::time_duration & v) override { (*this)(v); } + void blob(const Blob & v) override { (*this)(v); } + void null() override + { + if constexpr (is_optional<T>::value) { + target.reset(); + } + else { + throw UnexpectedNullValue(typeid(T).name()); + } + } + + template <typename D> + inline + void operator()(const D & v) + { + if constexpr (std::is_assignable<D, T>::value) { + target = v; + } + else if constexpr (std::is_convertible<D, T>::value) { + target = (T)v; + } + else { + throw InvalidConversion(typeid(D).name(), typeid(T).name()); + } + } + + T & target; + }; + /// STL like string extractor. - void operator>>(std::string &) const; - /// STL like boolean extractor. - void operator>>(bool &) const; - /// STL like integer extractor. - void operator>>(int64_t &) const; - /// STL like numeric extractor. - void operator>>(double &) const; - /// STL like duration extractor. - void operator>>(boost::posix_time::time_duration &) const; - /// STL like date time extractor. - void operator>>(boost::posix_time::ptime &) const; - /// STL like BLOB extractor. - void operator>>(Blob &) const; - /// STL like wrapper for optional types. - template <typename T> - void operator>>(std::optional<T> & v) const { - if (!isNull()) { - v = T(); - operator>>(*v); - } - else { - v = {}; - } + template<typename T> + void operator>>(T & v) const + { + Extract<T> e(v); + apply(e); } /// This column's ordinal. diff --git a/libdbpp/error.h b/libdbpp/error.h index 6e8ab18..bcb641d 100644 --- a/libdbpp/error.h +++ b/libdbpp/error.h @@ -35,6 +35,15 @@ namespace DB { const char * from; const char * to; }; + + class DLL_PUBLIC UnexpectedNullValue : public AdHoc::Exception<Error> { + public: + UnexpectedNullValue(const char * const from); + + private: + std::string message() const throw() override; + const char * to; + }; } #endif diff --git a/libdbpp/unittests/testUtils.cpp b/libdbpp/unittests/testUtils.cpp index 30d5bc4..6ff9edd 100644 --- a/libdbpp/unittests/testUtils.cpp +++ b/libdbpp/unittests/testUtils.cpp @@ -82,7 +82,7 @@ BOOST_AUTO_TEST_CASE( stdforOverRowsStructuredBinding ) int64_t totalOfa = 0; std::string totalOfc; auto sel = db->select("SELECT a, c FROM forEachRow ORDER BY a DESC"); - for (const auto [ a, c ] : sel->as<int64_t, std::string>()) { + for (const auto [ a, c ] : sel->as<int64_t, std::string_view>()) { count += 1; totalOfa += a; totalOfc += c; @@ -170,9 +170,11 @@ BOOST_AUTO_TEST_CASE( bulkLoadFile ) }); } -BOOST_AUTO_TEST_CASE( nullBind ) +typedef boost::mpl::list<std::string, std::string_view, Glib::ustring> StringTypes; +BOOST_AUTO_TEST_CASE_TEMPLATE( nullBind, Str, StringTypes ) { auto db = DB::MockDatabase::openConnectionTo("pqmock"); + db->execute("DELETE FROM forEachRow"); auto ins = db->modify("INSERT INTO forEachRow VALUES(?, ?, ?, ?, ?, ?)"); ins->bindParamI(0, std::optional<int>()); ins->bindParamF(1, std::optional<double>()); @@ -183,9 +185,12 @@ BOOST_AUTO_TEST_CASE( nullBind ) ins->execute(); auto sel = db->select("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; + for (const auto & row : sel->as<int, std::optional<double>, Str>()) { count += 1; + BOOST_CHECK_THROW(row.template get<0>(), DB::UnexpectedNullValue); + auto nd = row.template get<1>(); + BOOST_CHECK(!nd.has_value()); + BOOST_CHECK_THROW(row.template get<2>(), DB::UnexpectedNullValue); } BOOST_REQUIRE_EQUAL(1, count); } |