libpqxx  7.8.1
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-2023, 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 #if pqxx_have_unreachable
49 # define PQXX_UNREACHABLE std::unreachable()
51 #else
52 # define PQXX_UNREACHABLE assert(false)
53 #endif
54 
55 
57 namespace pqxx::internal
58 {
59 
60 // C++20: Retire wrapper.
62 template<typename LEFT, typename RIGHT>
63 inline constexpr bool cmp_less(LEFT lhs, RIGHT rhs) noexcept
64 {
65 #if defined(PQXX_HAVE_CMP)
66  return std::cmp_less(lhs, rhs);
67 #else
68  // We need a variable just because lgtm.com gives off a false positive
69  // warning when we compare the values directly. It considers that a
70  // "self-comparison."
71  constexpr bool left_signed{std::is_signed_v<LEFT>};
72  if constexpr (left_signed == std::is_signed_v<RIGHT>)
73  return lhs < rhs;
74  else if constexpr (std::is_signed_v<LEFT>)
75  return (lhs <= 0) ? true : (std::make_unsigned_t<LEFT>(lhs) < rhs);
76  else
77  return (rhs <= 0) ? false : (lhs < std::make_unsigned_t<RIGHT>(rhs));
78 #endif
79 }
80 
81 
82 // C++20: Retire wrapper.
84 template<typename LEFT, typename RIGHT>
85 inline constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
86 {
87 #if defined(PQXX_HAVE_CMP)
88  return std::cmp_greater(lhs, rhs);
89 #else
90  return cmp_less(rhs, lhs);
91 #endif
92 }
93 
94 
95 // C++20: Retire wrapper.
97 template<typename LEFT, typename RIGHT>
98 inline constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept
99 {
100 #if defined(PQXX_HAVE_CMP)
101  return std::cmp_less_equal(lhs, rhs);
102 #else
103  return not cmp_less(rhs, lhs);
104 #endif
105 }
106 
107 
108 // C++20: Retire wrapper.
110 template<typename LEFT, typename RIGHT>
111 inline constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept
112 {
113 #if defined(PQXX_HAVE_CMP)
114  return std::cmp_greater_equal(lhs, rhs);
115 #else
116  return not cmp_less(lhs, rhs);
117 #endif
118 }
119 
120 
122 
125 [[nodiscard]] inline std::string cat2(std::string_view x, std::string_view y)
126 {
127  std::string buf;
128  auto const xs{std::size(x)}, ys{std::size(y)};
129  buf.resize(xs + ys);
130  x.copy(std::data(buf), xs);
131  y.copy(std::data(buf) + xs, ys);
132  return buf;
133 }
134 } // namespace pqxx::internal
135 
136 
137 namespace pqxx
138 {
139 using namespace std::literals;
140 
142 template<typename... T> inline constexpr void ignore_unused(T &&...) noexcept
143 {}
144 
145 
147 
150 template<typename TO, typename FROM>
151 inline TO check_cast(FROM value, std::string_view description)
152 {
153  static_assert(std::is_arithmetic_v<FROM>);
154  static_assert(std::is_arithmetic_v<TO>);
155  static_assert(std::is_integral_v<FROM> == std::is_integral_v<TO>);
156 
157  // The rest of this code won't quite work for bool, but bool is trivially
158  // convertible to other arithmetic types as far as I can see.
159  if constexpr (std::is_same_v<FROM, bool>)
160  return static_cast<TO>(value);
161 
162  // Depending on our "if constexpr" conditions, this parameter may not be
163  // needed. Some compilers will warn.
164  ignore_unused(description);
165 
166  using from_limits = std::numeric_limits<decltype(value)>;
167  using to_limits = std::numeric_limits<TO>;
168  if constexpr (std::is_signed_v<FROM>)
169  {
170  if constexpr (std::is_signed_v<TO>)
171  {
172  if (value < to_limits::lowest())
173  throw range_error{internal::cat2("Cast underflow: "sv, description)};
174  }
175  else
176  {
177  // FROM is signed, but TO is not. Treat this as a special case, because
178  // there may not be a good broader type in which the compiler can even
179  // perform our check.
180  if (value < 0)
182  "Casting negative value to unsigned type: "sv, description)};
183  }
184  }
185  else
186  {
187  // No need to check: the value is unsigned so can't fall below the range
188  // of the TO type.
189  }
190 
191  if constexpr (std::is_integral_v<FROM>)
192  {
193  using unsigned_from = std::make_unsigned_t<FROM>;
194  using unsigned_to = std::make_unsigned_t<TO>;
195  constexpr auto from_max{static_cast<unsigned_from>((from_limits::max)())};
196  constexpr auto to_max{static_cast<unsigned_to>((to_limits::max)())};
197  if constexpr (from_max > to_max)
198  {
199  if (internal::cmp_greater(value, to_max))
200  throw range_error{internal::cat2("Cast overflow: "sv, description)};
201  }
202  }
203  else if constexpr ((from_limits::max)() > (to_limits::max)())
204  {
205  if (value > (to_limits::max)())
206  throw range_error{internal::cat2("Cast overflow: ", description)};
207  }
208 
209  return static_cast<TO>(value);
210 }
211 
212 
234 inline PQXX_PRIVATE void check_version() noexcept
235 {
236  // There is no particular reason to do this here in @ref connection, except
237  // to ensure that every meaningful libpqxx client will execute it. The call
238  // must be in the execution path somewhere or the compiler won't try to link
239  // it. We can't use it to initialise a global or class-static variable,
240  // because a smart compiler might resolve it at compile time.
241  //
242  // On the other hand, we don't want to make a useless function call too
243  // often for performance reasons. A local static variable is initialised
244  // only on the definition's first execution. Compilers will be well
245  // optimised for this behaviour, so there's a minimal one-time cost.
246  static auto const version_ok{internal::PQXX_VERSION_CHECK()};
247  ignore_unused(version_ok);
248 }
249 
250 
252 
254 struct PQXX_LIBEXPORT thread_safety_model
255 {
257  bool safe_libpq = false;
258 
260 
266  bool safe_kerberos = false;
267 
269  std::string description;
270 };
271 
272 
274 [[nodiscard]] PQXX_LIBEXPORT thread_safety_model describe_thread_safety();
275 
276 
277 #if defined(PQXX_HAVE_CONCEPTS)
278 # define PQXX_POTENTIAL_BINARY_ARG pqxx::potential_binary
279 #else
280 # define PQXX_POTENTIAL_BINARY_ARG typename
281 #endif
282 
283 
285 
302 template<PQXX_POTENTIAL_BINARY_ARG TYPE>
303 std::basic_string_view<std::byte> binary_cast(TYPE const &data)
304 {
305  static_assert(sizeof(value_type<TYPE>) == 1);
306  // C++20: Use std::as_bytes.
307  return {
308  reinterpret_cast<std::byte const *>(
309  const_cast<strip_t<decltype(*std::data(data))> const *>(
310  std::data(data))),
311  std::size(data)};
312 }
313 
314 
315 #if defined(PQXX_HAVE_CONCEPTS)
316 template<typename CHAR>
317 concept char_sized = (sizeof(CHAR) == 1);
318 # define PQXX_CHAR_SIZED_ARG char_sized
319 #else
320 # define PQXX_CHAR_SIZED_ARG typename
321 #endif
322 
324 
331 template<PQXX_CHAR_SIZED_ARG CHAR, typename SIZE>
332 std::basic_string_view<std::byte> binary_cast(CHAR const *data, SIZE size)
333 {
334  static_assert(sizeof(CHAR) == 1);
335  return {
336  reinterpret_cast<std::byte const *>(data),
337  check_cast<std::size_t>(size, "binary data size")};
338 }
339 
340 
342 constexpr oid oid_none{0};
343 } // namespace pqxx
344 
345 
347 
356 namespace pqxx::internal
357 {
358 using namespace std::literals;
359 
360 
362 
366 template<typename CHAR> inline constexpr bool is_digit(CHAR c) noexcept
367 {
368  return (c >= '0') and (c <= '9');
369 }
370 
371 
373 
375 [[nodiscard]] std::string
376 describe_object(std::string_view class_name, std::string_view name);
377 
378 
380 
391 void check_unique_register(
392  void const *old_guest, std::string_view old_class, std::string_view old_name,
393  void const *new_guest, std::string_view new_class,
394  std::string_view new_name);
395 
396 
398 
401 void check_unique_unregister(
402  void const *old_guest, std::string_view old_class, std::string_view old_name,
403  void const *new_guest, std::string_view new_class,
404  std::string_view new_name);
405 
406 
408 
411 inline constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept
412 {
413  return 2 + (2 * binary_bytes) + 1;
414 }
415 
416 
418 
420 inline constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept
421 {
422  return (escaped_bytes - 2) / 2;
423 }
424 
425 
426 // TODO: Use actual binary type for "data".
428 
433 void PQXX_LIBEXPORT
434 esc_bin(std::basic_string_view<std::byte> binary_data, char buffer[]) noexcept;
435 
436 
438 std::string PQXX_LIBEXPORT
439 esc_bin(std::basic_string_view<std::byte> binary_data);
440 
441 
443 void PQXX_LIBEXPORT
444 unesc_bin(std::string_view escaped_data, std::byte buffer[]);
445 
446 
448 std::basic_string<std::byte>
449  PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data);
450 
451 
453 template<typename T> auto ssize(T const &c)
454 {
455 #if pqxx_have_ssize
456  return std::ssize(c);
457 #else
458  using signed_t = std::make_signed_t<decltype(std::size(c))>;
459  return static_cast<signed_t>(std::size(c));
460 #endif // pqxx_have_ssize
461 }
462 
463 
465 
469 template<typename RETURN, typename... ARGS>
470 std::tuple<ARGS...> args_f(RETURN (&func)(ARGS...));
471 
472 
474 
478 template<typename RETURN, typename... ARGS>
479 std::tuple<ARGS...> args_f(std::function<RETURN(ARGS...)> const &);
480 
481 
483 
487 template<typename CLASS, typename RETURN, typename... ARGS>
488 std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...));
489 
490 
492 
496 template<typename CLASS, typename RETURN, typename... ARGS>
497 std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...) const);
498 
499 
501 
507 template<typename CALLABLE>
508 auto args_f(CALLABLE const &f)
509  -> decltype(member_args_f(&CALLABLE::operator()));
510 
511 
513 template<typename CALLABLE>
514 using args_t = decltype(args_f(std::declval<CALLABLE>()));
515 
516 
518 
521 template<typename... TYPES>
522 std::tuple<strip_t<TYPES>...> strip_types(std::tuple<TYPES...> const &);
523 
524 
526 template<typename... TYPES>
527 using strip_types_t = decltype(strip_types(std::declval<TYPES...>()));
528 
529 
531 inline constexpr char unescape_char(char escaped) noexcept
532 {
533  switch (escaped)
534  {
535  case 'b': // Backspace.
536  PQXX_UNLIKELY return '\b';
537  case 'f': // Form feed
538  PQXX_UNLIKELY return '\f';
539  case 'n': // Line feed.
540  return '\n';
541  case 'r': // Carriage return.
542  return '\r';
543  case 't': // Horizontal tab.
544  return '\t';
545  case 'v': // Vertical tab.
546  return '\v';
547  default: break;
548  }
549  // Regular character ("self-escaped").
550  return escaped;
551 }
552 
553 
554 // C++20: std::span?
556 template<std::size_t BYTES>
557 char const *PQXX_COLD
558 error_string(int err_num, std::array<char, BYTES> &buffer)
559 {
560  // Not entirely clear whether strerror_s will be in std or global namespace.
561  using namespace std;
562 
563 #if defined(PQXX_HAVE_STERROR_S) || defined(PQXX_HAVE_STRERROR_R)
564 # if defined(PQXX_HAVE_STRERROR_S)
565  auto const err_result{strerror_s(std::data(buffer), BYTES, err_num)};
566 # else
567  auto const err_result{strerror_r(err_num, std::data(buffer), BYTES)};
568 # endif
569  if constexpr (std::is_same_v<pqxx::strip_t<decltype(err_result)>, char *>)
570  {
571  // GNU version of strerror_r; returns the error string, which may or may
572  // not reside within buffer.
573  return err_result;
574  }
575  else
576  {
577  // Either strerror_s or POSIX strerror_r; returns an error code.
578  // Sorry for being lazy here: Not reporting error string for the case
579  // where we can't retrieve an error string.
580  if (err_result == 0)
581  return std::data(buffer);
582  else
583  return "Compound errors.";
584  }
585 
586 #else
587  // Fallback case, hopefully for no actual platforms out there.
588  pqxx::ignore_unused(err_num, buffer);
589  return "(No error information available.)";
590 #endif
591 }
592 } // namespace pqxx::internal
593 
594 
596 {
598 PQXX_LIBEXPORT void pqfreemem(void const *) noexcept;
599 } // namespace pqxx::internal::pq
600 #endif
constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater_equal, or workaround if not available.
Definition: util.hxx:111
constexpr bool is_digit(CHAR c) noexcept
A safer and more generic replacement for std::isdigit.
Definition: util.hxx:366
Something is out of range, similar to std::out_of_range.
Definition: except.hxx:325
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:558
constexpr oid oid_none
The "null" oid.
Definition: util.hxx:342
std::tuple< ARGS... > args_f(RETURN(&func)(ARGS...))
Helper for determining a function's parameter types.
Internal items for libpqxx' own use. Do not use these yourself.
Definition: composite.hxx:83
strip_t< decltype(*std::begin(std::declval< CONTAINER >()))> value_type
The type of a container's elements.
Definition: types.hxx:107
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:151
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:32
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:63
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:411
void pqfreemem(void const *) noexcept
Wrapper for PQfreemem(), with C++ linkage.
Definition: util.cxx:199
STL namespace.
void check_version() noexcept
Definition: util.hxx:234
std::remove_cv_t< std::remove_reference_t< TYPE >> strip_t
Remove any constness, volatile, and reference-ness from a type.
Definition: types.hxx:91
Descriptor of library's thread-safety model.
Definition: util.hxx:254
decltype(args_f(std::declval< CALLABLE >())) args_t
A callable's parameter types, as a tuple.
Definition: util.hxx:514
std::string cat2(std::string_view x, std::string_view y)
Efficiently concatenate two strings.
Definition: util.hxx:125
std::string description
A human-readable description of any thread-safety issues.
Definition: util.hxx:269
decltype(strip_types(std::declval< TYPES... >())) strip_types_t
Take a tuple type and apply strip_t to its component types.
Definition: util.hxx:527
thread_safety_model describe_thread_safety()
Describe thread safety available in this build.
Definition: util.cxx:33
auto ssize(T const &c)
Transitional: std::ssize(), or custom implementation if not available.
Definition: util.hxx:453
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:420
Definition: util.hxx:595
constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater, or workaround if not available.
Definition: util.hxx:85
std::basic_string_view< std::byte > binary_cast(TYPE const &data)
Cast binary data to a type that libpqxx will recognise as binary.
Definition: util.hxx:303
constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_less_equal, or workaround if not available.
Definition: util.hxx:98
constexpr void ignore_unused(T &&...) noexcept
Suppress compiler warning about an unused item.
Definition: util.hxx:142
std::tuple< strip_t< TYPES >... > strip_types(std::tuple< TYPES... > const &)
Helper: Apply strip_t to each of a tuple type's component types.
constexpr char unescape_char(char escaped) noexcept
Return original byte for escaped character.
Definition: util.hxx:531
int PQXX_VERSION_CHECK() noexcept
Library version check stub.
Definition: version.cxx:23