Skip to content

Commit

Permalink
Add share ability for ozo::result
Browse files Browse the repository at this point in the history
Initially ozo::result is not copyable object, but sometimes
it is better to have copyable result object. This is solved
via ozo::shared_result object which may be copyable. Shared
result may be obtained by constructing from ozo::result rvalue
reference.
  • Loading branch information
thed636 committed Apr 6, 2020
1 parent 3148fa6 commit bca7524
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 9 deletions.
4 changes: 2 additions & 2 deletions include/ozo/io/recv.h
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,8 @@ basic_result<T>& recv_result(basic_result<T>& in, const OidMap&, basic_result<T>
return out;
}

template <typename T, typename OidMap>
basic_result<T>& recv_result(basic_result<T>& in, const OidMap& oid_map, std::reference_wrapper<basic_result<T>> out) {
template <typename T, typename OidMap, typename Out>
basic_result<T>& recv_result(basic_result<T>& in, const OidMap& oid_map, std::reference_wrapper<Out> out) {
return recv_result(in, oid_map, out.get());
}

Expand Down
14 changes: 14 additions & 0 deletions include/ozo/pg/handle.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <libpq-fe.h>
#include <boost/hana/core/to.hpp>
#include <memory>

namespace ozo::pg {
Expand Down Expand Up @@ -36,4 +37,17 @@ using conn = safe_handle_t<::PGconn>;

using result = pg::safe_handle_t<::PGresult>;

using shared_result = std::shared_ptr<::PGresult>;

} // namespace ozo::pg

namespace boost::hana {

template <>
struct to_impl<ozo::pg::shared_result, ozo::pg::result> {
static ozo::pg::shared_result apply(ozo::pg::result x) {
return {x.release(), x.get_deleter()};
}
};

} // namespace boost::hana
58 changes: 51 additions & 7 deletions include/ozo/result.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,14 +316,22 @@ class basic_result {

basic_result() = default;
basic_result(handle_type res) noexcept(noexcept(handle_type(std::move(res))))
: res_(std::move(res)) {}
: handle_(std::move(res)) {}

template <typename Other, typename = hana::when<
hana::is_convertible<Other, T>::value && !std::is_same_v<T, Other>
>>
basic_result(basic_result<Other>&& x) noexcept(
noexcept(handle_type{hana::to<T>(std::move(x.release()))}))
: handle_(hana::to<T>(std::move(x.release()))) {
}

/**
* Iterator on the first row of the result.
*
* @return const_iterator --- iterator on the first row
*/
const_iterator begin() const noexcept { return {{std::addressof(*res_), 0, 0}}; }
const_iterator begin() const noexcept { return {{native_handle(), 0, 0}}; }

/**
* Iterator on end of row sequence.
Expand All @@ -337,12 +345,12 @@ class basic_result {
*
* @return `std::size_t` --- count of rows.
*/
std::size_t size() const noexcept { return impl::ntuples(*res_);}
std::size_t size() const noexcept { return impl::ntuples(*native_handle());}

/**
* Determine whether the result is empty.
*
* @return `true` --- no ros in the result, `size() == 0`, `begin() == end()`.
* @return `true` --- no rows in the result, `size() == 0`, `begin() == end()`.
* @return `false` --- row is not empty, `size() > 0`, `begin() != end()`.
*/
[[nodiscard]] bool empty() const noexcept { return size() == 0; }
Expand Down Expand Up @@ -382,13 +390,49 @@ class basic_result {
* @return native_handle_type --- native handle representation
*/
native_handle_type native_handle() const noexcept {
return std::addressof(*res_);
return std::addressof(*handle_);
}

/**
* Checks if object contains result handle.
*/
bool valid() const noexcept { return bool(handle_); }

/**
* Releases ownership of the native connection handle object.
*
* This function may be used to obtain the underlying result handle.
* After calling this function, `valid()` returns false.
*
* @return handle_type --- result handle object
*/
handle_type release() noexcept(std::is_nothrow_move_constructible_v<handle_type>) {
return std::move(handle_);
}

private:
handle_type res_;

handle_type handle_;
};

/**
* @brief Database raw result representation
*
* Copyable version of `ozo::result`. The result object is useful then it needs to get an access
* to raw data representation or the underlying `libpq` handle.
*
* Get this type of result may be obtained from `ozo::result`:
*
* @code
ozo::result res;
//...
ozo::share_result shared_res(std::move(res));
* @endcode
*
* @ingroup group-requests-types
*/
using shared_result = basic_result<pg::shared_result>;

/**
* @brief Database raw result representation
*
Expand All @@ -397,7 +441,7 @@ class basic_result {
*
* @ingroup group-requests-types
*/
using result = basic_result<ozo::pg::result>;
using result = basic_result<pg::result>;

template <typename T>
auto make_result(T&& handle) {
Expand Down
18 changes: 18 additions & 0 deletions tests/integration/result_integration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace {

using namespace ::testing;
using namespace std::string_literals;

auto execute_query(const char* query_text, int binary = 1) {
using scoped_connection = std::unique_ptr<PGconn, void(*)(PGconn*)>;
Expand Down Expand Up @@ -45,6 +46,23 @@ TEST(result, should_convert_into_tuple_integer_and_text) {
EXPECT_EQ(std::get<1>(r[0]), "2");
}

TEST(result, should_not_be_valid_after_share_call) {
auto result = execute_query("select 1::int4, '2'::text;");
ASSERT_TRUE(result.valid());
ozo::shared_result(std::move(result));
EXPECT_FALSE(result.valid());
}

TEST(shared_result, should_convert_into_tuple_integer_and_text) {
ozo::shared_result result;
std::vector<std::tuple<int32_t, std::string>> r;
result = execute_query("select 1::int4, '2'::text;");
ozo::recv_result(result, ozo::empty_oid_map_c, std::back_inserter(r));

ASSERT_EQ(r.size(), 1u);
EXPECT_EQ(r[0], std::make_tuple(1, "2"s));
}

TEST(result, should_convert_into_tuple_time_point_and_text) {
auto result = execute_query("select '2000-01-01 00:00:00'::timestamp, '2'::text;");
auto oid_map = ozo::empty_oid_map();
Expand Down
9 changes: 9 additions & 0 deletions tests/result.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,15 @@ struct basic_result : Test {
ozo::basic_result<pg_result_mock*> result{&mock};
};

TEST_F(basic_result, valid_should_return_true_for_constructed_with_handle) {
EXPECT_TRUE(result.valid());
}

TEST_F(basic_result, valid_should_return_false_for_constructed_with_null_handle) {
ozo::basic_result<pg_result_mock*> result{nullptr};
EXPECT_FALSE(result.valid());
}

TEST_F(basic_result, empty_should_return_true_if_pg_ntuples_returns_0) {
EXPECT_CALL(mock, ntuples()).WillOnce(Return(0));

Expand Down

0 comments on commit bca7524

Please sign in to comment.