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