Loading...
Searching...
No Matches
util.hxx
/* Various utility definitions for libpqxx.
*
* DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/util instead.
*
* Copyright (c) 2000-2024, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
#ifndef PQXX_H_UTIL
#define PQXX_H_UTIL
#if !defined(PQXX_HEADER_PRE)
# error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
#endif
#include <cassert>
#include <cctype>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <functional>
#include <iterator>
#include <limits>
#include <memory>
#include <stdexcept>
#include <string>
#include <string_view>
#include <type_traits>
#include <typeinfo>
#include <utility>
#include <vector>
#include "pqxx/except.hxx"
#include "pqxx/types.hxx"
#include "pqxx/version.hxx"
namespace pqxx
{}
#include <pqxx/internal/libpq-forward.hxx>
// C++23: Retire wrapper.
// PQXX_UNREACHABLE: equivalent to `std::unreachable()` if available.
#if !defined(__cpp_lib_unreachable)
# define PQXX_UNREACHABLE while (false)
#elif !__cpp_lib_unreachable
# define PQXX_UNREACHABLE while (false)
#else
# define PQXX_UNREACHABLE std::unreachable()
#endif
namespace pqxx::internal
{
// C++20: Retire wrapper.
template<typename LEFT, typename RIGHT>
inline constexpr bool cmp_less(LEFT lhs, RIGHT rhs) noexcept
{
#if defined(PQXX_HAVE_CMP)
return std::cmp_less(lhs, rhs);
#else
// We need a variable just because lgtm.com gives off a false positive
// warning when we compare the values directly. It considers that a
// "self-comparison."
constexpr bool left_signed{std::is_signed_v<LEFT>};
if constexpr (left_signed == std::is_signed_v<RIGHT>)
return lhs < rhs;
else if constexpr (std::is_signed_v<LEFT>)
return (lhs <= 0) ? true : (std::make_unsigned_t<LEFT>(lhs) < rhs);
else
return (rhs <= 0) ? false : (lhs < std::make_unsigned_t<RIGHT>(rhs));
#endif
}
// C++20: Retire wrapper.
template<typename LEFT, typename RIGHT>
inline constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
{
#if defined(PQXX_HAVE_CMP)
return std::cmp_greater(lhs, rhs);
#else
return cmp_less(rhs, lhs);
#endif
}
// C++20: Retire wrapper.
template<typename LEFT, typename RIGHT>
inline constexpr bool cmp_less_equal(LEFT lhs, RIGHT rhs) noexcept
{
#if defined(PQXX_HAVE_CMP)
return std::cmp_less_equal(lhs, rhs);
#else
return not cmp_less(rhs, lhs);
#endif
}
// C++20: Retire wrapper.
template<typename LEFT, typename RIGHT>
inline constexpr bool cmp_greater_equal(LEFT lhs, RIGHT rhs) noexcept
{
#if defined(PQXX_HAVE_CMP)
return std::cmp_greater_equal(lhs, rhs);
#else
return not cmp_less(lhs, rhs);
#endif
}
[[nodiscard]] inline std::string cat2(std::string_view x, std::string_view y)
{
std::string buf;
auto const xs{std::size(x)}, ys{std::size(y)};
buf.resize(xs + ys);
x.copy(std::data(buf), xs);
y.copy(std::data(buf) + xs, ys);
return buf;
}
} // namespace pqxx::internal
namespace pqxx
{
using namespace std::literals;
template<typename... T> inline constexpr void ignore_unused(T &&...) noexcept
{}
template<typename TO, typename FROM>
inline TO check_cast(FROM value, std::string_view description)
{
static_assert(std::is_arithmetic_v<FROM>);
static_assert(std::is_arithmetic_v<TO>);
static_assert(std::is_integral_v<FROM> == std::is_integral_v<TO>);
// The rest of this code won't quite work for bool, but bool is trivially
// convertible to other arithmetic types as far as I can see.
if constexpr (std::is_same_v<FROM, bool>)
return static_cast<TO>(value);
// Depending on our "if constexpr" conditions, this parameter may not be
// needed. Some compilers will warn.
ignore_unused(description);
using from_limits = std::numeric_limits<decltype(value)>;
using to_limits = std::numeric_limits<TO>;
if constexpr (std::is_signed_v<FROM>)
{
if constexpr (std::is_signed_v<TO>)
{
if (value < to_limits::lowest())
throw range_error{internal::cat2("Cast underflow: "sv, description)};
}
else
{
// FROM is signed, but TO is not. Treat this as a special case, because
// there may not be a good broader type in which the compiler can even
// perform our check.
if (value < 0)
throw range_error{internal::cat2(
"Casting negative value to unsigned type: "sv, description)};
}
}
else
{
// No need to check: the value is unsigned so can't fall below the range
// of the TO type.
}
if constexpr (std::is_integral_v<FROM>)
{
using unsigned_from = std::make_unsigned_t<FROM>;
using unsigned_to = std::make_unsigned_t<TO>;
constexpr auto from_max{static_cast<unsigned_from>((from_limits::max)())};
constexpr auto to_max{static_cast<unsigned_to>((to_limits::max)())};
if constexpr (from_max > to_max)
{
if (internal::cmp_greater(value, to_max))
throw range_error{internal::cat2("Cast overflow: "sv, description)};
}
}
else if constexpr ((from_limits::max)() > (to_limits::max)())
{
if (value > (to_limits::max)())
throw range_error{internal::cat2("Cast overflow: ", description)};
}
return static_cast<TO>(value);
}
inline PQXX_PRIVATE void check_version() noexcept
{
// There is no particular reason to do this here in @ref connection, except
// to ensure that every meaningful libpqxx client will execute it. The call
// must be in the execution path somewhere or the compiler won't try to link
// it. We can't use it to initialise a global or class-static variable,
// because a smart compiler might resolve it at compile time.
//
// On the other hand, we don't want to make a useless function call too
// often for performance reasons. A local static variable is initialised
// only on the definition's first execution. Compilers will be well
// optimised for this behaviour, so there's a minimal one-time cost.
static auto const version_ok{internal::PQXX_VERSION_CHECK()};
ignore_unused(version_ok);
}
struct PQXX_LIBEXPORT thread_safety_model
{
bool safe_libpq = false;
bool safe_kerberos = false;
std::string description;
};
[[nodiscard]] PQXX_LIBEXPORT thread_safety_model describe_thread_safety();
#if defined(PQXX_HAVE_CONCEPTS)
# define PQXX_POTENTIAL_BINARY_ARG pqxx::potential_binary
#else
# define PQXX_POTENTIAL_BINARY_ARG typename
#endif
struct byte_char_traits : std::char_traits<char>
{
using char_type = std::byte;
static void assign(std::byte &a, const std::byte &b) noexcept { a = b; }
static bool eq(std::byte a, std::byte b) { return a == b; }
static bool lt(std::byte a, std::byte b) { return a < b; }
static int compare(const std::byte *a, const std::byte *b, std::size_t size)
{
return std::memcmp(a, b, size);
}
/* This is nonsense: we can't determine the length of a random sequence of
* bytes. There is no terminating zero like there is for C strings.
*
* But `std::char_traits` requires us to provide this function, so we
* declare it without defining it.
*/
static size_t length(const std::byte *data);
static const std::byte *
find(const std::byte *data, std::size_t size, const std::byte &value)
{
return static_cast<const std::byte *>(
std::memchr(data, static_cast<int>(value), size));
}
static std::byte *
move(std::byte *dest, const std::byte *src, std::size_t size)
{
return static_cast<std::byte *>(std::memmove(dest, src, size));
}
static std::byte *
copy(std::byte *dest, const std::byte *src, std::size_t size)
{
return static_cast<std::byte *>(std::memcpy(dest, src, size));
}
static std::byte *assign(std::byte *dest, std::size_t size, std::byte value)
{
return static_cast<std::byte *>(
std::memset(dest, static_cast<int>(value), size));
}
static int_type not_eof(int_type value);
static std::byte to_char_type(int_type value) { return std::byte(value); }
static int_type to_int_type(std::byte value) { return int_type(value); }
static bool eq_int_type(int_type a, int_type b) { return a == b; }
static int_type eof();
};
template<typename TYPE, typename = void>
struct has_generic_char_traits : std::false_type
{};
template<typename TYPE>
struct has_generic_char_traits<
TYPE, std::void_t<decltype(std::char_traits<TYPE>::eof)>> : std::true_type
{};
inline constexpr bool has_generic_bytes_char_traits =
has_generic_char_traits<std::byte>::value;
// Supress warnings from potentially using a deprecated generic
// std::char_traits.
// Necessary for libc++ 18.
#include "pqxx/internal/ignore-deprecated-pre.hxx"
// C++20: Change this type.
/* Required to support standard libraries without a generic implementation for
* `std::char_traits<std::byte>`.
* @warn Will change to `std::vector<std::byte>` in the next major release.
*/
using bytes = std::conditional<
has_generic_bytes_char_traits, std::basic_string<std::byte>,
std::basic_string<std::byte, byte_char_traits>>::type;
// C++20: Change this type.
/* Required to support standard libraries without a generic implementation for
* `std::char_traits<std::byte>`.
* @warn Will change to `std::span<std::byte>` in the next major release.
*/
using bytes_view = std::conditional<
has_generic_bytes_char_traits, std::basic_string_view<std::byte>,
std::basic_string_view<std::byte, byte_char_traits>>::type;
#include "pqxx/internal/ignore-deprecated-post.hxx"
template<PQXX_POTENTIAL_BINARY_ARG TYPE>
bytes_view binary_cast(TYPE const &data)
{
static_assert(sizeof(value_type<TYPE>) == 1);
// C++20: Use std::as_bytes.
return {
reinterpret_cast<std::byte const *>(
const_cast<strip_t<decltype(*std::data(data))> const *>(
std::data(data))),
std::size(data)};
}
#if defined(PQXX_HAVE_CONCEPTS)
template<typename CHAR>
concept char_sized = (sizeof(CHAR) == 1);
# define PQXX_CHAR_SIZED_ARG char_sized
#else
# define PQXX_CHAR_SIZED_ARG typename
#endif
template<PQXX_CHAR_SIZED_ARG CHAR, typename SIZE>
bytes_view binary_cast(CHAR const *data, SIZE size)
{
static_assert(sizeof(CHAR) == 1);
return {
reinterpret_cast<std::byte const *>(data),
check_cast<std::size_t>(size, "binary data size")};
}
constexpr oid oid_none{0};
} // namespace pqxx
namespace pqxx::internal
{
using namespace std::literals;
template<typename CHAR> inline constexpr bool is_digit(CHAR c) noexcept
{
return (c >= '0') and (c <= '9');
}
[[nodiscard]] std::string
describe_object(std::string_view class_name, std::string_view name);
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);
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);
inline constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept
{
return 2 + (2 * binary_bytes) + 1;
}
inline constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept
{
return (escaped_bytes - 2) / 2;
}
// TODO: Use actual binary type for "data".
void PQXX_LIBEXPORT esc_bin(bytes_view binary_data, char buffer[]) noexcept;
std::string PQXX_LIBEXPORT esc_bin(bytes_view binary_data);
void PQXX_LIBEXPORT
unesc_bin(std::string_view escaped_data, std::byte buffer[]);
bytes PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data);
template<typename T> auto ssize(T const &c)
{
#if defined(PQXX_HAVE_SSIZE)
return std::ssize(c);
#else
using signed_t = std::make_signed_t<decltype(std::size(c))>;
return static_cast<signed_t>(std::size(c));
#endif // PQXX_HAVE_SSIZe
}
template<typename RETURN, typename... ARGS>
std::tuple<ARGS...> args_f(RETURN (&func)(ARGS...));
template<typename RETURN, typename... ARGS>
std::tuple<ARGS...> args_f(std::function<RETURN(ARGS...)> const &);
template<typename CLASS, typename RETURN, typename... ARGS>
std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...));
template<typename CLASS, typename RETURN, typename... ARGS>
std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...) const);
template<typename CALLABLE>
auto args_f(CALLABLE const &f)
-> decltype(member_args_f(&CALLABLE::operator()));
template<typename CALLABLE>
using args_t = decltype(args_f(std::declval<CALLABLE>()));
template<typename... TYPES>
std::tuple<strip_t<TYPES>...> strip_types(std::tuple<TYPES...> const &);
template<typename... TYPES>
using strip_types_t = decltype(strip_types(std::declval<TYPES...>()));
inline constexpr char unescape_char(char escaped) noexcept
{
switch (escaped)
{
case 'b': // Backspace.
PQXX_UNLIKELY return '\b';
case 'f': // Form feed
PQXX_UNLIKELY return '\f';
case 'n': // Line feed.
return '\n';
case 'r': // Carriage return.
return '\r';
case 't': // Horizontal tab.
return '\t';
case 'v': // Vertical tab.
return '\v';
default: break;
}
// Regular character ("self-escaped").
return escaped;
}
// C++20: std::span?
template<std::size_t BYTES>
char const *PQXX_COLD
error_string(int err_num, std::array<char, BYTES> &buffer)
{
// Not entirely clear whether strerror_s will be in std or global namespace.
using namespace std;
#if defined(PQXX_HAVE_STERROR_S) || defined(PQXX_HAVE_STRERROR_R)
# if defined(PQXX_HAVE_STRERROR_S)
auto const err_result{strerror_s(std::data(buffer), BYTES, err_num)};
# else
auto const err_result{strerror_r(err_num, std::data(buffer), BYTES)};
# endif
if constexpr (std::is_same_v<pqxx::strip_t<decltype(err_result)>, char *>)
{
// GNU version of strerror_r; returns the error string, which may or may
// not reside within buffer.
return err_result;
}
else
{
// Either strerror_s or POSIX strerror_r; returns an error code.
// Sorry for being lazy here: Not reporting error string for the case
// where we can't retrieve an error string.
if (err_result == 0)
return std::data(buffer);
else
return "Compound errors.";
}
#else
// Fallback case, hopefully for no actual platforms out there.
pqxx::ignore_unused(err_num, buffer);
return "(No error information available.)";
#endif
}
} // namespace pqxx::internal
namespace pqxx::internal::pq
{
PQXX_LIBEXPORT void pqfreemem(void const *) noexcept;
} // namespace pqxx::internal::pq
#endif
pqxx::range_error
Something is out of range, similar to std::out_of_range.
Definition except.hxx:326
pqxx::internal::pq
Forward declarations of libpq types as needed in libpqxx headers.
Definition util.cxx:205
pqxx::internal::pq::pqfreemem
void pqfreemem(void const *ptr) noexcept
Wrapper for PQfreemem(), with C++ linkage.
Definition util.cxx:206
pqxx::internal
Internal items for libpqxx' own use. Do not use these yourself.
Definition encodings.cxx:33
pqxx::internal::unesc_bin
void PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data, std::byte buffer[])
Reconstitute binary data from its escaped version.
Definition util.cxx:166
pqxx::internal::error_string
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
pqxx::internal::size_esc_bin
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
pqxx::internal::member_args_f
std::tuple< ARGS... > member_args_f(RETURN(CLASS::*)(ARGS...))
Helper for determining a member function's parameter types.
pqxx::internal::cmp_less
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
pqxx::internal::check_unique_unregister
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:80
pqxx::internal::esc_bin
void PQXX_LIBEXPORT esc_bin(bytes_view binary_data, char buffer[]) noexcept
Hex-escape binary data into a buffer.
Definition util.cxx:134
pqxx::internal::strip_types_t
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
pqxx::internal::check_unique_register
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
pqxx::internal::strip_types
std::tuple< strip_t< TYPES >... > strip_types(std::tuple< TYPES... > const &)
Helper: Apply strip_t to each of a tuple type's component types.
pqxx::internal::describe_object
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
pqxx::internal::args_f
std::tuple< ARGS... > args_f(RETURN(&func)(ARGS...))
Helper for determining a function's parameter types.
pqxx::internal::cmp_greater
constexpr bool cmp_greater(LEFT lhs, RIGHT rhs) noexcept
C++20 std::cmp_greater, or workaround if not available.
Definition util.hxx:87
pqxx::internal::args_t
decltype(args_f(std::declval< CALLABLE >())) args_t
A callable's parameter types, as a tuple.
Definition util.hxx:616
pqxx::internal::is_digit
constexpr bool is_digit(CHAR c) noexcept
A safer and more generic replacement for std::isdigit.
Definition util.hxx:471
pqxx::internal::unescape_char
constexpr char unescape_char(char escaped) noexcept
Return original byte for escaped character.
Definition util.hxx:633
pqxx::internal::cmp_greater_equal
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::internal::cat2
std::string cat2(std::string_view x, std::string_view y)
Efficiently concatenate two strings.
Definition util.hxx:127
pqxx::internal::cmp_less_equal
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
pqxx::internal::ssize
auto ssize(T const &c)
Transitional: std::ssize(), or custom implementation if not available.
Definition util.hxx:555
pqxx::internal::size_unesc_bin
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
pqxx
The home of all libpqxx classes, functions, templates, etc.
Definition array.cxx:27
pqxx::strip_t
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
pqxx::check_version
PQXX_PRIVATE void check_version() noexcept
Definition util.hxx:236
pqxx::binary_cast
bytes_view binary_cast(TYPE const &data)
Cast binary data to a type that libpqxx will recognise as binary.
Definition util.hxx:409
pqxx::value_type
strip_t< decltype(*std::begin(std::declval< CONTAINER >()))> value_type
The type of a container's elements.
Definition types.hxx:96
pqxx::ignore_unused
constexpr void ignore_unused(T &&...) noexcept
Suppress compiler warning about an unused item.
Definition util.hxx:144
pqxx::describe_thread_safety
PQXX_LIBEXPORT thread_safety_model describe_thread_safety()
Describe thread safety available in this build.
Definition util.cxx:35
pqxx::bytes
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
pqxx::oid
unsigned int oid
PostgreSQL database row identifier.
Definition libpq-forward.hxx:33
pqxx::thread_safety_model::description
std::string description
A human-readable description of any thread-safety issues.
Definition util.hxx:271
pqxx::bytes_view
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
pqxx::oid_none
constexpr oid oid_none
The "null" oid.
Definition util.hxx:447
pqxx::check_cast
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
pqxx::thread_safety_model
Descriptor of library's thread-safety model.
Definition util.hxx:257
pqxx::byte_char_traits
Custom std::char_trast if the compiler does not provide one.
Definition util.hxx:291
pqxx::byte_char_traits::length
static size_t length(const std::byte *data)
Deliberately undefined: "guess" the length of an array of bytes.
pqxx::byte_char_traits::not_eof
static int_type not_eof(int_type value)
Declared but not defined: makes no sense for binary data.
pqxx::byte_char_traits::eof
static int_type eof()
Declared but not defined: makes no sense for binary data.
pqxx::has_generic_char_traits
Definition util.hxx:352