libpqxx
The C++ client library for PostgreSQL
util.hxx
1 /* Various utility definitions for libpqxx.
2  *
3  * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/util instead.
4  *
5  * Copyright (c) 2000-2025, 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_H_UTIL
12 #define PQXX_H_UTIL
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 <cstdio>
22 #include <cstring>
23 #include <functional>
24 #include <iterator>
25 #include <limits>
26 #include <memory>
27 #include <stdexcept>
28 #include <string>
29 #include <string_view>
30 #include <type_traits>
31 #include <typeinfo>
32 #include <utility>
33 #include <vector>
34 
35 #include "pqxx/except.hxx"
36 #include "pqxx/types.hxx"
37 #include "pqxx/version.hxx"
38 
39 
41 namespace pqxx
42 {}
43 
44 #include <pqxx/internal/libpq-forward.hxx>
45 
46 
47 // C++23: Retire wrapper.
48 // PQXX_UNREACHABLE: equivalent to `std::unreachable()` if available.
49 #if !defined(__cpp_lib_unreachable)
50 # define PQXX_UNREACHABLE while (false)
51 #elif !__cpp_lib_unreachable
52 # define PQXX_UNREACHABLE while (false)
53 #else
54 # define PQXX_UNREACHABLE std::unreachable()
55 #endif
56 
57 
59 namespace pqxx::internal
60 {
61 
62 // C++20: Retire wrapper.
64 template<typename LEFT, typename RIGHT>
65 inline constexpr bool cmp_less(LEFT lhs, RIGHT rhs) noexcept
66 {
67 #if defined(PQXX_HAVE_CMP)
68  return std::cmp_less(lhs, rhs);
69 #else
70  // We need a variable just because lgtm.com gives off a false positive
71  // warning when we compare the values directly. It considers that a
72  // "self-comparison."
73  constexpr bool left_signed{std::is_signed_v<LEFT>};
74  if constexpr (left_signed == std::is_signed_v<RIGHT>)
75  return lhs < rhs;
76  else if constexpr (std::is_signed_v<LEFT>)
77  return (lhs <= 0) ? true : (std::make_unsigned_t<LEFT>(lhs) < rhs);
78  else
79  return (rhs <= 0) ? false : (lhs < std::make_unsigned_t<RIGHT>(rhs));
80 #endif
81 }
82 
83 
84 // C++20: Retire wrapper.
86 template<typename LEFT, typename RIGHT>
87 inline constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
88 {
89 #if defined(PQXX_HAVE_CMP)
90  return std::cmp_greater(lhs, rhs);
91 #else
92  return cmp_less(rhs, lhs);
93 #endif
94 }
95 
96 
97 // C++20: Retire wrapper.
99 template<typename LEFT, typename RIGHT>
100 inline constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept
101 {
102 #if defined(PQXX_HAVE_CMP)
103  return std::cmp_less_equal(lhs, rhs);
104 #else
105  return not cmp_less(rhs, lhs);
106 #endif
107 }
108 
109 
110 // C++20: Retire wrapper.
112 template<typename LEFT, typename RIGHT>
113 inline constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept
114 {
115 #if defined(PQXX_HAVE_CMP)
116  return std::cmp_greater_equal(lhs, rhs);
117 #else
118  return not cmp_less(lhs, rhs);
119 #endif
120 }
121 
122 
124 
127 [[nodiscard]] inline std::string cat2(std::string_view x, std::string_view y)
128 {
129  std::string buf;
130  auto const xs{std::size(x)}, ys{std::size(y)};
131  buf.resize(xs + ys);
132  x.copy(std::data(buf), xs);
133  y.copy(std::data(buf) + xs, ys);
134  return buf;
135 }
136 } // namespace pqxx::internal
137 
138 
139 namespace pqxx
140 {
141 using namespace std::literals;
142 
144 template<typename... T> inline constexpr void ignore_unused(T &&...) noexcept
145 {}
146 
147 
149 
152 template<typename TO, typename FROM>
153 inline TO check_cast(FROM value, std::string_view description)
154 {
155  static_assert(std::is_arithmetic_v<FROM>);
156  static_assert(std::is_arithmetic_v<TO>);
157  static_assert(std::is_integral_v<FROM> == std::is_integral_v<TO>);
158 
159  // The rest of this code won't quite work for bool, but bool is trivially
160  // convertible to other arithmetic types as far as I can see.
161  if constexpr (std::is_same_v<FROM, bool>)
162  return static_cast<TO>(value);
163 
164  // Depending on our "if constexpr" conditions, this parameter may not be
165  // needed. Some compilers will warn.
166  ignore_unused(description);
167 
168  using from_limits = std::numeric_limits<decltype(value)>;
169  using to_limits = std::numeric_limits<TO>;
170  if constexpr (std::is_signed_v<FROM>)
171  {
172  if constexpr (std::is_signed_v<TO>)
173  {
174  if (value < to_limits::lowest())
175  throw range_error{internal::cat2("Cast underflow: "sv, description)};
176  }
177  else
178  {
179  // FROM is signed, but TO is not. Treat this as a special case, because
180  // there may not be a good broader type in which the compiler can even
181  // perform our check.
182  if (value < 0)
184  "Casting negative value to unsigned type: "sv, description)};
185  }
186  }
187  else
188  {
189  // No need to check: the value is unsigned so can't fall below the range
190  // of the TO type.
191  }
192 
193  if constexpr (std::is_integral_v<FROM>)
194  {
195  using unsigned_from = std::make_unsigned_t<FROM>;
196  using unsigned_to = std::make_unsigned_t<TO>;
197  constexpr auto from_max{static_cast<unsigned_from>((from_limits::max)())};
198  constexpr auto to_max{static_cast<unsigned_to>((to_limits::max)())};
199  if constexpr (from_max > to_max)
200  {
201  if (internal::cmp_greater(value, to_max))
202  throw range_error{internal::cat2("Cast overflow: "sv, description)};
203  }
204  }
205  else if constexpr ((from_limits::max)() > (to_limits::max)())
206  {
207  if (value > (to_limits::max)())
208  throw range_error{internal::cat2("Cast overflow: ", description)};
209  }
210 
211  return static_cast<TO>(value);
212 }
213 
214 
236 inline PQXX_PRIVATE void check_version() noexcept
237 {
238  // There is no particular reason to do this here in @ref connection, except
239  // to ensure that every meaningful libpqxx client will execute it. The call
240  // must be in the execution path somewhere or the compiler won't try to link
241  // it. We can't use it to initialise a global or class-static variable,
242  // because a smart compiler might resolve it at compile time.
243  //
244  // On the other hand, we don't want to make a useless function call too
245  // often for performance reasons. A local static variable is initialised
246  // only on the definition's first execution. Compilers will be well
247  // optimised for this behaviour, so there's a minimal one-time cost.
248  static auto const version_ok{internal::PQXX_VERSION_CHECK()};
249  ignore_unused(version_ok);
250 }
251 
252 
254 
256 struct PQXX_LIBEXPORT thread_safety_model
257 {
259  bool safe_libpq = false;
260 
262 
268  bool safe_kerberos = false;
269 
271  std::string description;
272 };
273 
274 
276 [[nodiscard]] PQXX_LIBEXPORT thread_safety_model describe_thread_safety();
277 
278 
279 #if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_RANGES)
280 # define PQXX_POTENTIAL_BINARY_ARG pqxx::potential_binary
281 #else
282 # define PQXX_POTENTIAL_BINARY_ARG typename
283 #endif
284 
286 
290 struct byte_char_traits : std::char_traits<char>
291 {
292  using char_type = std::byte;
293 
294  static void assign(std::byte &a, const std::byte &b) noexcept { a = b; }
295  static bool eq(std::byte a, std::byte b) { return a == b; }
296  static bool lt(std::byte a, std::byte b) { return a < b; }
297 
298  static int compare(const std::byte *a, const std::byte *b, std::size_t size)
299  {
300  return std::memcmp(a, b, size);
301  }
302 
304  /* This is nonsense: we can't determine the length of a random sequence of
305  * bytes. There is no terminating zero like there is for C strings.
306  *
307  * But `std::char_traits` requires us to provide this function, so we
308  * declare it without defining it.
309  */
310  static size_t length(const std::byte *data);
311 
312  static const std::byte *
313  find(const std::byte *data, std::size_t size, const std::byte &value)
314  {
315  return static_cast<const std::byte *>(
316  std::memchr(data, static_cast<int>(value), size));
317  }
318 
319  static std::byte *
320  move(std::byte *dest, const std::byte *src, std::size_t size)
321  {
322  return static_cast<std::byte *>(std::memmove(dest, src, size));
323  }
324 
325  static std::byte *
326  copy(std::byte *dest, const std::byte *src, std::size_t size)
327  {
328  return static_cast<std::byte *>(std::memcpy(dest, src, size));
329  }
330 
331  static std::byte *assign(std::byte *dest, std::size_t size, std::byte value)
332  {
333  return static_cast<std::byte *>(
334  std::memset(dest, static_cast<int>(value), size));
335  }
336 
338  static int_type not_eof(int_type value);
339 
340  static std::byte to_char_type(int_type value) { return std::byte(value); }
341 
342  static int_type to_int_type(std::byte value) { return int_type(value); }
343 
344  static bool eq_int_type(int_type a, int_type b) { return a == b; }
345 
347  static int_type eof();
348 };
349 
350 template<typename TYPE, typename = void>
351 struct has_generic_char_traits : std::false_type
352 {};
353 
354 template<typename TYPE>
356  TYPE, std::void_t<decltype(std::char_traits<TYPE>::eof)>> : std::true_type
357 {};
358 
359 inline constexpr bool has_generic_bytes_char_traits =
361 
362 // Supress warnings from potentially using a deprecated generic
363 // std::char_traits.
364 // Necessary for libc++ 18.
365 #include "pqxx/internal/ignore-deprecated-pre.hxx"
366 
367 // C++20: Change this type.
369 /* Required to support standard libraries without a generic implementation for
370  * `std::char_traits<std::byte>`.
371  * @warn Will change to `std::vector<std::byte>` in the next major release.
372  */
373 using bytes = std::conditional<
374  has_generic_bytes_char_traits, std::basic_string<std::byte>,
375  std::basic_string<std::byte, byte_char_traits>>::type;
376 
377 // C++20: Change this type.
379 /* Required to support standard libraries without a generic implementation for
380  * `std::char_traits<std::byte>`.
381  * @warn Will change to `std::span<std::byte>` in the next major release.
382  */
383 using bytes_view = std::conditional<
384  has_generic_bytes_char_traits, std::basic_string_view<std::byte>,
385  std::basic_string_view<std::byte, byte_char_traits>>::type;
386 
387 #include "pqxx/internal/ignore-deprecated-post.hxx"
388 
389 
391 
408 template<PQXX_POTENTIAL_BINARY_ARG TYPE>
409 bytes_view binary_cast(TYPE const &data)
410 {
411  static_assert(sizeof(value_type<TYPE>) == 1);
412  // C++20: Use std::as_bytes.
413  return {
414  reinterpret_cast<std::byte const *>(
415  const_cast<strip_t<decltype(*std::data(data))> const *>(
416  std::data(data))),
417  std::size(data)};
418 }
419 
420 
421 #if defined(PQXX_HAVE_CONCEPTS)
422 template<typename CHAR>
423 concept char_sized = (sizeof(CHAR) == 1);
424 # define PQXX_CHAR_SIZED_ARG char_sized
425 #else
426 # define PQXX_CHAR_SIZED_ARG typename
427 #endif
428 
430 
436 template<PQXX_CHAR_SIZED_ARG CHAR, typename SIZE>
437 bytes_view binary_cast(CHAR const *data, SIZE size)
438 {
439  static_assert(sizeof(CHAR) == 1);
440  return {
441  reinterpret_cast<std::byte const *>(data),
442  check_cast<std::size_t>(size, "binary data size")};
443 }
444 
445 
447 constexpr oid oid_none{0};
448 } // namespace pqxx
449 
450 
452 
461 namespace pqxx::internal
462 {
463 using namespace std::literals;
464 
465 
467 
471 template<typename CHAR> inline constexpr bool is_digit(CHAR c) noexcept
472 {
473  return (c >= '0') and (c <= '9');
474 }
475 
476 
478 
480 [[nodiscard]] std::string
481 describe_object(std::string_view class_name, std::string_view name);
482 
483 
485 
496 void check_unique_register(
497  void const *old_guest, std::string_view old_class, std::string_view old_name,
498  void const *new_guest, std::string_view new_class,
499  std::string_view new_name);
500 
501 
503 
506 void check_unique_unregister(
507  void const *old_guest, std::string_view old_class, std::string_view old_name,
508  void const *new_guest, std::string_view new_class,
509  std::string_view new_name);
510 
511 
513 
516 inline constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept
517 {
518  return 2 + (2 * binary_bytes) + 1;
519 }
520 
521 
523 
525 inline constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept
526 {
527  return (escaped_bytes - 2) / 2;
528 }
529 
530 
531 // TODO: Use actual binary type for "data".
533 
538 void PQXX_LIBEXPORT esc_bin(bytes_view binary_data, char buffer[]) noexcept;
539 
540 
542 std::string PQXX_LIBEXPORT esc_bin(bytes_view binary_data);
543 
544 
546 void PQXX_LIBEXPORT
547 unesc_bin(std::string_view escaped_data, std::byte buffer[]);
548 
549 
551 bytes PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data);
552 
553 
555 template<typename T> auto ssize(T const &c)
556 {
557 #if defined(PQXX_HAVE_SSIZE)
558  return std::ssize(c);
559 #else
560  using signed_t = std::make_signed_t<decltype(std::size(c))>;
561  return static_cast<signed_t>(std::size(c));
562 #endif // PQXX_HAVE_SSIZe
563 }
564 
565 
567 
571 template<typename RETURN, typename... ARGS>
572 std::tuple<ARGS...> args_f(RETURN (&func)(ARGS...));
573 
574 
576 
580 template<typename RETURN, typename... ARGS>
581 std::tuple<ARGS...> args_f(std::function<RETURN(ARGS...)> const &);
582 
583 
585 
589 template<typename CLASS, typename RETURN, typename... ARGS>
590 std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...));
591 
592 
594 
598 template<typename CLASS, typename RETURN, typename... ARGS>
599 std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...) const);
600 
601 
603 
609 template<typename CALLABLE>
610 auto args_f(CALLABLE const &f)
611  -> decltype(member_args_f(&CALLABLE::operator()));
612 
613 
615 template<typename CALLABLE>
616 using args_t = decltype(args_f(std::declval<CALLABLE>()));
617 
618 
620 
623 template<typename... TYPES>
624 std::tuple<strip_t<TYPES>...> strip_types(std::tuple<TYPES...> const &);
625 
626 
628 template<typename... TYPES>
629 using strip_types_t = decltype(strip_types(std::declval<TYPES...>()));
630 
631 
633 inline constexpr char unescape_char(char escaped) noexcept
634 {
635  switch (escaped)
636  {
637  case 'b': // Backspace.
638  PQXX_UNLIKELY return '\b';
639  case 'f': // Form feed
640  PQXX_UNLIKELY return '\f';
641  case 'n': // Line feed.
642  return '\n';
643  case 'r': // Carriage return.
644  return '\r';
645  case 't': // Horizontal tab.
646  return '\t';
647  case 'v': // Vertical tab.
648  return '\v';
649  default: break;
650  }
651  // Regular character ("self-escaped").
652  return escaped;
653 }
654 
655 
656 // C++20: std::span?
658 template<std::size_t BYTES>
659 char const *PQXX_COLD
660 error_string(int err_num, std::array<char, BYTES> &buffer)
661 {
662  // Not entirely clear whether strerror_s will be in std or global namespace.
663  using namespace std;
664 
665 #if defined(PQXX_HAVE_STERROR_S) || defined(PQXX_HAVE_STRERROR_R)
666 # if defined(PQXX_HAVE_STRERROR_S)
667  auto const err_result{strerror_s(std::data(buffer), BYTES, err_num)};
668 # else
669  auto const err_result{strerror_r(err_num, std::data(buffer), BYTES)};
670 # endif
671  if constexpr (std::is_same_v<pqxx::strip_t<decltype(err_result)>, char *>)
672  {
673  // GNU version of strerror_r; returns the error string, which may or may
674  // not reside within buffer.
675  return err_result;
676  }
677  else
678  {
679  // Either strerror_s or POSIX strerror_r; returns an error code.
680  // Sorry for being lazy here: Not reporting error string for the case
681  // where we can't retrieve an error string.
682  if (err_result == 0)
683  return std::data(buffer);
684  else
685  return "Compound errors.";
686  }
687 
688 #else
689  // Fallback case, hopefully for no actual platforms out there.
690  pqxx::ignore_unused(err_num, buffer);
691  return "(No error information available.)";
692 #endif
693 }
694 } // namespace pqxx::internal
695 
696 
697 namespace pqxx::internal::pq
698 {
700 PQXX_LIBEXPORT void pqfreemem(void const *) noexcept;
701 } // namespace pqxx::internal::pq
702 #endif
bytes_view binary_cast(TYPE const &data)
End a code block started by "ignore-deprecated-pre.hxx".
Definition: util.hxx:409
constexpr 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:525
auto ssize(T const &c)
Transitional: std::ssize(), or custom implementation if not available.
Definition: util.hxx:555
TO check_cast(FROM value, std::string_view description)
Cast a numeric value to another type, or throw if it underflows/overflows.
Definition: util.hxx:153
Internal items for libpqxx' own use. Do not use these yourself.
Definition: encodings.cxx:32
constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater_equal, or workaround if not available.
Definition: util.hxx:113
PQXX_LIBEXPORT thread_safety_model describe_thread_safety()
Describe thread safety available in this build.
Definition: util.cxx:35
Definition: util.hxx:351
std::tuple< strip_t< TYPES >... > strip_types(std::tuple< TYPES... > const &)
Helper: Apply strip_t to each of a tuple type's component types.
Something is out of range, similar to std::out_of_range.
Definition: except.hxx:325
Descriptor of library's thread-safety model.
Definition: util.hxx:256
constexpr bool is_digit(CHAR c) noexcept
A safer and more generic replacement for std::isdigit.
Definition: util.hxx:471
std::string description
A human-readable description of any thread-safety issues.
Definition: util.hxx:271
std::tuple< ARGS... > args_f(RETURN(&func)(ARGS...))
Helper for determining a function's parameter types.
constexpr char unescape_char(char escaped) noexcept
Return original byte for escaped character.
Definition: util.hxx:633
Custom std::char_trast if the compiler does not provide one.
Definition: util.hxx:290
constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater, or workaround if not available.
Definition: util.hxx:87
constexpr 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:516
char const *PQXX_COLD error_string(int err_num, std::array< char, BYTES > &buffer)
Get error string for a given errno value.
Definition: util.hxx:660
The home of all libpqxx classes, functions, templates, etc.
Definition: array.cxx:26
constexpr void ignore_unused(T &&...) noexcept
Suppress compiler warning about an unused item.
Definition: util.hxx:144
std::conditional< has_generic_bytes_char_traits, std::basic_string< std::byte >, std::basic_string< std::byte, byte_char_traits >>::type bytes
Type alias for a container containing bytes.
Definition: util.hxx:375
decltype(strip_types(std::declval< TYPES... >())) strip_types_t
Take a tuple type and apply strip_t to its component types.
Definition: util.hxx:629
unsigned int oid
PostgreSQL database row identifier.
Definition: libpq-forward.hxx:33
strip_t< decltype(*std::begin(std::declval< CONTAINER >()))> value_type
The type of a container's elements.
Definition: types.hxx:96
PQXX_PRIVATE void check_version() noexcept
Definition: util.hxx:236
std::string cat2(std::string_view x, std::string_view y)
Efficiently concatenate two strings.
Definition: util.hxx:127
std::conditional< has_generic_bytes_char_traits, std::basic_string_view< std::byte >, std::basic_string_view< std::byte, byte_char_traits >>::type bytes_view
Type alias for a view of bytes.
Definition: util.hxx:385
Forward declarations of libpq types as needed in libpqxx headers.
Definition: util.cxx:203
constexpr oid oid_none
The "null" oid.
Definition: util.hxx:447
std::remove_cv_t< std::remove_reference_t< TYPE >> strip_t
Remove any constness, volatile, and reference-ness from a type.
Definition: types.hxx:80
constexpr bool cmp_less(LEFT lhs, RIGHT rhs) noexcept
Same as std::cmp_less, or a workaround where that's not available.
Definition: util.hxx:65
decltype(args_f(std::declval< CALLABLE >())) args_t
A callable's parameter types, as a tuple.
Definition: util.hxx:616
constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_less_equal, or workaround if not available.
Definition: util.hxx:100