libpqxx
The C++ client library for PostgreSQL
result.hxx
Go to the documentation of this file.
1 /* Definitions for the pqxx::result class and support classes.
2  *
3  * pqxx::result represents the set of result rows from a database query.
4  *
5  * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/result instead.
6  *
7  * Copyright (c) 2000-2026, Jeroen T. Vermeulen.
8  *
9  * See COPYING for copyright license. If you did not receive a file called
10  * COPYING with this source code, please notify the distributor of this
11  * mistake, or contact the author.
12  */
13 #ifndef PQXX_RESULT_HXX
14 #define PQXX_RESULT_HXX
15 
16 #if !defined(PQXX_HEADER_PRE)
17 # error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
18 #endif
19 
20 #include <format>
21 #include <functional>
22 #include <ios>
23 #include <list>
24 #include <memory>
25 #include <stdexcept>
26 
27 #include "pqxx/except.hxx"
28 #include "pqxx/types.hxx"
29 #include "pqxx/util.hxx"
30 #include "pqxx/zview.hxx"
31 
33 
34 
35 namespace pqxx::internal
36 {
37 PQXX_LIBEXPORT void clear_result(pq::PGresult const *) noexcept;
38 } // namespace pqxx::internal
39 
40 
41 namespace pqxx::internal::gate
42 {
43 class result_connection;
44 class result_creation;
45 class result_field_ref;
46 class result_pipeline;
47 class result_row;
48 class result_sql_cursor;
49 } // namespace pqxx::internal::gate
50 
51 
52 namespace pqxx::internal
53 {
54 // 9.0: Remove this, just use the notice handler in connection/result.
56 struct notice_waiters final
57 {
58  // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
59  std::function<void(zview)> notice_handler;
60  std::list<errorhandler *> errorhandlers;
61  // NOLINTEND(misc-non-private-member-variables-in-classes)
62 
63  notice_waiters() = default;
64  notice_waiters(notice_waiters const &) = delete;
66  ~notice_waiters() = default;
69 };
70 } // namespace pqxx::internal
71 
72 
73 namespace pqxx
74 {
75 class row_ref;
76 class field_ref;
77 
78 
80 
101 {
102 public:
111 
112  result() noexcept = default;
113  result(result const &rhs) noexcept = default;
114  result(result &&rhs) noexcept = default;
115  ~result() = default;
116 
118 
121  result &operator=(result const &rhs) noexcept = default;
122 
124  result &operator=(result &&rhs) noexcept = default;
125 
156  [[nodiscard]] bool operator==(result const &rhs) const noexcept
157  {
158  return rhs.m_data == m_data;
159  }
161  [[nodiscard]] bool operator!=(result const &rhs) const noexcept
162  {
163  return not operator==(rhs);
164  }
166 
175 
181  template<typename... TYPE> auto iter() const;
182 
183  [[nodiscard]] PQXX_PURE const_reverse_iterator rbegin() const noexcept;
184  [[nodiscard]] PQXX_PURE const_reverse_iterator crbegin() const noexcept;
185  [[nodiscard]] PQXX_PURE const_reverse_iterator rend() const noexcept;
186  [[nodiscard]] PQXX_PURE const_reverse_iterator crend() const noexcept;
187 
188  [[nodiscard]] PQXX_PURE const_iterator begin() const noexcept;
189  [[nodiscard]] PQXX_PURE const_iterator cbegin() const noexcept;
190  [[nodiscard]] PQXX_PURE inline const_iterator end() const noexcept;
191  [[nodiscard]] PQXX_PURE inline const_iterator cend() const noexcept;
192 
193  [[nodiscard]] PQXX_PURE row_ref front() const noexcept;
194  [[nodiscard]] PQXX_PURE row_ref back() const noexcept;
195 
196  [[nodiscard]] PQXX_PURE size_type size() const noexcept;
197  [[nodiscard]] PQXX_PURE bool empty() const noexcept;
198  [[nodiscard]] PQXX_PURE size_type capacity() const noexcept
199  {
200  return size();
201  }
203 
205 
209  void swap(result &) noexcept;
210 
212 
219  [[nodiscard]] PQXX_PURE row_ref operator[](size_type i) const noexcept;
220 
221 #if defined(PQXX_HAVE_MULTIDIM)
223  [[nodiscard]] PQXX_PURE field_ref
224  operator[](size_type row_num, row_size_type col_num) const noexcept;
225 #endif // PQXX_HAVE_MULTIDIM
226 
228  [[nodiscard]] row_ref at(size_type, sl = sl::current()) const;
229 
231  [[nodiscard]] field_ref
232  at(size_type, row_size_type, sl = sl::current()) const;
233 
235 
242  void clear() noexcept
243  {
244  m_data.reset();
245  m_query = nullptr;
246  }
247 
253  [[nodiscard]] PQXX_PURE row_size_type columns() const noexcept;
254 
256 
264  [[nodiscard]] row_size_type
265  column_number(zview name, sl = sl::current()) const;
266 
268  [[nodiscard]] PQXX_RETURNS_NONNULL char const *
269  column_name(row_size_type number, sl = sl::current()) const &;
270 
272 
275  [[nodiscard]] int
276  column_storage(row_size_type number, sl = sl::current()) const;
277 
279 
289  [[nodiscard]] int column_type_modifier(row_size_type number) const noexcept;
290 
292  [[nodiscard]] oid
293  column_type(row_size_type col_num, sl = sl::current()) const;
294 
296 
304  [[nodiscard]] oid column_type(zview col_name, sl loc = sl::current()) const
305  {
306  return column_type(column_number(col_name, loc));
307  }
308 
310  [[nodiscard]] oid
311  column_table(row_size_type col_num, sl = sl::current()) const;
312 
314 
322  [[nodiscard]] oid column_table(zview col_name, sl loc = sl::current()) const
323  {
324  return column_table(column_number(col_name, loc), loc);
325  }
326 
328  [[nodiscard]] row_size_type
329  table_column(row_size_type col_num, sl = sl::current()) const;
330 
332 
340  [[nodiscard]] row_size_type
341  table_column(zview col_name, sl loc = sl::current()) const
342  {
343  return table_column(column_number(col_name), loc);
344  }
346 
348  [[nodiscard]] PQXX_PURE std::string const &query() const & noexcept;
349 
351 
354  [[nodiscard]] PQXX_PURE oid inserted_oid(sl = sl::current()) const;
355 
357 
364  [[nodiscard]] PQXX_PURE size_type affected_rows() const;
365 
367 
404  template<typename CALLABLE>
405  inline void for_each(CALLABLE &&func, sl = sl::current()) const;
406 
407  // NOLINTBEGIN(modernize-use-nodiscard)
408 
410 
413  result const &expect_rows(size_type n, sl loc = sl::current()) const
414  {
415  auto const sz{size()};
416  if (sz != n)
417  {
418  // TODO: See whether result contains a generated statement.
419  if (not m_query or m_query->empty())
420  throw unexpected_rows{
421  std::format("Expected {} row(s) from query, got {}.", n, sz), loc};
422  else
423  throw unexpected_rows{
424  std::format(
425  "Expected {} row(s) from query '{}', got {}.", n, *m_query, sz),
426  loc};
427  }
428  return *this;
429  }
430 
432 
438  row one_row(sl = sl::current()) const;
439 
441 
446  row_ref one_row_ref(sl = sl::current()) const;
447 
449 
454  std::optional<row> opt_row(sl = sl::current()) const;
455 
457 
462  std::optional<row_ref> opt_row_ref(sl = sl::current()) const;
463 
465  result const &no_rows(sl loc = sl::current()) const
466  {
467  expect_rows(0, loc);
468  return *this;
469  }
470 
472 
475  result const &
476  expect_columns(row_size_type cols, sl loc = sl::current()) const
477  {
478  auto const actual{columns()};
479  if (actual != cols)
480  {
481  // TODO: See whether result contains a generated statement.
482  if (not m_query or m_query->empty())
483  throw usage_error{
484  std::format(
485  "Expected {} column(s) from query, got {}.", cols, actual),
486  loc};
487  else
488  throw usage_error{
489  std::format(
490  "Expected {} column(s) from query '{}', got {}.", cols, *m_query,
491  actual),
492  loc};
493  }
494  return *this;
495  }
496 
498 
504  field one_field(sl = sl::current()) const;
505 
507 
512  field_ref one_field_ref(sl = sl::current()) const;
513 
514  // NOLINTEND(modernize-use-nodiscard)
515 
517  [[nodiscard]] encoding_group get_encoding_group() const noexcept
518  {
519  return m_encoding;
520  }
521 
522 private:
523  using data_pointer = std::shared_ptr<internal::pq::PGresult const>;
524 
526  void check_one_row(sl loc) const
527  {
528  auto const sz{size()};
529  if (sz != 1)
530  {
531  if (not m_query or m_query->empty())
532  throw unexpected_rows{
533  std::format("Expected 1 row from query, got {}.", sz), loc};
534  else
535  throw unexpected_rows{
536  std::format("Expected 1 row from query '{}', got {}.", *m_query, sz),
537  loc};
538  }
539  }
540 
542  data_pointer m_data;
543 
545  PQXX_PURE [[nodiscard]] std::shared_ptr<std::string const>
546  query_ptr() const noexcept
547  {
548  return m_query;
549  }
550 
551  // TODO: Could we colocate some members in a single struct?
553  std::shared_ptr<std::string const> m_query;
554 
556 
560  std::shared_ptr<pqxx::internal::notice_waiters> m_notice_waiters;
561 
563 
564  static std::string const s_empty_string;
565 
567  PQXX_PURE PQXX_RETURNS_NONNULL [[nodiscard]] char const *
568  get_value(size_type row, row_size_type col) const noexcept;
569  PQXX_PURE [[nodiscard]] bool
570  get_is_null(size_type row, row_size_type col) const noexcept;
571  PQXX_PURE [[nodiscard]] field_size_type
572  get_length(size_type, row_size_type) const noexcept;
573 
575  result(
576  std::shared_ptr<internal::pq::PGresult> const &rhs,
577  std::shared_ptr<std::string> const &query,
578  std::shared_ptr<pqxx::internal::notice_waiters> const &waiters,
579  encoding_group enc);
580 
581  PQXX_PRIVATE void check_status(std::string_view desc, sl loc) const;
582 
584  friend class pqxx::internal::gate::result_row;
585  bool operator!() const noexcept { return m_data.get() == nullptr; }
586  // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
587  operator bool() const noexcept { return m_data.get() != nullptr; }
588 
589  [[noreturn]] PQXX_COLD PQXX_PRIVATE void
590  throw_sql_error(std::string const &Err, std::string const &Query, sl) const;
591  PQXX_PURE PQXX_PRIVATE [[nodiscard]] int errorposition() const;
592  PQXX_PRIVATE [[nodiscard]] std::string status_error(sl) const;
593 
595  PQXX_PURE [[nodiscard]] char const *cmd_status() const noexcept;
596 };
597 } // namespace pqxx
598 #endif
Iterator for rows in a result. Use as result::const_iterator.
Definition: result_iterator.hxx:35
Reverse iterator for result. Use as result::const_reverse_iterator.
Definition: result_iterator.hxx:173
Lightweight reference to a field in a result set.
Definition: field.hxx:52
Reference to a field in a result set.
Definition: field.hxx:309
Definition: result-connection.hxx:9
Definition: result-creation.hxx:9
Definition: result-field_ref.hxx:9
Definition: result-pipeline.hxx:9
Definition: result-sql_cursor.hxx:6
Result set containing data returned by a query or command.
Definition: result.hxx:101
encoding_group get_encoding_group() const noexcept
Retrieve encoding group for this result's client encoding.
Definition: result.hxx:517
result_size_type size_type
Definition: result.hxx:103
bool operator!=(result const &rhs) const noexcept
Compare two results for inequality.
Definition: result.hxx:161
result const & no_rows(sl loc=sl::current()) const
Expect that result contains no rows. Return result for convenience.
Definition: result.hxx:465
void clear() noexcept
Let go of the result's data.
Definition: result.hxx:242
row_size_type table_column(zview col_name, sl loc=sl::current()) const
What column in its table did this column come from?
Definition: result.hxx:341
result_difference_type difference_type
Definition: result.hxx:104
result const & expect_columns(row_size_type cols, sl loc=sl::current()) const
Expect that result consists of exactly cols columns.
Definition: result.hxx:476
result() noexcept=default
oid column_table(zview col_name, sl loc=sl::current()) const
What table did this column come from?
Definition: result.hxx:322
Lightweight reference to one row in a result.
Definition: row.hxx:57
Reference to one row in a result.
Definition: row.hxx:415
Marker-type wrapper: zero-terminated std::string_view.
Definition: zview.hxx:55
Query returned an unexpected number of rows.
Definition: except.hxx:664
Error in usage of libpqxx library, similar to std::logic_error.
Definition: except.hxx:580
#define PQXX_COLD
Definition: header-pre.hxx:72
#define PQXX_LIBEXPORT
Definition: header-pre.hxx:206
#define PQXX_PURE
Definition: header-pre.hxx:64
#define PQXX_RETURNS_NONNULL
Definition: header-pre.hxx:119
#define PQXX_PRIVATE
Definition: header-pre.hxx:207
Definition: connection.hxx:94
void PGresult
Placeholder for libpq's result type.
Definition: types.hxx:431
Private namespace for libpqxx's internal use; do not access.
Definition: connection.cxx:333
PQXX_LIBEXPORT void clear_result(pq::PGresult const *) noexcept
C++ wrapper for libpq's PQclear.
Definition: result.cxx:79
The home of all libpqxx classes, functions, templates, etc.
Definition: array.cxx:26
int row_size_type
Number of fields in a row of database data.
Definition: types.hxx:83
std::source_location sl
Convenience alias for std::source_location. It's just too long.
Definition: types.hxx:38
std::size_t field_size_type
Number of bytes in a field of database data.
Definition: types.hxx:89
int result_difference_type
Difference between result sizes.
Definition: types.hxx:80
encoding_group
Definition: encoding_group.hxx:40
@ unknown
Default: indeterminate encoding. All we know is it supports ASCII.
int result_size_type
Number of rows in a result set.
Definition: types.hxx:77
unsigned int oid
PostgreSQL database row identifier.
Definition: types.hxx:73
constexpr bool operator==(char const lhs[], pqxx::zview rhs) noexcept
Disambiguating comparison operator: leave it to std::string_view.
Definition: zview.hxx:203
format
Format code: is data text or binary?
Definition: types.hxx:121
Various callbacks waiting for a notice to come in.
Definition: result.hxx:57
std::function< void(zview)> notice_handler
Definition: result.hxx:59
notice_waiters(notice_waiters &&)=delete
notice_waiters(notice_waiters const &)=delete
notice_waiters & operator=(notice_waiters const &)=delete
std::list< errorhandler * > errorhandlers
Definition: result.hxx:60
notice_waiters & operator=(notice_waiters &&)=delete