libpqxx
The C++ client library for PostgreSQL
util.hxx
Go to the documentation of this file.
1 /* Various utility definitions for libpqxx.
2  *
3  * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/util instead.
4  *
5  * Copyright (c) 2000-2026, Jeroen T. Vermeulen.
6  *
7  * See COPYING for copyright license. If you did not receive a file called
8  * COPYING with this source code, please notify the distributor of this
9  * mistake, or contact the author.
10  */
11 #ifndef PQXX_UTIL_HXX
12 #define PQXX_UTIL_HXX
13 
14 #if !defined(PQXX_HEADER_PRE)
15 # error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
16 #endif
17 
18 #include <cassert>
19 #include <cctype>
20 #include <cerrno>
21 #include <cmath>
22 #include <cstring>
23 #include <format>
24 #include <functional>
25 #include <iterator>
26 #include <limits>
27 #include <memory>
28 #include <stdexcept>
29 #include <string>
30 #include <string_view>
31 #include <type_traits>
32 #include <typeinfo>
33 #include <utility>
34 #include <vector>
35 
36 #include "pqxx/except.hxx"
37 #include "pqxx/types.hxx"
38 #include "pqxx/version.hxx"
39 
40 
42 namespace pqxx
43 {} // namespace pqxx
44 
45 
46 // C++23: Retire wrapper.
47 // PQXX_UNREACHABLE: equivalent to `std::unreachable()` if available.
48 #if defined(__cpp_lib_unreachable) && __cpp_lib_unreachable
49 # define PQXX_UNREACHABLE std::unreachable()
50 #else
51 # define PQXX_UNREACHABLE [[unlikely]] while (false)
52 #endif
53 
54 
56 
65 namespace pqxx::internal
66 {} // namespace pqxx::internal
67 
68 
69 namespace pqxx
70 {
71 using namespace std::literals;
72 
73 
75 
78 template<typename TO, typename FROM>
79 inline TO
80 check_cast(FROM value, std::string_view description, sl loc = sl::current())
81  requires(
82  std::is_arithmetic_v<FROM> and std::is_arithmetic_v<TO> and
83  (std::is_integral_v<FROM> == std::is_integral_v<TO>))
84 {
85  // The rest of this code won't quite work for bool, but bool is trivially
86  // convertible to other arithmetic types as far as I can see.
87  if constexpr (std::is_same_v<FROM, bool>)
88  return static_cast<TO>(value);
89 
90  using to_limits = std::numeric_limits<TO>;
91 
92  if constexpr (std::is_integral_v<FROM>)
93  {
94  // Integral value. These are simple, but only thanks to the standard
95  // library's safe comparison functions.
96  if (std::cmp_less(value, to_limits::lowest()))
97  throw range_error{
99  "Underflow casting {} from {} to {}: {}", value, name_type<FROM>(),
100  name_type<TO>(), description),
101  loc};
102  if (std::cmp_greater(value, (to_limits::max)()))
103  throw range_error{
104  std::format(
105  "Overflow casting {} from {} to {}: {}", value, name_type<FROM>(),
106  name_type<TO>(), description),
107  loc};
108  }
109  else if (std::isinf(value))
110  {
111  // Floating-point infinities. These will always exceed TO's upper or lower
112  // limit, but that's fine; they will translate directly to a TO infinity.
113  }
114  else
115  {
116  // NaN, or a regular floating-point value. A NaN will never be less than
117  // or greater than any value, such as TO's upper/lower bounds.
118  if (value < to_limits::lowest())
119  throw range_error{
120  std::format(
121  "Underflow casting {} from {} to {}: {}", value, name_type<FROM>(),
122  name_type<TO>(), description),
123  loc};
124  if (value > (to_limits::max)())
125  throw range_error{
126  std::format(
127  "Overflow casting {} from {} to {}: {}", value, name_type<FROM>(),
128  name_type<TO>(), description),
129  loc};
130  }
131 
132  return static_cast<TO>(value);
133 }
134 
135 
136 // TODO: No longer useful as of PostgreSQL 17: libpq is always thread-safe.
138 
141 {
143  std::string description;
144 
146  bool safe_libpq = false;
147 
149 
155  bool safe_kerberos = false;
156 };
157 
158 
161 
162 
164 
173 struct byte_char_traits final : std::char_traits<char>
174 {
175  using char_type = std::byte;
176 
177  static void assign(std::byte &a, const std::byte &b) noexcept { a = b; }
178  static bool eq(std::byte a, std::byte b) { return a == b; }
179  static bool lt(std::byte a, std::byte b) { return a < b; }
180 
181  static int compare(const std::byte *a, const std::byte *b, std::size_t size)
182  {
183  return std::memcmp(a, b, size);
184  }
185 
187  /* This would be nonsense: we can't determine the length of a random sequence
188  * of bytes. There is no terminating zero like there is for C strings.
189  *
190  * But `std::char_traits` requires us to provide this function, so we
191  * declare it without defining it.
192  */
193  static size_t length(const std::byte *data);
194 
195  PQXX_RETURNS_NONNULL static const std::byte *
196  find(const std::byte *data, std::size_t size, const std::byte &value)
197  {
198  return static_cast<const std::byte *>(
199  std::memchr(data, static_cast<int>(value), size));
200  }
201 
202  PQXX_RETURNS_NONNULL static std::byte *
203  move(std::byte *dest, const std::byte *src, std::size_t size)
204  {
205  return static_cast<std::byte *>(std::memmove(dest, src, size));
206  }
207 
208  PQXX_RETURNS_NONNULL static std::byte *
209  copy(std::byte *dest, const std::byte *src, std::size_t size)
210  {
211  return static_cast<std::byte *>(std::memcpy(dest, src, size));
212  }
213 
214  PQXX_RETURNS_NONNULL static std::byte *
215  assign(std::byte *dest, std::size_t size, std::byte value)
216  {
217  return static_cast<std::byte *>(
218  std::memset(dest, static_cast<int>(value), size));
219  }
220 
222  static int_type not_eof(int_type value);
223 
224  static std::byte to_char_type(int_type value) { return std::byte(value); }
225 
226  static int_type to_int_type(std::byte value) { return int_type(value); }
227 
228  static bool eq_int_type(int_type a, int_type b) { return a == b; }
229 
231  static int_type eof();
232 };
233 
234 // Supress warnings from potentially using a deprecated generic
235 // std::char_traits.
236 // Necessary for libc++ 18.
238 
240 using bytes = std::vector<std::byte>;
241 
243 
244 
246 
260 template<potential_binary TYPE> inline bytes_view binary_cast(TYPE const &data)
261 {
262  using item_t = value_type<TYPE>;
263  return std::as_bytes(
264  std::span<item_t const>{std::data(data), std::size(data)});
265 }
266 
267 
269 
275 template<char_sized CHAR, typename SIZE>
276 bytes_view binary_cast(CHAR const *data, SIZE size)
277 {
278  return binary_cast(std::span<CHAR>{data, check_cast<std::size_t>(size)});
279 }
280 
281 
283 constexpr oid oid_none{0};
284 
285 
287 
290 template<typename... T>
291 [[maybe_unused, deprecated("Use [[maybe_unused]], std::ignore, etc.")]]
292 inline constexpr void ignore_unused(T &&...) noexcept
293 {}
294 
295 
296 // clang-tidy rule bug:
297 // NOLINTBEGIN(
298 // cppcoreguidelines-pro-bounds-array-to-pointer-decay,
299 // hicpp-no-array-decay
300 // )
301 
303 
306 template<typename HAYSTACK, typename NEEDLE>
307 inline bool str_contains(HAYSTACK const &haystack, NEEDLE const &needle)
308  requires(
309  std::same_as<HAYSTACK, std::string> or
310  std::same_as<HAYSTACK, std::string_view>)
311 {
312  // C++23: Replace with `haystack.contains(needle)`. Retire wrapper.
313  return haystack.find(needle) != HAYSTACK::npos;
314 }
315 // NOLINTEND(
316 // cppcoreguidelines-pro-bounds-array-to-pointer-decay,
317 // hicpp-no-array-decay
318 // )
319 
320 
322 
325 template<typename SL>
326 concept c_source_location = requires(SL const loc) {
327  { loc.file_name() } -> std::convertible_to<char const *>;
328  { loc.function_name() } -> std::convertible_to<char const *>;
329  { loc.line() } -> std::convertible_to<std::uint_least32_t>;
330  { loc.column() } -> std::convertible_to<std::uint_least32_t>;
331 };
332 
334 
338 template<c_source_location LOC>
339 PQXX_PURE inline std::string source_loc(LOC const &loc)
340 {
341  char const *const file{loc.file_name()};
342  assert(file != nullptr);
343 
344  char const *const func{loc.function_name()};
345  unsigned const line{loc.line()}, column{loc.column()};
346 
347  // (The standard says this can't be null, but let's be conservative.)
348  bool const have_func{func != nullptr and *func != '\0'}, have_line{line > 0},
349  have_column{column > 0};
350 
351  if (have_func and have_line and have_column)
352  {
353  return std::format("{}:{}:{}: ({})", file, line, column, func);
354  }
355  else if (have_func and have_line)
356  {
357  return std::format("{}:{}: ({})", file, line, func);
358  }
359  else if (have_line and have_column)
360  {
361  return std::format("{}:{}:{}:", file, line, column);
362  }
363  else if (have_func)
364  {
365  // (In this case we don't care whether we have a column.)
366  return std::format("{}: ({})", file, func);
367  }
368  else if (have_line)
369  {
370  return std::format("{}:{}:", file, line);
371  }
372  else
373  {
374  return std::format("{}:", file);
375  }
376 }
377 } // namespace pqxx
378 
379 
380 namespace pqxx::internal
381 {
382 using namespace std::literals;
383 
384 
386 
401  int apps_major, int apps_minor, int apps_patch,
402  std::string_view apps_version);
403 
404 
406 PQXX_ZARGS inline constexpr char const *as_c_string(char const str[]) noexcept
407 {
408  return str;
409 }
411 template<std::size_t N>
412 inline constexpr char const *as_c_string(char (&str)[N]) noexcept
413 {
414  return str;
415 }
417 template<std::size_t N>
418 inline constexpr char const *as_c_string(char const (&str)[N]) noexcept
419 {
420  return str;
421 }
423 inline constexpr char const *as_c_string(std::string const &str) noexcept
424 {
425  return str.c_str();
426 }
427 
428 
429 // LCOV_EXCL_START
431 
435 template<typename CHAR> inline constexpr bool is_digit(CHAR c) noexcept
436 {
437  return (c >= '0') and (c <= '9');
438 }
439 
440 
441 #if !defined(NDEBUG)
442 static_assert(is_digit('0'));
443 static_assert(is_digit('1'));
444 static_assert(is_digit('9'));
445 static_assert(not is_digit('a'));
446 static_assert(not is_digit('f'));
447 static_assert(not is_digit('z'));
448 static_assert(not is_digit(' '));
449 #endif
450 // LCOV_EXCL_STOP
451 
452 
454 
456 [[nodiscard]] std::string
457 describe_object(std::string_view class_name, std::string_view name);
458 
459 
461 
473  void const *old_guest, std::string_view old_class, std::string_view old_name,
474  void const *new_guest, std::string_view new_class,
475  std::string_view new_name);
476 
477 
479 
483  void const *old_guest, std::string_view old_class, std::string_view old_name,
484  void const *new_guest, std::string_view new_class,
485  std::string_view new_name);
486 
487 
489 
492 PQXX_PURE inline constexpr std::size_t
493 size_esc_bin(std::size_t binary_bytes) noexcept
494 {
495  assert(std::cmp_less(
496  binary_bytes, (std::numeric_limits<std::size_t>::max)() / 2u));
497  return 2 + (2 * binary_bytes) + 1;
498 }
499 
500 
502 
504 PQXX_PURE inline constexpr std::size_t
505 size_unesc_bin(std::size_t escaped_bytes) noexcept
506 {
507  if (escaped_bytes < 2u) [[unlikely]]
508  return 0;
509  else
510  return (escaped_bytes - 2) / 2;
511 }
512 
513 
515 
519 PQXX_LIBEXPORT void
520 esc_bin(bytes_view binary_data, std::span<char> buffer) noexcept;
521 
522 
524 
528 template<binary T>
529 inline void esc_bin(T &&binary_data, std::span<char> buffer) noexcept
530 {
531  esc_bin(binary_cast(binary_data), buffer);
532 }
533 
534 
536 PQXX_LIBEXPORT std::string esc_bin(bytes_view binary_data);
537 
538 
540 PQXX_LIBEXPORT void
541 unesc_bin(std::string_view escaped_data, std::span<std::byte> buffer, sl loc);
542 
543 
545 PQXX_LIBEXPORT bytes unesc_bin(std::string_view escaped_data, sl loc);
546 
547 
549 
553 template<typename RETURN, typename... ARGS>
554 std::tuple<ARGS...> args_f(RETURN (&func)(ARGS...));
555 
556 
558 
562 template<typename RETURN, typename... ARGS>
563 std::tuple<ARGS...> args_f(std::function<RETURN(ARGS...)> const &);
564 
565 
567 
571 template<typename CLASS, typename RETURN, typename... ARGS>
572 std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...));
573 
574 
576 
580 template<typename CLASS, typename RETURN, typename... ARGS>
581 std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...) const);
582 
583 
585 
591 template<typename CALLABLE>
592 auto args_f(CALLABLE const &f)
593  -> decltype(member_args_f(&CALLABLE::operator()));
594 
595 
597 template<typename CALLABLE>
598 using args_t = decltype(args_f(std::declval<CALLABLE>()));
599 
600 
602 
605 template<typename... TYPES>
606 std::tuple<std::remove_cvref_t<TYPES>...>
607 strip_types(std::tuple<TYPES...> const &);
608 
609 
611 template<typename... TYPES>
612 using strip_types_t = decltype(strip_types(std::declval<TYPES...>()));
613 
614 
615 // LCOV_EXCL_START
617 PQXX_PURE inline constexpr char unescape_char(char escaped) noexcept
618 {
619  switch (escaped)
620  {
621  case 'b': // Backspace.
622  [[unlikely]] return '\b';
623  case 'f': // Form feed
624  [[unlikely]] return '\f';
625  case 'n': // Line feed.
626  return '\n';
627  case 'r': // Carriage return.
628  return '\r';
629  case 't': // Horizontal tab.
630  return '\t';
631  case 'v': // Vertical tab.
632  return '\v';
633  default: break;
634  }
635  // Regular character ("self-escaped").
636  return escaped;
637 }
638 
639 
640 #if !defined(NDEBUG)
641 static_assert(unescape_char('a') == 'a');
642 static_assert(unescape_char('b') == '\b');
643 static_assert(unescape_char('f') == '\f');
644 static_assert(unescape_char('n') == '\n');
645 static_assert(unescape_char('r') == '\r');
646 static_assert(unescape_char('t') == '\t');
647 static_assert(unescape_char('v') == '\v');
648 static_assert(unescape_char('z') == 'z');
649 #endif
650 // LCOV_EXCL_STOP
651 
652 
654 
667 [[maybe_unused]] PQXX_COLD inline char const *
668 make_strerror_rs_result(int err_result, std::span<char> buffer)
669 {
670  if (err_result == 0)
671  return std::data(buffer);
672  else
673  return "Unknown error; could not retrieve error string.";
674 }
675 
676 
678 
683 [[maybe_unused]] PQXX_COLD PQXX_ZARGS inline char const *
684 make_strerror_rs_result(char const *err_result, std::span<char>)
685 {
686  return err_result;
687 }
688 
689 
691 [[nodiscard]] PQXX_COLD inline char const *error_string(
692  [[maybe_unused]] int err_num, [[maybe_unused]] std::span<char> buffer)
693 {
694  // Not entirely clear whether strerror_s will be in std or global namespace.
695  // NOLINTNEXTLINE(google-build-using-namespace)
696  using namespace std;
697 
698 #if defined(PQXX_HAVE_STERROR_S) || defined(PQXX_HAVE_STRERROR_R)
699 # if defined(PQXX_HAVE_STRERROR_S)
700  auto const err_result{
701  strerror_s(std::data(buffer), std::size(buffer), err_num)};
702 # else
703  auto const err_result{
704  strerror_r(err_num, std::data(buffer), std::size(buffer))};
705 # endif
706  return make_strerror_rs_result(err_result, buffer);
707 #else
708  // Fallback case, hopefully for no actual platforms out there.
709  return "(No error information available.)";
710 #endif
711 }
712 
713 
715 
730 template<bool terminate>
731 inline std::size_t copy_chars(
732  std::string_view src, std::span<char> dst, std::size_t dst_offset, sl loc)
733 {
734  auto const sz{std::size(src)};
735  if (std::cmp_greater(
736  dst_offset + sz + std::size_t(terminate), std::size(dst)))
737  throw conversion_overrun{
738  std::format(
739  "Text copy exceeded buffer space: tried to copy {} bytes '{}' into a "
740  "buffer of {} bytes, at offset {}.",
741  sz, src, std::size(dst), dst_offset),
742  loc};
743  auto at{dst_offset + src.copy(std::data(dst) + dst_offset, sz)};
744  if constexpr (terminate)
745  dst[at++] = '\0';
746  return at;
747 }
748 } // namespace pqxx::internal
749 
750 
751 namespace pqxx::internal::pq
752 {
754 PQXX_LIBEXPORT void pqfreemem(void const *) noexcept;
755 } // namespace pqxx::internal::pq
756 #endif
Could not convert value to string: not enough buffer space.
Definition: except.hxx:638
Something is out of range, similar to std::out_of_range.
Definition: except.hxx:651
#define PQXX_ZARGS
Definition: header-pre.hxx:136
#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_NOINLINE
Never inline this function.
Definition: header-pre.hxx:111
#define PQXX_RETURNS_NONNULL
Definition: header-pre.hxx:119
Placeholders for libpq declarations.
Definition: util.cxx:298
void pqfreemem(void const *ptr) noexcept
Wrapper for PQfreemem(), with C++ linkage.
Definition: util.cxx:299
Private namespace for libpqxx's internal use; do not access.
Definition: connection.cxx:333
constexpr PQXX_ZARGS char const * as_c_string(char const str[]) noexcept
Get a raw C string pointer.
Definition: util.hxx:406
void unesc_bin(std::string_view escaped_data, std::span< std::byte > buffer, sl loc)
Reconstitute binary data from its escaped version.
Definition: util.cxx:194
std::tuple< ARGS... > args_f(RETURN(&func)(ARGS...))
Helper for determining a function's parameter types.
int check_libpqxx_version(int apps_major, int apps_minor, int apps_patch, std::string_view apps_version)
Check library binary version against application's expectations.
Definition: util.cxx:258
constexpr PQXX_PURE std::size_t size_esc_bin(std::size_t binary_bytes) noexcept
Compute buffer size needed to escape binary data for use as a BYTEA.
Definition: util.hxx:493
std::tuple< ARGS... > member_args_f(RETURN(CLASS::*)(ARGS...))
Helper for determining a member function's parameter types.
void check_unique_unregister(void const *old_guest, std::string_view old_class, std::string_view old_name, void const *new_guest, std::string_view new_class, std::string_view new_name)
Like check_unique_register, but for un-registering a guest.
Definition: util.cxx:82
std::size_t copy_chars(std::string_view src, std::span< char > dst, std::size_t dst_offset, sl loc)
Copy text from src into buf at offset dst_offset.
Definition: util.hxx:731
decltype(strip_types(std::declval< TYPES... >())) strip_types_t
Take a tuple type and apply std::remove_cvref_t to its component types.
Definition: util.hxx:612
PQXX_COLD char const * make_strerror_rs_result(int err_result, std::span< char > buffer)
Helper for avoiding type trouble with strerror_r()/strerror_s().
Definition: util.hxx:668
void check_unique_register(void const *old_guest, std::string_view old_class, std::string_view old_name, void const *new_guest, std::string_view new_class, std::string_view new_name)
Check validity of registering a new "guest" in a "host.".
Definition: util.cxx:63
std::string describe_object(std::string_view class_name, std::string_view name)
Describe an object for humans, based on class name and optional name.
Definition: util.cxx:53
decltype(args_f(std::declval< CALLABLE >())) args_t
A callable's parameter types, as a tuple.
Definition: util.hxx:598
constexpr bool is_digit(CHAR c) noexcept
A safer and more generic replacement for std::isdigit.
Definition: util.hxx:435
constexpr PQXX_PURE char unescape_char(char escaped) noexcept
Return original byte for escaped character.
Definition: util.hxx:617
constexpr PQXX_PURE std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept
Compute binary size from the size of its escaped version.
Definition: util.hxx:505
PQXX_COLD char const * error_string([[maybe_unused]] int err_num, [[maybe_unused]] std::span< char > buffer)
Get error string for a given errno value.
Definition: util.hxx:691
void esc_bin(bytes_view binary_data, std::span< char > buffer) noexcept
Hex-escape binary data into a buffer.
Definition: util.cxx:159
std::tuple< std::remove_cvref_t< TYPES >... > strip_types(std::tuple< TYPES... > const &)
Apply std::remove_cvref_t to each of a tuple type's component types.
The home of all libpqxx classes, functions, templates, etc.
Definition: array.cxx:26
std::span< std::byte const > bytes_view
Type alias for a view of bytes.
Definition: types.hxx:188
std::source_location sl
Convenience alias for std::source_location. It's just too long.
Definition: types.hxx:38
bytes_view binary_cast(TYPE const &data)
Cast binary data to a type that libpqxx will recognise as binary.
Definition: util.hxx:260
std::remove_cvref_t< std::ranges::range_value_t< CONTAINER > > value_type
The type of a container's elements.
Definition: types.hxx:138
requires(pqxx::internal::to_buf_7< TYPE > or pqxx::internal::to_buf_8< TYPE >) const eval bool supports_to_buf_8()
Is the libpqxx 8 version of to_buf() supported for TYPE?
Definition: strconv.hxx:417
PQXX_LIBEXPORT thread_safety_model describe_thread_safety()
Describe thread safety available in this build.
Definition: util.cxx:35
unsigned int oid
PostgreSQL database row identifier.
Definition: types.hxx:73
std::string description
A human-readable description of any thread-safety issues.
Definition: util.hxx:143
TO check_cast(FROM value, std::string_view description, sl loc=sl::current()) requires(std
Cast a numeric value to another type, or throw if it underflows/overflows.
Definition: util.hxx:80
std::vector< std::byte > bytes
Type alias for a container containing bytes.
Definition: util.hxx:240
constexpr oid oid_none
The "null" oid.
Definition: util.hxx:283
format
Format code: is data text or binary?
Definition: types.hxx:121
Descriptor of library's thread-safety model.
Definition: util.hxx:141
Custom std::char_trast if the compiler does not provide one.
Definition: util.hxx:174
static PQXX_RETURNS_NONNULL std::byte * assign(std::byte *dest, std::size_t size, std::byte value)
Definition: util.hxx:215
static PQXX_RETURNS_NONNULL const std::byte * find(const std::byte *data, std::size_t size, const std::byte &value)
Definition: util.hxx:196
static PQXX_RETURNS_NONNULL std::byte * move(std::byte *dest, const std::byte *src, std::size_t size)
Definition: util.hxx:203
static size_t length(const std::byte *data)
Deliberately undefined: "guess" the length of an array of bytes.
static void assign(std::byte &a, const std::byte &b) noexcept
Definition: util.hxx:177
static std::byte to_char_type(int_type value)
Definition: util.hxx:224
static PQXX_RETURNS_NONNULL std::byte * copy(std::byte *dest, const std::byte *src, std::size_t size)
Definition: util.hxx:209
static int_type not_eof(int_type value)
Declared but not defined: makes no sense for binary data.
static bool eq(std::byte a, std::byte b)
Definition: util.hxx:178
static int_type eof()
Declared but not defined: makes no sense for binary data.
static bool lt(std::byte a, std::byte b)
Definition: util.hxx:179
static int_type to_int_type(std::byte value)
Definition: util.hxx:226
static bool eq_int_type(int_type a, int_type b)
Definition: util.hxx:228
std::byte char_type
Definition: util.hxx:175
static int compare(const std::byte *a, const std::byte *b, std::size_t size)
Definition: util.hxx:181