summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Goodliffe <dan@randomdan.homeip.net>2018-08-27 14:44:39 +0100
committerDan Goodliffe <dan@randomdan.homeip.net>2018-08-27 14:44:39 +0100
commit2c4c619b060ed670c44ce62405229c7e641b3b27 (patch)
tree8cc5539771add606e72f07dac9d8f52cdd3ce42e
parentMove multi_index for columns out of header (diff)
downloadlibdbpp-2c4c619b060ed670c44ce62405229c7e641b3b27.tar.bz2
libdbpp-2c4c619b060ed670c44ce62405229c7e641b3b27.tar.xz
libdbpp-2c4c619b060ed670c44ce62405229c7e641b3b27.zip
Fully templated extractor
-rw-r--r--libdbpp/column.cpp51
-rw-r--r--libdbpp/column.h75
-rw-r--r--libdbpp/error.h9
-rw-r--r--libdbpp/unittests/testUtils.cpp13
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);
}