libpqxx  7.7.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-2022, 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 <cctype>
19 #include <cstdio>
20 #include <functional>
21 #include <iterator>
22 #include <limits>
23 #include <memory>
24 #include <stdexcept>
25 #include <string>
26 #include <string_view>
27 #include <type_traits>
28 #include <typeinfo>
29 #include <utility>
30 #include <vector>
31 
32 #if __has_include(<version>)
33 # include <version>
34 #endif
35 
36 #include "pqxx/except.hxx"
37 #include "pqxx/internal/encodings.hxx"
38 #include "pqxx/types.hxx"
39 #include "pqxx/version.hxx"
40 
41 
43 namespace pqxx
44 {}
45 
46 #include <pqxx/internal/libpq-forward.hxx>
47 
48 
50 namespace pqxx::internal
51 {
52 
53 
54 // C++20: Retire wrapper.
56 template<typename LEFT, typename RIGHT>
57 inline constexpr bool cmp_less(LEFT lhs, RIGHT rhs) noexcept
58 {
59 #if defined(PQXX_HAVE_CMP)
60  return std::cmp_less(lhs, rhs);
61 #else
62  // We need a variable just because lgtm.com gives off a false positive
63  // warning when we compare the values directly. It considers that a
64  // "self-comparison."
65  constexpr bool left_signed{std::is_signed_v<LEFT>};
66  if constexpr (left_signed == std::is_signed_v<RIGHT>)
67  return lhs < rhs;
68  else if constexpr (std::is_signed_v<LEFT>)
69  return (lhs <= 0) ? true : (std::make_unsigned_t<LEFT>(lhs) < rhs);
70  else
71  return (rhs <= 0) ? false : (lhs < std::make_unsigned_t<RIGHT>(rhs));
72 #endif
73 }
74 
75 
76 // C++20: Retire wrapper.
78 template<typename LEFT, typename RIGHT>
79 inline constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
80 {
81 #if defined(PQXX_HAVE_CMP)
82  return std::cmp_greater(lhs, rhs);
83 #else
84  return cmp_less(rhs, lhs);
85 #endif
86 }
87 
88 
89 // C++20: Retire wrapper.
91 template<typename LEFT, typename RIGHT>
92 inline constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept
93 {
94 #if defined(PQXX_HAVE_CMP)
95  return std::cmp_less_equal(lhs, rhs);
96 #else
97  return not cmp_less(rhs, lhs);
98 #endif
99 }
100 
101 
102 // C++20: Retire wrapper.
104 template<typename LEFT, typename RIGHT>
105 inline constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept
106 {
107 #if defined(PQXX_HAVE_CMP)
108  return std::cmp_greater_equal(lhs, rhs);
109 #else
110  return not cmp_less(lhs, rhs);
111 #endif
112 }
113 
114 
116 
119 [[nodiscard]] inline std::string cat2(std::string_view x, std::string_view y)
120 {
121  std::string buf;
122  auto const xs{std::size(x)}, ys{std::size(y)};
123  buf.resize(xs + ys);
124  x.copy(std::data(buf), xs);
125  y.copy(std::data(buf) + xs, ys);
126  return buf;
127 }
128 } // namespace pqxx::internal
129 
130 
131 namespace pqxx
132 {
133 using namespace std::literals;
134 
136 template<typename... T> inline constexpr void ignore_unused(T &&...) noexcept
137 {}
138 
139 
141 
144 template<typename TO, typename FROM>
145 inline TO check_cast(FROM value, std::string_view description)
146 {
147  static_assert(std::is_arithmetic_v<FROM>);
148  static_assert(std::is_arithmetic_v<TO>);
149  static_assert(std::is_integral_v<FROM> == std::is_integral_v<TO>);
150 
151  // The rest of this code won't quite work for bool, but bool is trivially
152  // convertible to other arithmetic types as far as I can see.
153  if constexpr (std::is_same_v<FROM, bool>)
154  return static_cast<TO>(value);
155 
156  // Depending on our "if constexpr" conditions, this parameter may not be
157  // needed. Some compilers will warn.
158  ignore_unused(description);
159 
160  using from_limits = std::numeric_limits<decltype(value)>;
161  using to_limits = std::numeric_limits<TO>;
162  if constexpr (std::is_signed_v<FROM>)
163  {
164  if constexpr (std::is_signed_v<TO>)
165  {
166  if (value < to_limits::lowest())
167  throw range_error{internal::cat2("Cast underflow: "sv, description)};
168  }
169  else
170  {
171  // FROM is signed, but TO is not. Treat this as a special case, because
172  // there may not be a good broader type in which the compiler can even
173  // perform our check.
174  if (value < 0)
176  "Casting negative value to unsigned type: "sv, description)};
177  }
178  }
179  else
180  {
181  // No need to check: the value is unsigned so can't fall below the range
182  // of the TO type.
183  }
184 
185  if constexpr (std::is_integral_v<FROM>)
186  {
187  using unsigned_from = std::make_unsigned_t<FROM>;
188  using unsigned_to = std::make_unsigned_t<TO>;
189  constexpr auto from_max{static_cast<unsigned_from>((from_limits::max)())};
190  constexpr auto to_max{static_cast<unsigned_to>((to_limits::max)())};
191  if constexpr (from_max > to_max)
192  {
193  if (internal::cmp_greater(value, to_max))
194  throw range_error{internal::cat2("Cast overflow: "sv, description)};
195  }
196  }
197  else if constexpr ((from_limits::max)() > (to_limits::max)())
198  {
199  if (value > (to_limits::max)())
200  throw range_error{internal::cat2("Cast overflow: ", description)};
201  }
202 
203  return static_cast<TO>(value);
204 }
205 
206 
228 inline PQXX_PRIVATE void check_version() noexcept
229 {
230  // There is no particular reason to do this here in @ref connection, except
231  // to ensure that every meaningful libpqxx client will execute it. The call
232  // must be in the execution path somewhere or the compiler won't try to link
233  // it. We can't use it to initialise a global or class-static variable,
234  // because a smart compiler might resolve it at compile time.
235  //
236  // On the other hand, we don't want to make a useless function call too
237  // often for performance reasons. A local static variable is initialised
238  // only on the definition's first execution. Compilers will be well
239  // optimised for this behaviour, so there's a minimal one-time cost.
240  static auto const version_ok{internal::PQXX_VERSION_CHECK()};
241  ignore_unused(version_ok);
242 }
243 
244 
246 
248 struct PQXX_LIBEXPORT thread_safety_model
249 {
251  bool safe_libpq = false;
252 
254 
260  bool safe_kerberos = false;
261 
263  std::string description;
264 };
265 
266 
268 [[nodiscard]] PQXX_LIBEXPORT thread_safety_model describe_thread_safety();
269 
270 
271 #if defined(PQXX_HAVE_CONCEPTS)
272 # define PQXX_POTENTIAL_BINARY_ARG pqxx::potential_binary
273 #else
274 # define PQXX_POTENTIAL_BINARY_ARG typename
275 #endif
276 
277 
279 
296 template<PQXX_POTENTIAL_BINARY_ARG TYPE>
297 std::basic_string_view<std::byte> binary_cast(TYPE const &data)
298 {
299  static_assert(sizeof(value_type<TYPE>) == 1);
300  return {
301  reinterpret_cast<std::byte const *>(
302  const_cast<strip_t<decltype(*std::data(data))> const *>(
303  std::data(data))),
304  std::size(data)};
305 }
306 
307 
308 #if defined(PQXX_HAVE_CONCEPTS)
309 template<typename CHAR>
310 concept char_sized = (sizeof(CHAR) == 1);
311 # define PQXX_CHAR_SIZED_ARG char_sized
312 #else
313 # define PQXX_CHAR_SIZED_ARG typename
314 #endif
315 
317 
324 template<PQXX_CHAR_SIZED_ARG CHAR, typename SIZE>
325 std::basic_string_view<std::byte> binary_cast(CHAR const *data, SIZE size)
326 {
327  static_assert(sizeof(CHAR) == 1);
328  return {
329  reinterpret_cast<std::byte const *>(data),
330  check_cast<std::size_t>(size, "binary data size")};
331 }
332 
333 
335 constexpr oid oid_none{0};
336 } // namespace pqxx
337 
338 
340 
349 namespace pqxx::internal
350 {
351 using namespace std::literals;
352 
353 
355 
359 template<typename CHAR> inline constexpr bool is_digit(CHAR c) noexcept
360 {
361  return (c >= '0') and (c <= '9');
362 }
363 
364 
366 
368 [[nodiscard]] std::string
369 describe_object(std::string_view class_name, std::string_view name);
370 
371 
373 
385  void const *old_guest, std::string_view old_class, std::string_view old_name,
386  void const *new_guest, std::string_view new_class,
387  std::string_view new_name);
388 
389 
391 
395  void const *old_guest, std::string_view old_class, std::string_view old_name,
396  void const *new_guest, std::string_view new_class,
397  std::string_view new_name);
398 
399 
401 
404 inline constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept
405 {
406  return 2 + (2 * binary_bytes) + 1;
407 }
408 
409 
411 
413 inline constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept
414 {
415  return (escaped_bytes - 2) / 2;
416 }
417 
418 
419 // TODO: Use actual binary type for "data".
421 
426 void PQXX_LIBEXPORT
427 esc_bin(std::basic_string_view<std::byte> binary_data, char buffer[]) noexcept;
428 
429 
431 std::string PQXX_LIBEXPORT
432 esc_bin(std::basic_string_view<std::byte> binary_data);
433 
434 
436 void PQXX_LIBEXPORT
437 unesc_bin(std::string_view escaped_data, std::byte buffer[]);
438 
439 
441 std::basic_string<std::byte>
442  PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data);
443 
444 
446 template<typename T> auto ssize(T const &c)
447 {
448 #if defined(__cpp_lib_ssize) && __cplusplus >= __cpp_lib_ssize
449  return std::ssize(c);
450 #else
451  using signed_t = std::make_signed_t<decltype(std::size(c))>;
452  return static_cast<signed_t>(std::size(c));
453 #endif // __cpp_lib_ssize
454 }
455 } // namespace pqxx::internal
456 #endif
Something is out of range, similar to std::out_of_range.
Definition: except.hxx:201
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:51
Descriptor of library&#39;s thread-safety model.
Definition: util.hxx:248
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:404
The home of all libpqxx classes, functions, templates, etc.
Definition: array.hxx:26
auto ssize(T const &c)
Transitional: std::ssize(), or custom implementation if not available.
Definition: util.hxx:446
constexpr bool is_digit(CHAR c) noexcept
A safer and more generic replacement for std::isdigit.
Definition: util.hxx:359
constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater, or workaround if not available.
Definition: util.hxx:79
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:61
std::string description
A human-readable description of any thread-safety issues.
Definition: util.hxx:263
void check_version() noexcept
Definition: util.hxx:228
void esc_bin(std::basic_string_view< std::byte > binary_data, char buffer[]) noexcept
Hex-escape binary data into a buffer.
Definition: util.cxx:126
constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_less_equal, or workaround if not available.
Definition: util.hxx:92
constexpr oid oid_none
The "null" oid.
Definition: util.hxx:335
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:78
decltype(*std::begin(std::declval< CONTAINER >())) value_type
The type of a container&#39;s elements.
Definition: types.hxx:107
int PQXX_VERSION_CHECK() noexcept
Library version check stub.
Definition: version.cxx:23
constexpr void ignore_unused(T &&...) noexcept
Suppress compiler warning about an unused item.
Definition: util.hxx:136
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
Internal items for libpqxx&#39; own use. Do not use these yourself.
Definition: composite.hxx:82
thread_safety_model describe_thread_safety()
Describe thread safety available in this build.
Definition: util.cxx:33
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:145
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:413
constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater_equal, or workaround if not available.
Definition: util.hxx:105
std::string cat2(std::string_view x, std::string_view y)
Efficiently concatenate two strings.
Definition: util.hxx:119
constexpr bool cmp_less(LEFT lhs, RIGHT rhs) noexcept
Same as std::cmp_less, or a workaround where that&#39;s not available.
Definition: util.hxx:57
void unesc_bin(std::string_view escaped_data, std::byte buffer[])
Reconstitute binary data from its escaped version.
Definition: util.cxx:158
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:297