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
41namespace pqxx
42{}
43
44#include <pqxx/internal/libpq-forward.hxx>
45
46
47// C++23: Retire wrapper.
48#if pqxx_have_unreachable
50# define PQXX_UNREACHABLE std::unreachable()
51#else
52# define PQXX_UNREACHABLE assert(false)
53#endif
54
55
57namespace pqxx::internal
58{
59
60// C++20: Retire wrapper.
62template<typename LEFT, typename RIGHT>
63inline 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.
84template<typename LEFT, typename RIGHT>
85inline 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.
97template<typename LEFT, typename RIGHT>
98inline 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.
110template<typename LEFT, typename RIGHT>
111inline 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
137namespace pqxx
138{
139using namespace std::literals;
140
142template<typename... T> inline constexpr void ignore_unused(T &&...) noexcept
143{}
144
145
147
150template<typename TO, typename FROM>
151inline 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
234inline 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
254struct 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
302template<PQXX_POTENTIAL_BINARY_ARG TYPE>
303std::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)
316template<typename CHAR>
317concept 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
331template<PQXX_CHAR_SIZED_ARG CHAR, typename SIZE>
332std::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
342constexpr oid oid_none{0};
343} // namespace pqxx
344
345
347
356namespace pqxx::internal
357{
358using namespace std::literals;
359
360
362
366template<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
376describe_object(std::string_view class_name, std::string_view name);
377
378
380
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
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
411inline 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
420inline 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
433void PQXX_LIBEXPORT
434esc_bin(std::basic_string_view<std::byte> binary_data, char buffer[]) noexcept;
435
436
438std::string PQXX_LIBEXPORT
439esc_bin(std::basic_string_view<std::byte> binary_data);
440
441
443void PQXX_LIBEXPORT
444unesc_bin(std::string_view escaped_data, std::byte buffer[]);
445
446
448std::basic_string<std::byte>
449 PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data);
450
451
453template<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
469template<typename RETURN, typename... ARGS>
470std::tuple<ARGS...> args_f(RETURN (&func)(ARGS...));
471
472
474
478template<typename RETURN, typename... ARGS>
479std::tuple<ARGS...> args_f(std::function<RETURN(ARGS...)> const &);
480
481
483
487template<typename CLASS, typename RETURN, typename... ARGS>
488std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...));
489
490
492
496template<typename CLASS, typename RETURN, typename... ARGS>
497std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...) const);
498
499
501
507template<typename CALLABLE>
508auto args_f(CALLABLE const &f)
509 -> decltype(member_args_f(&CALLABLE::operator()));
510
511
513template<typename CALLABLE>
514using args_t = decltype(args_f(std::declval<CALLABLE>()));
515
516
518
521template<typename... TYPES>
522std::tuple<strip_t<TYPES>...> strip_types(std::tuple<TYPES...> const &);
523
524
526template<typename... TYPES>
527using strip_types_t = decltype(strip_types(std::declval<TYPES...>()));
528
529
531inline 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?
556template<std::size_t BYTES>
557char const *PQXX_COLD
558error_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{
598PQXX_LIBEXPORT void pqfreemem(void const *) noexcept;
599} // namespace pqxx::internal::pq
600#endif
The home of all libpqxx classes, functions, templates, etc.
Definition array.hxx:33
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
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
strip_t< decltype(*std::begin(std::declval< CONTAINER >()))> value_type
The type of a container's elements.
Definition types.hxx:107
void check_version() noexcept
Definition util.hxx:234
thread_safety_model describe_thread_safety()
Describe thread safety available in this build.
Definition util.cxx:33
constexpr void ignore_unused(T &&...) noexcept
Suppress compiler warning about an unused item.
Definition util.hxx:142
constexpr oid oid_none
The "null" oid.
Definition util.hxx:342
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
Internal items for libpqxx' own use. Do not use these yourself.
Definition composite.hxx:84
void unesc_bin(std::string_view escaped_data, std::byte buffer[])
Reconstitute binary data from its escaped version.
Definition util.cxx:158
int PQXX_VERSION_CHECK() noexcept
Library version check stub.
Definition version.cxx:23
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
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 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
std::tuple< ARGS... > member_args_f(RETURN(CLASS::*)(ARGS...))
Helper for determining a member function's parameter types.
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
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(strip_types(std::declval< TYPES... >())) strip_types_t
Take a tuple type and apply strip_t to its component types.
Definition util.hxx:527
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::tuple< strip_t< TYPES >... > strip_types(std::tuple< TYPES... > const &)
Helper: Apply strip_t to each of a tuple type's component types.
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
std::tuple< ARGS... > args_f(RETURN(&func)(ARGS...))
Helper for determining a function's parameter types.
constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater, or workaround if not available.
Definition util.hxx:85
decltype(args_f(std::declval< CALLABLE >())) args_t
A callable's parameter types, as a tuple.
Definition util.hxx:514
constexpr bool is_digit(CHAR c) noexcept
A safer and more generic replacement for std::isdigit.
Definition util.hxx:366
constexpr char unescape_char(char escaped) noexcept
Return original byte for escaped character.
Definition util.hxx:531
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
std::string cat2(std::string_view x, std::string_view y)
Efficiently concatenate two strings.
Definition util.hxx:125
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
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:596
void pqfreemem(void const *) noexcept
Wrapper for PQfreemem(), with C++ linkage.
Definition util.cxx:199
Something is out of range, similar to std::out_of_range.
Definition except.hxx:326
Descriptor of library's thread-safety model.
Definition util.hxx:255
std::string description
A human-readable description of any thread-safety issues.
Definition util.hxx:269