Cppgres
Build Postgres extensions in C++
Loading...
Searching...
No Matches
bgw.hpp
Go to the documentation of this file.
1
4#pragma once
5
6#include "backend.hpp"
7#include "datum.hpp"
8#include "utils/maybe_ref.hpp"
9
10namespace cppgres {
15
22 background_worker() { worker->bgw_notify_pid = ::MyProcPid; }
23
28 background_worker(::BackgroundWorker &worker) : worker(worker) {}
29
30 background_worker &name(std::string_view name) {
31 size_t n = std::min(name.size(), static_cast<size_t>(sizeof(worker->bgw_name) - 1));
32 std::copy_n(name.data(), n, worker->bgw_name);
33 worker->bgw_name[n] = '\0';
34 return *this;
35 }
36 std::string_view name() { return worker->bgw_name; }
37
38 background_worker &type(std::string_view name) {
39 size_t n = std::min(name.size(), static_cast<size_t>(sizeof(worker->bgw_type) - 1));
40 std::copy_n(name.data(), n, worker->bgw_type);
41 worker->bgw_type[n] = '\0';
42 return *this;
43 }
44 std::string_view type() { return worker->bgw_type; }
45
46 background_worker &library_name(std::string_view name) {
47 size_t n = std::min(name.size(), static_cast<size_t>(sizeof(worker->bgw_library_name) - 1));
48 std::copy_n(name.data(), n, worker->bgw_library_name);
49 worker->bgw_library_name[n] = '\0';
50 return *this;
51 }
52 std::string_view library_name() { return worker->bgw_library_name; }
53
54 background_worker &function_name(std::string_view name) {
55 size_t n = std::min(name.size(), static_cast<size_t>(sizeof(worker->bgw_function_name) - 1));
56 std::copy_n(name.data(), n, worker->bgw_function_name);
57 worker->bgw_function_name[n] = '\0';
58 return *this;
59 }
60 std::string_view function_name() { return worker->bgw_function_name; }
61
62 background_worker &start_time(BgWorkerStartTime time) {
63 worker->bgw_start_time = time;
64 return *this;
65 }
66
67 ::BgWorkerStartTime start_time() const { return worker->bgw_start_time; }
68
69 background_worker &restart_time(int time) {
70 worker->bgw_restart_time = time;
71 return *this;
72 }
73
74 int restart_time() const { return worker->bgw_restart_time; }
75
76 background_worker &flags(int flags) {
77 worker->bgw_flags = flags;
78 return *this;
79 }
80
81 int flags() const { return worker->bgw_flags; }
82
83 background_worker &main_arg(datum datum) {
84 worker->bgw_main_arg = datum;
85 return *this;
86 }
87
88 datum main_arg() const { return datum(worker->bgw_main_arg); }
89
90 background_worker &extra(std::string_view name) {
91 size_t n = std::min(name.size(), static_cast<size_t>(sizeof(worker->bgw_extra) - 1));
92 std::copy_n(name.data(), n, worker->bgw_extra);
93 worker->bgw_extra[n] = '\0';
94 return *this;
95 }
96 std::string_view extra() { return worker->bgw_extra; }
97
98 background_worker &notify_pid(pid_t pid) {
99 worker->bgw_notify_pid = pid;
100 return *this;
101 }
102
103 pid_t notify_pid() const { return worker->bgw_notify_pid; }
104
105 operator BackgroundWorker &() { return worker; }
106 operator BackgroundWorker *() { return worker.operator->(); }
107
108 struct worker_stopped : public std::exception {
109 const char *what() const noexcept override { return "Background worker stopped"; }
110 };
111
112 struct worker_not_yet_started : public std::exception {
113 const char *what() const noexcept override {
114 return "Background worker hasn't been started yet";
115 }
116 };
117
118 struct postmaster_died : public std::exception {
119 const char *what() const noexcept override { return "postmaster died"; }
120 };
121
122 struct handle {
123 handle() : handle_(nullptr) {}
124 handle(::BackgroundWorkerHandle *handle) : handle_(handle) {}
125
126 ::BackgroundWorkerHandle *operator->() { return handle_; }
127
128 bool has_value() const { return handle_ != nullptr; }
129 ::BackgroundWorkerHandle *value() { return handle_; }
130
131 pid_t wait_for_startup() {
132 if (has_value()) {
133 pid_t pid;
134 ffi_guard{::WaitForBackgroundWorkerStartup}(value(), &pid);
135 return pid;
136 }
137 throw std::logic_error("Attempting to wait for a background worker with no handle");
138 }
139
140 void wait_for_shutdown() {
141 if (has_value()) {
142 ffi_guard{::WaitForBackgroundWorkerShutdown}(value());
143 return;
144 }
145 throw std::logic_error("Attempting to wait for a background worker with no handle");
146 }
147
148 void terminate() {
149 if (has_value()) {
150 ffi_guard{::TerminateBackgroundWorker}(value());
151 return;
152 }
153 throw std::logic_error("Attempting to terminate a background worker with no handle");
154 }
155
156 pid_t get_pid() {
157 if (has_value()) {
158 pid_t pid;
159 auto rc = ffi_guard{::GetBackgroundWorkerPid}(value(), &pid);
160 switch (rc) {
161 case BGWH_STARTED:
162 return pid;
163 case BGWH_STOPPED:
164 throw worker_stopped();
165 case BGWH_NOT_YET_STARTED:
167 case BGWH_POSTMASTER_DIED:
168 throw postmaster_died();
169 }
170 }
171 throw std::logic_error("Attempting to get a PID of a background worker with no handle");
172 }
173
174 const char *worker_type() { return ffi_guard{::GetBackgroundWorkerTypeByPid}(get_pid()); }
175
176 private:
177 ::BackgroundWorkerHandle *handle_;
178 };
179
180 handle start(bool dynamic = true) {
181 if (!dynamic) {
182 if (::IsUnderPostmaster || !::IsPostmasterEnvironment) {
183 throw std::runtime_error(
184 "static background worker can only be start in the postmaster process");
185 }
186 ffi_guard{::RegisterBackgroundWorker}(operator BackgroundWorker *());
187 return {};
188 }
189 ::BackgroundWorkerHandle *handle;
190 ffi_guard{::RegisterDynamicBackgroundWorker}(operator BackgroundWorker *(), &handle);
191 return {handle};
192 }
193
194private:
195 utils::maybe_ref<::BackgroundWorker> worker = {};
196};
197
199 virtual int flag() const { return 0; }
200};
201
204 int flag() const override { return BGWORKER_BYPASS_ALLOWCONN; }
205};
206
207#if PG_MAJORVERSION_NUM >= 17
208struct background_worker_bypass_role_login_check
210 int flag() const override { return BGWORKER_BYPASS_ROLELOGINCHECK; }
211};
212#endif
213
215 friend std::optional<current_background_worker> get_current_background_worker();
216
224 if (backend::type() != backend_type::bg_worker) {
225 throw std::logic_error("can't access current background worker in a different backend type");
226 }
227 }
228
229 void unblock_signals() { ffi_guard{::BackgroundWorkerUnblockSignals}(); }
230
231 void block_signals() { ffi_guard{::BackgroundWorkerBlockSignals}(); }
232
243 template <typename... Flags>
244 void connect(std::string dbname, std::optional<std::string> user = std::nullopt, Flags... flags)
245 requires(
246 std::conjunction_v<std::is_base_of<background_worker_database_conection_flag, Flags>...>)
247 {
248 ffi_guard{::BackgroundWorkerInitializeConnection}(
249 dbname.c_str(), user.has_value() ? user.value().c_str() : nullptr,
250 (flags.flag() | ... | 0));
251 }
252
263 template <typename... Flags>
264 void connect(oid db, std::optional<oid> user = std::nullopt, Flags... flags) requires(
265 std::conjunction_v<std::is_base_of<background_worker_database_conection_flag, Flags>...>)
266 {
267 ffi_guard{::BackgroundWorkerInitializeConnectionByOid}(
268 db, user.has_value() ? user.value() : oid(InvalidOid), (flags.flag() | ... | 0));
269 }
270};
271
272} // namespace cppgres
static backend_type::type type()
get current backend type
Definition: backend.hpp:49
Definition: bgw.hpp:122
Background worker construction and operations.
Definition: bgw.hpp:14
background_worker(::BackgroundWorker &worker)
Definition: bgw.hpp:28
background_worker()
Initialize background worker specification.
Definition: bgw.hpp:22
Definition: bgw.hpp:214
current_background_worker()
gets current background worker's entry
Definition: bgw.hpp:223
void connect(oid db, std::optional< oid > user=std::nullopt, Flags... flags)
Connect to the database using db oid and, optionally, user oid.
Definition: bgw.hpp:264
void connect(std::string dbname, std::optional< std::string > user=std::nullopt, Flags... flags)
Connect to the database using db name and, optionally, username.
Definition: bgw.hpp:244
Definition: guard.hpp:19
Definition: name.hpp:7
Definition: datum.hpp:17
Definition: value.hpp:8
Single-threaded Postgres workload worker.
Definition: threading.hpp:34