From bca75248182ea09614688af0538df87a7087bbce Mon Sep 17 00:00:00 2001 From: "Sergei L. Khandrikov" Date: Sat, 28 Mar 2020 00:56:37 +0300 Subject: [PATCH] Add share ability for ozo::result 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. --- include/ozo/io/recv.h | 4 +- include/ozo/pg/handle.h | 14 ++++++ include/ozo/result.h | 58 +++++++++++++++++++++--- tests/integration/result_integration.cpp | 18 ++++++++ tests/result.cpp | 9 ++++ 5 files changed, 94 insertions(+), 9 deletions(-) diff --git a/include/ozo/io/recv.h b/include/ozo/io/recv.h index d4574d8f1..0159d79a2 100644 --- a/include/ozo/io/recv.h +++ b/include/ozo/io/recv.h @@ -351,8 +351,8 @@ basic_result& recv_result(basic_result& in, const OidMap&, basic_result return out; } -template -basic_result& recv_result(basic_result& in, const OidMap& oid_map, std::reference_wrapper> out) { +template +basic_result& recv_result(basic_result& in, const OidMap& oid_map, std::reference_wrapper out) { return recv_result(in, oid_map, out.get()); } diff --git a/include/ozo/pg/handle.h b/include/ozo/pg/handle.h index 801956c6f..8714cfb0a 100644 --- a/include/ozo/pg/handle.h +++ b/include/ozo/pg/handle.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace ozo::pg { @@ -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 { + static ozo::pg::shared_result apply(ozo::pg::result x) { + return {x.release(), x.get_deleter()}; + } +}; + +} // namespace boost::hana diff --git a/include/ozo/result.h b/include/ozo/result.h index 299937b86..765e4880a 100644 --- a/include/ozo/result.h +++ b/include/ozo/result.h @@ -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 ::value && !std::is_same_v + >> + basic_result(basic_result&& x) noexcept( + noexcept(handle_type{hana::to(std::move(x.release()))})) + : handle_(hana::to(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. @@ -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; } @@ -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) { + 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; + /** * @brief Database raw result representation * @@ -397,7 +441,7 @@ class basic_result { * * @ingroup group-requests-types */ -using result = basic_result; +using result = basic_result; template auto make_result(T&& handle) { diff --git a/tests/integration/result_integration.cpp b/tests/integration/result_integration.cpp index c1c0cf625..ce5bf8ac3 100644 --- a/tests/integration/result_integration.cpp +++ b/tests/integration/result_integration.cpp @@ -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; @@ -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> 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(); diff --git a/tests/result.cpp b/tests/result.cpp index 3c7f5b6af..46fd6bb0f 100644 --- a/tests/result.cpp +++ b/tests/result.cpp @@ -186,6 +186,15 @@ struct basic_result : Test { ozo::basic_result 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 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));