libdballe 9.13
postgresql.h
Go to the documentation of this file.
1
4#ifndef DBALLE_SQL_POSTGRESQL_H
5#define DBALLE_SQL_POSTGRESQL_H
6
7#include <arpa/inet.h>
8#include <dballe/sql/sql.h>
9#include <functional>
10#include <libpq-fe.h>
11#include <unordered_set>
12#include <vector>
13
14namespace dballe {
15namespace sql {
16
20struct error_postgresql : public error_db
21{
22 std::string msg;
23
24 error_postgresql(PGconn* db, const std::string& msg);
25 error_postgresql(PGresult* db, const std::string& msg);
26 error_postgresql(const std::string& dbmsg, const std::string& msg);
27 ~error_postgresql() throw() {}
28
29 const char* what() const noexcept override { return msg.c_str(); }
30
31 static void throwf(PGconn* db, const char* fmt, ...)
33 static void throwf(PGresult* db, const char* fmt, ...)
35};
36
37namespace postgresql {
38
39int64_t encode_datetime(const Datetime& arg);
40int64_t encode_int64_t(int64_t arg);
41
43template <typename... ARGS> struct Params
44{
45 static const int count = sizeof...(ARGS);
46 const char* args[sizeof...(ARGS)];
47 int lengths[sizeof...(ARGS)];
48 int formats[sizeof...(ARGS)];
49 void* local[sizeof...(ARGS)];
50
51 Params(const ARGS&... args) { _add(0, args...); }
52 ~Params()
53 {
54 for (auto& i : local)
55 free(i);
56 }
57
58 Params(const Params&) = delete;
59 Params(const Params&&) = delete;
60 Params& operator=(const Params&) = delete;
61 Params& operator=(const Params&&) = delete;
62
63protected:
65 void _add(unsigned pos) {}
66
68 template <typename... REST>
69 void _add(unsigned pos, std::nullptr_t arg, const REST&... rest)
70 {
71 local[pos] = nullptr;
72 args[pos] = nullptr;
73 lengths[pos] = 0;
74 formats[pos] = 0;
75 _add(pos + 1, rest...);
76 }
77
79 template <typename... REST>
80 void _add(unsigned pos, int32_t arg, const REST&... rest)
81 {
82 local[pos] = malloc(sizeof(int32_t));
83 *(int32_t*)local[pos] = (int32_t)htonl((uint32_t)arg);
84 args[pos] = (const char*)local[pos];
85 lengths[pos] = sizeof(int32_t);
86 formats[pos] = 1;
87 _add(pos + 1, rest...);
88 }
89
91 template <typename... REST>
92 void _add(unsigned pos, uint64_t arg, const REST&... rest)
93 {
94 local[pos] = malloc(sizeof(int64_t));
95 *(int64_t*)local[pos] = encode_int64_t(arg);
96 args[pos] = (const char*)local[pos];
97 lengths[pos] = sizeof(int64_t);
98 formats[pos] = 1;
99 _add(pos + 1, rest...);
100 }
101
103 template <typename... REST>
104 void _add(unsigned pos, const char* arg, const REST&... rest)
105 {
106 local[pos] = nullptr;
107 args[pos] = arg;
108 lengths[pos] = 0;
109 formats[pos] = 0;
110 _add(pos + 1, rest...);
111 }
112
114 template <typename... REST>
115 void _add(unsigned pos, const std::string& arg, const REST&... rest)
116 {
117 local[pos] = nullptr;
118 args[pos] = arg.data();
119 lengths[pos] = arg.size();
120 formats[pos] = 0;
121 _add(pos + 1, rest...);
122 }
123
125 template <typename... REST>
126 void _add(unsigned pos, const std::vector<uint8_t>& arg,
127 const REST&... rest)
128 {
129 local[pos] = nullptr;
130 args[pos] = (const char*)arg.data();
131 lengths[pos] = arg.size();
132 formats[pos] = 1;
133 _add(pos + 1, rest...);
134 }
135
137 template <typename... REST>
138 void _add(unsigned pos, const Datetime& arg, const REST&... rest)
139 {
140 local[pos] = malloc(sizeof(int64_t));
141 *(int64_t*)local[pos] = encode_datetime(arg);
142 args[pos] = (const char*)local[pos];
143 lengths[pos] = sizeof(int64_t);
144 formats[pos] = 1;
145 _add(pos + 1, rest...);
146 }
147};
148
150struct Result
151{
152 PGresult* res;
153
154 Result() : res(nullptr) {}
155 Result(PGresult* res) : res(res) {}
156 ~Result() { PQclear(res); }
157
159 Result(Result&& o) : res(o.res) { o.res = nullptr; }
160 Result& operator=(Result&& o)
161 {
162 if (this == &o)
163 return *this;
164 PQclear(res);
165 res = o.res;
166 o.res = nullptr;
167 return *this;
168 }
169
170 operator bool() const { return res != nullptr; }
171 operator PGresult*() { return res; }
172 operator const PGresult*() const { return res; }
173
175 void expect_no_data(const std::string& query);
176
178 void expect_result(const std::string& query);
179
181 void expect_one_row(const std::string& query);
182
184 void expect_success(const std::string& query);
185
187 unsigned rowcount() const { return PQntuples(res); }
188
190 bool is_null(unsigned row, unsigned col) const
191 {
192 return PQgetisnull(res, row, col);
193 }
194
196 bool get_bool(unsigned row, unsigned col) const
197 {
198 char* val = PQgetvalue(res, row, col);
199 return *val;
200 }
201
203 uint16_t get_int2(unsigned row, unsigned col) const
204 {
205 char* val = PQgetvalue(res, row, col);
206 return ntohs(*(uint16_t*)val);
207 }
208
210 uint32_t get_int4(unsigned row, unsigned col) const
211 {
212 char* val = PQgetvalue(res, row, col);
213 return ntohl(*(uint32_t*)val);
214 }
215
217 uint64_t get_int8(unsigned row, unsigned col) const;
218
220 std::vector<uint8_t> get_bytea(unsigned row, unsigned col) const;
221
223 const char* get_string(unsigned row, unsigned col) const
224 {
225 return PQgetvalue(res, row, col);
226 }
227
229 Datetime get_timestamp(unsigned row, unsigned col) const;
230
231 // Prevent copy
232 Result(const Result&) = delete;
233 Result& operator=(const Result&) = delete;
234};
235
236} // namespace postgresql
237
239class PostgreSQLConnection : public Connection
240{
241protected:
243 PGconn* db = nullptr;
244 std::unordered_set<std::string> prepared_names;
246 bool forked = false;
247
248protected:
249 void init_after_connect();
250
251 PostgreSQLConnection();
252
253 void fork_prepare() override;
254 void fork_parent() override;
255 void fork_child() override;
256
257 void check_connection();
258
259public:
260 PostgreSQLConnection(const PostgreSQLConnection&) = delete;
261 PostgreSQLConnection(const PostgreSQLConnection&&) = delete;
262 ~PostgreSQLConnection();
263
264 PostgreSQLConnection& operator=(const PostgreSQLConnection&) = delete;
265
266 static std::shared_ptr<PostgreSQLConnection> create();
267
268 operator PGconn*() { return db; }
269
276 void open_url(const std::string& connection_string);
277 void open_test();
278
279 std::unique_ptr<Transaction> transaction(bool readonly = false) override;
280
282 void prepare(const std::string& name, const std::string& query);
283
284 postgresql::Result exec_unchecked(const char* query)
285 {
286 check_connection();
287 auto res =
288 PQexecParams(db, query, 0, nullptr, nullptr, nullptr, nullptr, 1);
289 if (!res)
290 throw error_postgresql(db, std::string("cannot execute query ") +
291 query);
292 return res;
293 }
294
295 postgresql::Result exec_unchecked(const std::string& query)
296 {
297 check_connection();
298 auto res = PQexecParams(db, query.c_str(), 0, nullptr, nullptr, nullptr,
299 nullptr, 1);
300 if (!res)
301 throw error_postgresql(db, "cannot execute query " + query);
302 return res;
303 }
304
305 template <typename STRING> void exec_no_data(STRING query)
306 {
307 postgresql::Result res(exec_unchecked(query));
308 res.expect_no_data(query);
309 }
310
311 template <typename STRING> postgresql::Result exec(STRING query)
312 {
313 postgresql::Result res(exec_unchecked(query));
314 res.expect_result(query);
315 return res;
316 }
317
318 template <typename STRING> postgresql::Result exec_one_row(STRING query)
319 {
320 postgresql::Result res(exec_unchecked(query));
321 res.expect_one_row(query);
322 return res;
323 }
324
325 template <typename... ARGS>
326 postgresql::Result exec_unchecked(const char* query, ARGS... args)
327 {
328 check_connection();
329 postgresql::Params<ARGS...> params(args...);
330 auto res = PQexecParams(db, query, params.count, nullptr, params.args,
331 params.lengths, params.formats, 1);
332 if (!res)
333 throw error_postgresql(db, std::string("cannot execute query ") +
334 query);
335 return res;
336 }
337
338 template <typename... ARGS>
339 postgresql::Result exec_unchecked(const std::string& query, ARGS... args)
340 {
341 check_connection();
342 postgresql::Params<ARGS...> params(args...);
343 auto res = PQexecParams(db, query.c_str(), params.count, nullptr,
344 params.args, params.lengths, params.formats, 1);
345 if (!res)
346 throw error_postgresql(db, "cannot execute query " + query);
347 return res;
348 }
349
350 template <typename STRING, typename... ARGS>
351 void exec_no_data(STRING query, ARGS... args)
352 {
353 postgresql::Result res(exec_unchecked(query, args...));
354 res.expect_no_data(query);
355 }
356
357 template <typename STRING, typename... ARGS>
358 postgresql::Result exec(STRING query, ARGS... args)
359 {
360 postgresql::Result res(exec_unchecked(query, args...));
361 res.expect_result(query);
362 return res;
363 }
364
365 template <typename STRING, typename... ARGS>
366 postgresql::Result exec_one_row(STRING query, ARGS... args)
367 {
368 postgresql::Result res(exec_unchecked(query, args...));
369 res.expect_one_row(query);
370 return res;
371 }
372
373 postgresql::Result exec_prepared_unchecked(const char* name)
374 {
375 check_connection();
376 auto res = PQexecPrepared(db, name, 0, nullptr, nullptr, nullptr, 1);
377 if (!res)
378 throw error_postgresql(
379 db, std::string("cannot execute prepared query ") + name);
380 return res;
381 }
382
383 postgresql::Result exec_prepared_unchecked(const std::string& name)
384 {
385 check_connection();
386 auto res =
387 PQexecPrepared(db, name.c_str(), 0, nullptr, nullptr, nullptr, 1);
388 if (!res)
389 throw error_postgresql(db, "cannot execute prepared query " + name);
390 return res;
391 }
392
393 template <typename STRING> void exec_prepared_no_data(STRING name)
394 {
395 postgresql::Result res(exec_prepared_unchecked(name));
396 res.expect_no_data(name);
397 }
398
399 template <typename STRING> postgresql::Result exec_prepared(STRING name)
400 {
401 postgresql::Result res(exec_prepared_unchecked(name));
402 res.expect_result(name);
403 return res;
404 }
405
406 template <typename STRING>
407 postgresql::Result exec_prepared_one_row(STRING name)
408 {
409 postgresql::Result res(exec_prepared_unchecked(name));
410 res.expect_one_row(name);
411 return res;
412 }
413
414 template <typename... ARGS>
415 postgresql::Result exec_prepared_unchecked(const char* name, ARGS... args)
416 {
417 postgresql::Params<ARGS...> params(args...);
418 return PQexecPrepared(db, name, params.count, params.args,
419 params.lengths, params.formats, 1);
420 }
421
422 template <typename... ARGS>
423 postgresql::Result exec_prepared_unchecked(const std::string& name,
424 ARGS... args)
425 {
426 postgresql::Params<ARGS...> params(args...);
427 return PQexecPrepared(db, name.c_str(), params.count, params.args,
428 params.lengths, params.formats, 1);
429 }
430
431 template <typename STRING, typename... ARGS>
432 void exec_prepared_no_data(STRING name, ARGS... args)
433 {
434 postgresql::Result res(exec_prepared_unchecked(name, args...));
435 res.expect_no_data(name);
436 }
437
438 template <typename STRING, typename... ARGS>
439 postgresql::Result exec_prepared(STRING name, ARGS... args)
440 {
441 postgresql::Result res(exec_prepared_unchecked(name, args...));
442 res.expect_result(name);
443 return res;
444 }
445
446 template <typename STRING, typename... ARGS>
447 postgresql::Result exec_prepared_one_row(STRING name, ARGS... args)
448 {
449 postgresql::Result res(exec_prepared_unchecked(name, args...));
450 res.expect_one_row(name);
451 return res;
452 }
453
456
459
460 bool has_table(const std::string& name) override;
461 std::string get_setting(const std::string& key) override;
462 void set_setting(const std::string& key, const std::string& value) override;
463 void drop_settings() override;
464 void execute(const std::string& query) override;
465 void explain(const std::string& query, FILE* out) override;
466
470 void drop_table_if_exists(const char* name);
471
473 int changes();
474
476 void pqexec(const std::string& query);
477
484 void pqexec_nothrow(const std::string& query) noexcept;
485
487 void
488 run_single_row_mode(const std::string& query_desc,
489 std::function<void(const postgresql::Result&)> dest);
490
492 void append_escaped(Querybuf& qb, const char* str);
493
495 void append_escaped(Querybuf& qb, const std::string& str);
496
498 void append_escaped(Querybuf& qb, const std::vector<uint8_t>& buf);
499};
500
501} // namespace sql
502} // namespace dballe
503#endif
void drop_table_if_exists(const char *name)
Delete a table in the database if it exists, otherwise do nothing.
void prepare(const std::string &name, const std::string &query)
Precompile a query.
void append_escaped(Querybuf &qb, const char *str)
Escape the string as a literal value and append it to qb.
void run_single_row_mode(const std::string &query_desc, std::function< void(const postgresql::Result &)> dest)
Retrieve query results in single row mode.
void pqexec_nothrow(const std::string &query) noexcept
Wrap PQexec but do not throw an exception in case of errors.
void pqexec(const std::string &query)
Wrap PQexec.
std::unique_ptr< Transaction > transaction(bool readonly=false) override
Begin a transaction.
void set_setting(const std::string &key, const std::string &value) override
Set a value in the settings table.
int changes()
Count the number of rows modified by the last query that was run.
bool forked
Marker to catch attempts to reuse connections in forked processes.
Definition postgresql.h:246
PGconn * db
Database connection.
Definition postgresql.h:243
void open_url(const std::string &connection_string)
Connect to PostgreSQL using a connection URI.
void cancel_running_query_nothrow() noexcept
Send a cancellation command to the server.
void discard_all_input_nothrow() noexcept
Discard all input from an asynchronous request.
void explain(const std::string &query, FILE *out) override
Format and print the EXPLAIN output for the query to the given file.
void drop_settings() override
Drop the settings table.
bool has_table(const std::string &name) override
Check if the database contains a table.
void execute(const std::string &query) override
Execute a query without reading its results.
std::string get_setting(const std::string &key) override
Get a value from the settings table.
#define WREPORT_THROWF_ATTRS(a, b)
Common infrastructure for talking with SQL databases.
Date and time.
Definition types.h:164
Error in case of failed database operations.
Definition error.h:22
String buffer for composing database queries.
Definition querybuf.h:16
Report an PostgreSQL error.
Definition postgresql.h:21
void _add(unsigned pos, const std::string &arg, const REST &... rest)
Fill in the argument structures.
Definition postgresql.h:115
void _add(unsigned pos, const char *arg, const REST &... rest)
Fill in the argument structures.
Definition postgresql.h:104
void _add(unsigned pos, int32_t arg, const REST &... rest)
Fill in the argument structures.
Definition postgresql.h:80
void _add(unsigned pos)
Terminating condition for compile-time arg expansion.
Definition postgresql.h:65
void _add(unsigned pos, const std::vector< uint8_t > &arg, const REST &... rest)
Fill in the argument structures.
Definition postgresql.h:126
void _add(unsigned pos, std::nullptr_t arg, const REST &... rest)
Fill in the argument structures.
Definition postgresql.h:69
void _add(unsigned pos, uint64_t arg, const REST &... rest)
Fill in the argument structures.
Definition postgresql.h:92
void _add(unsigned pos, const Datetime &arg, const REST &... rest)
Fill in the argument structures.
Definition postgresql.h:138
Wrap a PGresult, taking care of its memory management.
Definition postgresql.h:151
const char * get_string(unsigned row, unsigned col) const
Return a result value, transmitted as a string.
Definition postgresql.h:223
uint32_t get_int4(unsigned row, unsigned col) const
Return a result value, transmitted in binary as a 4 bit integer.
Definition postgresql.h:210
bool get_bool(unsigned row, unsigned col) const
Return a result value, transmitted in binary as a byte (?)
Definition postgresql.h:196
uint64_t get_int8(unsigned row, unsigned col) const
Return a result value, transmitted in binary as an 8 bit integer.
void expect_success(const std::string &query)
Check that the result was successful.
uint16_t get_int2(unsigned row, unsigned col) const
Return a result value, transmitted in binary as a 2 bit integer.
Definition postgresql.h:203
Result(Result &&o)
Implement move.
Definition postgresql.h:159
void expect_no_data(const std::string &query)
Check that the result successfully returned no data.
std::vector< uint8_t > get_bytea(unsigned row, unsigned col) const
Return a result value, transmitted in binary as an 8 bit integer.
void expect_result(const std::string &query)
Check that the result successfully returned some (possibly empty) data.
void expect_one_row(const std::string &query)
Check that the result successfully returned one row of data.
Datetime get_timestamp(unsigned row, unsigned col) const
Return a result value, transmitted as a timestamp without timezone.
unsigned rowcount() const
Get the number of rows in the result.
Definition postgresql.h:187
bool is_null(unsigned row, unsigned col) const
Check if a result value is null.
Definition postgresql.h:190