userver: userver/storages/postgres/query_queue.hpp Source File
Loading...
Searching...
No Matches
query_queue.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/query_queue.hpp
4/// @brief An utility to execute multiple queries in a single network
5/// round-trip.
6
7#include <userver/storages/postgres/options.hpp>
8#include <userver/storages/postgres/query.hpp>
9#include <userver/storages/postgres/result_set.hpp>
10
11#include <userver/storages/postgres/detail/connection_ptr.hpp>
12#include <userver/storages/postgres/detail/query_parameters.hpp>
13
14#include <userver/utils/any_movable.hpp>
15#include <userver/utils/fast_pimpl.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace storages::postgres {
20
21/// @brief A container to enqueue queries in FIFO order and execute them all
22/// within a single network round-trip.
23///
24/// Acquired from storages::postgres::Cluster, one is expected to `Push`
25/// some queries into the queue and then `Collect` them into vector of results.
26///
27/// From a client point of view `Collect` is transactional: either all the
28/// queries succeed or `Collect` rethrows the first error encountered. However,
29/// this is *NOT* the case for the server: server treats all the provided
30/// queries independently and is likely to execute subsequent queries even
31/// after prior failures.
32///
33/// @warning If an explicit transaction ("BEGIN") or a modifying query is added
34/// into the queue the behavior is unspecified. *Don't do that*.
35///
36/// @note Queries may or may not be sent to the server prior to `Collect` call.
37///
38/// @note Requires both pipelining and prepared statements to be enabled in the
39/// driver, UINVARIANTS (that is, throws in release builds and aborts in debug
40/// ones) that on construction.
41class QueryQueue final {
42 public:
43 QueryQueue(CommandControl default_cc, detail::ConnectionPtr&& conn);
44
45 QueryQueue(QueryQueue&&) noexcept;
46 QueryQueue& operator=(QueryQueue&&) noexcept;
47
48 QueryQueue(const QueryQueue&) = delete;
49 QueryQueue& operator=(const QueryQueue&) = delete;
50
51 ~QueryQueue();
52
53 /// Reserve internal storage to hold this amount of queries.
54 void Reserve(std::size_t size);
55
56 /// Add a query into the queue with specified command-control.
57 /// CommandControl is used as following: 'execute' is used as a timeout for
58 /// preparing the statement (if needed), 'statement' is used as a statement
59 /// timeout for later execution.
60 template <typename... Args>
61 void Push(CommandControl cc, const Query& query, const Args&... args);
62
63 /// Add a query into the queue with default command-control.
64 template <typename... Args>
65 void Push(const Query& query, const Args&... args);
66
67 /// Collect results of all the queued queries, with specified timeout.
68 /// Either returns a vector of N `ResultSet`s, where N is the number of
69 /// queries enqueued, or rethrow the first error encountered, be that a query
70 /// execution error or a timeout.
72
73 /// Collect results of all the queued queries, with default timeout.
74 /// Either returns a vector of N `ResultSet`s, where N is the number of
75 /// queries enqueued, or rethrow the first error encountered, be that a query
76 /// execution error or a timeout.
77 [[nodiscard]] std::vector<ResultSet> Collect();
78
79 private:
80 struct ParamsHolder final {
81 // We only need to know what's here at construction (and at construction we
82 // do know), after that we just need to preserve lifetime.
83 USERVER_NAMESPACE::utils::AnyMovable any_params{};
84 // This here knows what's there actually is in params.
85 detail::QueryParameters params_proxy;
86 };
87
88 const UserTypes& GetConnectionUserTypes() const;
89
90 void DoPush(CommandControl cc, const Query& query, ParamsHolder&& params);
91
92 void ValidateUsage() const;
93
94 CommandControl default_cc_;
95 detail::ConnectionPtr conn_;
96
97 struct QueriesStorage;
98 USERVER_NAMESPACE::utils::FastPimpl<QueriesStorage, 48, 8> queries_storage_;
99};
100
101template <typename... Args>
102void QueryQueue::Push(CommandControl cc, const Query& query,
103 const Args&... args) {
104 ParamsHolder holder{};
105 auto& params = holder.any_params
106 .Emplace<detail::StaticQueryParameters<sizeof...(args)>>();
107 params.Write(GetConnectionUserTypes(), args...);
108 holder.params_proxy = detail::QueryParameters{params};
109 DoPush(cc, query, std::move(holder));
110}
111
112template <typename... Args>
113void QueryQueue::Push(const Query& query, const Args&... args) {
114 Push(default_cc_, query, args...);
115}
116
117} // namespace storages::postgres
118
119USERVER_NAMESPACE_END