libdballe  9.14
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 
14 namespace dballe {
15 namespace sql {
16 
20 struct 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 
37 namespace postgresql {
38 
39 int64_t encode_datetime(const Datetime& arg);
40 int64_t encode_int64_t(int64_t arg);
41 
43 template <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 
63 protected:
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 
150 struct 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 
240 {
241 protected:
243  PGconn* db = nullptr;
244  std::unordered_set<std::string> prepared_names;
246  bool forked = false;
247 
248 protected:
249  void init_after_connect();
250 
252 
253  void fork_prepare() override;
254  void fork_parent() override;
255  void fork_child() override;
256 
257  void check_connection();
258 
259 public:
261  PostgreSQLConnection(const PostgreSQLConnection&&) = delete;
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 
455  void cancel_running_query_nothrow() noexcept;
456 
458  void discard_all_input_nothrow() noexcept;
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 open_url(const std::string &connection_string)
Connect to PostgreSQL using a connection URI.
Database connection.
Definition: postgresql.h:239
Result(Result &&o)
Implement move.
Definition: postgresql.h:159
void set_setting(const std::string &key, const std::string &value) override
Set a value in the settings table.
Argument list for PQexecParams built at compile time.
Definition: postgresql.h:43
void _add(unsigned pos, const char *arg, const REST &... rest)
Fill in the argument structures.
Definition: postgresql.h:104
void _add(unsigned pos, uint64_t arg, const REST &... rest)
Fill in the argument structures.
Definition: postgresql.h:92
void run_single_row_mode(const std::string &query_desc, std::function< void(const postgresql::Result &)> dest)
Retrieve query results in single row mode.
bool forked
Marker to catch attempts to reuse connections in forked processes.
Definition: postgresql.h:246
int changes()
Count the number of rows modified by the last query that was run.
void cancel_running_query_nothrow() noexcept
Send a cancellation command to the server.
bool has_table(const std::string &name) override
Check if the database contains a table.
unsigned rowcount() const
Get the number of rows in the result.
Definition: postgresql.h:187
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
Definition: utils.h:40
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
Definition: cmdline.h:18
void prepare(const std::string &name, const std::string &query)
Precompile a query.
bool is_null(unsigned row, unsigned col) const
Check if a result value is null.
Definition: postgresql.h:190
std::vector< uint8_t > get_bytea(unsigned row, unsigned col) const
Return a result value, transmitted in binary as an 8 bit integer.
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
void pqexec(const std::string &query)
Wrap PQexec.
Datetime get_timestamp(unsigned row, unsigned col) const
Return a result value, transmitted as a timestamp without timezone.
Definition: sql.h:53
void pqexec_nothrow(const std::string &query) noexcept
Wrap PQexec but do not throw an exception in case of errors.
void expect_one_row(const std::string &query)
Check that the result successfully returned one row of data.
Wrap a PGresult, taking care of its memory management.
Definition: postgresql.h:150
bool get_bool(unsigned row, unsigned col) const
Return a result value, transmitted in binary as a byte (?)
Definition: postgresql.h:196
void _add(unsigned pos, int32_t arg, const REST &... rest)
Fill in the argument structures.
Definition: postgresql.h:80
void append_escaped(Querybuf &qb, const char *str)
Escape the string as a literal value and append it to qb.
void expect_result(const std::string &query)
Check that the result successfully returned some (possibly empty) data.
void expect_no_data(const std::string &query)
Check that the result successfully returned no data.
void drop_table_if_exists(const char *name)
Delete a table in the database if it exists, otherwise do nothing.
Report an PostgreSQL error.
Definition: postgresql.h:20
Common infrastructure for talking with SQL databases.
Error in case of failed database operations.
Definition: error.h:21
void _add(unsigned pos, const std::string &arg, const REST &... rest)
Fill in the argument structures.
Definition: postgresql.h:115
PGconn * db
Database connection.
Definition: postgresql.h:243
void discard_all_input_nothrow() noexcept
Discard all input from an asynchronous request.
void _add(unsigned pos, const Datetime &arg, const REST &... rest)
Fill in the argument structures.
Definition: postgresql.h:138
void explain(const std::string &query, FILE *out) override
Format and print the EXPLAIN output for the query to the given file.
Date and time.
Definition: types.h:163
std::unique_ptr< Transaction > transaction(bool readonly=false) override
Begin a transaction.
void drop_settings() override
Drop the settings table.
#define WREPORT_THROWF_ATTRS(a, b)
void _add(unsigned pos, std::nullptr_t arg, const REST &... rest)
Fill in the argument structures.
Definition: postgresql.h:69
const char * get_string(unsigned row, unsigned col) const
Return a result value, transmitted as a string.
Definition: postgresql.h:223
uint64_t get_int8(unsigned row, unsigned col) const
Return a result value, transmitted in binary as an 8 bit integer.
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.
void expect_success(const std::string &query)
Check that the result was successful.