| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- /* Various utility definitions for libpqxx.
- *
- * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/util instead.
- *
- * Copyright (c) 2000-2022, 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 <cctype>
- #include <cstdio>
- #include <functional>
- #include <iterator>
- #include <limits>
- #include <memory>
- #include <stdexcept>
- #include <string>
- #include <string_view>
- #include <type_traits>
- #include <typeinfo>
- #include <utility>
- #include <vector>
- #if __has_include(<version>)
- # include <version>
- #endif
- #include "pqxx/except.hxx"
- #include "pqxx/internal/encodings.hxx"
- #include "pqxx/types.hxx"
- #include "pqxx/version.hxx"
- /// The home of all libpqxx classes, functions, templates, etc.
- namespace pqxx
- {}
- #include <pqxx/internal/libpq-forward.hxx>
- /// Internal items for libpqxx' own use. Do not use these yourself.
- namespace pqxx::internal
- {
- // C++20: Retire wrapper.
- /// Same as `std::cmp_less`, or a workaround where that's not available.
- 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.
- /// C++20 std::cmp_greater, or workaround if not available.
- 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.
- /// C++20 std::cmp_less_equal, or workaround if not available.
- 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.
- /// C++20 std::cmp_greater_equal, or workaround if not available.
- 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
- }
- /// Efficiently concatenate two strings.
- /** This is a special case of concatenate(), needed because dependency
- * management does not let us use that function here.
- */
- [[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;
- /// Suppress compiler warning about an unused item.
- template<typename... T> inline constexpr void ignore_unused(T &&...) noexcept
- {}
- /// Cast a numeric value to another type, or throw if it underflows/overflows.
- /** Both types must be arithmetic types, and they must either be both integral
- * or both floating-point types.
- */
- 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);
- }
- /** Check library version at link time.
- *
- * Ensures a failure when linking an application against a radically
- * different libpqxx version than the one against which it was compiled.
- *
- * Sometimes application builds fail in unclear ways because they compile
- * using headers from libpqxx version X, but then link against libpqxx
- * binary version Y. A typical scenario would be one where you're building
- * against a libpqxx which you have built yourself, but a different version
- * is installed on the system.
- *
- * The check_library_version template is declared for any library version,
- * but only actually defined for the version of the libpqxx binary against
- * which the code is linked.
- *
- * If the library binary is a different version than the one declared in
- * these headers, then this call will fail to link: there will be no
- * definition for the function with these exact template parameter values.
- * There will be a definition, but the version in the parameter values will
- * be different.
- */
- 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);
- }
- /// Descriptor of library's thread-safety model.
- /** This describes what the library knows about various risks to thread-safety.
- */
- struct PQXX_LIBEXPORT thread_safety_model
- {
- /// Is the underlying libpq build thread-safe?
- bool safe_libpq = false;
- /// Is Kerberos thread-safe?
- /** @warning Is currently always `false`.
- *
- * If your application uses Kerberos, all accesses to libpqxx or Kerberos
- * must be serialized. Confine their use to a single thread, or protect it
- * with a global lock.
- */
- bool safe_kerberos = false;
- /// A human-readable description of any thread-safety issues.
- std::string description;
- };
- /// Describe thread safety available in this build.
- [[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
- /// Cast binary data to a type that libpqxx will recognise as binary.
- /** There are many different formats for storing binary data in memory. You
- * may have yours as a `std::string`, or a `std::vector<uchar_t>`, or one of
- * many other types.
- *
- * But for libpqxx to recognise your data as binary, it needs to be a
- * `std::basic_string<std::byte>`, or a `std::basic_string_view<std::byte>`;
- * or in C++20 or better, any contiguous block of `std::byte`.
- *
- * Use `binary_cast` as a convenience helper to cast your data as a
- * `std::basic_string_view<std::byte>`.
- *
- * @warning There are two things you should be aware of! First, the data must
- * be contiguous in memory. In C++20 the compiler will enforce this, but in
- * C++17 it's your own problem. Second, you must keep the object where you
- * store the actual data alive for as long as you might use this function's
- * return value.
- */
- template<PQXX_POTENTIAL_BINARY_ARG TYPE>
- std::basic_string_view<std::byte> binary_cast(TYPE const &data)
- {
- static_assert(sizeof(value_type<TYPE>) == 1);
- 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
- /// Construct a type that libpqxx will recognise as binary.
- /** Takes a data pointer and a size, without being too strict about their
- * types, and constructs a `std::basic_string_view<std::byte>` pointing to
- * the same data.
- *
- * This makes it a little easier to turn binary data, in whatever form you
- * happen to have it, into binary data as libpqxx understands it.
- */
- template<PQXX_CHAR_SIZED_ARG CHAR, typename SIZE>
- std::basic_string_view<std::byte> 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")};
- }
- /// The "null" oid.
- constexpr oid oid_none{0};
- } // namespace pqxx
- /// Private namespace for libpqxx's internal use; do not access.
- /** This namespace hides definitions internal to libpqxx. These are not
- * supposed to be used by client programs, and they may change at any time
- * without notice.
- *
- * Conversely, if you find something in this namespace tremendously useful, by
- * all means do lodge a request for its publication.
- *
- * @warning Here be dragons!
- */
- namespace pqxx::internal
- {
- using namespace std::literals;
- /// A safer and more generic replacement for `std::isdigit`.
- /** Turns out `std::isdigit` isn't as easy to use as it sounds. It takes an
- * `int`, but requires it to be nonnegative. Which means it's an outright
- * liability on systems where `char` is signed.
- */
- template<typename CHAR> inline constexpr bool is_digit(CHAR c) noexcept
- {
- return (c >= '0') and (c <= '9');
- }
- /// Describe an object for humans, based on class name and optional name.
- /** Interprets an empty name as "no name given."
- */
- [[nodiscard]] std::string
- describe_object(std::string_view class_name, std::string_view name);
- /// Check validity of registering a new "guest" in a "host."
- /** The host might be e.g. a connection, and the guest a transaction. The
- * host can only have one guest at a time, so it is an error to register a new
- * guest while the host already has a guest.
- *
- * If the new registration is an error, this function throws a descriptive
- * exception.
- *
- * Pass the old guest (if any) and the new guest (if any), for both, a type
- * name (at least if the guest is not null), and optionally an object name
- * (but which may be omitted if the caller did not assign one).
- */
- 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);
- /// Like @ref check_unique_register, but for un-registering a guest.
- /** Pass the guest which was registered, as well as the guest which is being
- * unregistered, so that the function can check that they are the same one.
- */
- 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);
- /// Compute buffer size needed to escape binary data for use as a BYTEA.
- /** This uses the hex-escaping format. The return value includes room for the
- * "\x" prefix.
- */
- inline constexpr std::size_t size_esc_bin(std::size_t binary_bytes) noexcept
- {
- return 2 + (2 * binary_bytes) + 1;
- }
- /// Compute binary size from the size of its escaped version.
- /** Do not include a terminating zero in `escaped_bytes`.
- */
- 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".
- /// Hex-escape binary data into a buffer.
- /** The buffer must be able to accommodate
- * `size_esc_bin(std::size(binary_data))` bytes, and the function will write
- * exactly that number of bytes into the buffer. This includes a trailing
- * zero.
- */
- void PQXX_LIBEXPORT
- esc_bin(std::basic_string_view<std::byte> binary_data, char buffer[]) noexcept;
- /// Hex-escape binary data into a std::string.
- std::string PQXX_LIBEXPORT
- esc_bin(std::basic_string_view<std::byte> binary_data);
- /// Reconstitute binary data from its escaped version.
- void PQXX_LIBEXPORT
- unesc_bin(std::string_view escaped_data, std::byte buffer[]);
- /// Reconstitute binary data from its escaped version.
- std::basic_string<std::byte>
- PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data);
- /// Transitional: std::ssize(), or custom implementation if not available.
- template<typename T> auto ssize(T const &c)
- {
- #if defined(__cpp_lib_ssize) && __cplusplus >= __cpp_lib_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 // __cpp_lib_ssize
- }
- /// Helper for determining a function's parameter types.
- /** This function has no definition. It's not meant to be actually called.
- * It's just there for pattern-matching in the compiler, so we can use its
- * hypothetical return value.
- */
- template<typename RETURN, typename... ARGS>
- std::tuple<ARGS...> args_f(RETURN (&func)(ARGS...));
- /// Helper for determining a `std::function`'s parameter types.
- /** This function has no definition. It's not meant to be actually called.
- * It's just there for pattern-matching in the compiler, so we can use its
- * hypothetical return value.
- */
- template<typename RETURN, typename... ARGS>
- std::tuple<ARGS...> args_f(std::function<RETURN(ARGS...)> const &);
- /// Helper for determining a member function's parameter types.
- /** This function has no definition. It's not meant to be actually called.
- * It's just there for pattern-matching in the compiler, so we can use its
- * hypothetical return value.
- */
- template<typename CLASS, typename RETURN, typename... ARGS>
- std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...));
- /// Helper for determining a const member function's parameter types.
- /** This function has no definition. It's not meant to be actually called.
- * It's just there for pattern-matching in the compiler, so we can use its
- * hypothetical return value.
- */
- template<typename CLASS, typename RETURN, typename... ARGS>
- std::tuple<ARGS...> member_args_f(RETURN (CLASS::*)(ARGS...) const);
- /// Helper for determining a callable type's parameter types.
- /** This specialisation should work for lambdas.
- *
- * This function has no definition. It's not meant to be actually called.
- * It's just there for pattern-matching in the compiler, so we can use its
- * hypothetical return value.
- */
- template<typename CALLABLE>
- auto args_f(CALLABLE const &f)
- -> decltype(member_args_f(&CALLABLE::operator()));
- /// A callable's parameter types, as a tuple.
- template<typename CALLABLE>
- using args_t = decltype(args_f(std::declval<CALLABLE>()));
- /// Helper: Apply `strip_t` to each of a tuple type's component types.
- /** This function has no definition. It is not meant to be called, only to be
- * used to deduce the right types.
- */
- template<typename... TYPES>
- std::tuple<strip_t<TYPES>...> strip_types(std::tuple<TYPES...> const &);
- /// Take a tuple type and apply @ref strip_t to its component types.
- template<typename... TYPES>
- using strip_types_t = decltype(strip_types(std::declval<TYPES...>()));
- } // namespace pqxx::internal
- #endif
|