Loading...
Searching...
No Matches
conversions.hxx
#include <array>
#include <cstring>
#include <map>
#include <memory>
#include <numeric>
#include <optional>
#if defined(PQXX_HAVE_SPAN) && __has_include(<span>)
# include <span>
#endif
#include <type_traits>
#include <variant>
#include <vector>
#include "pqxx/types.hxx"
#include "pqxx/util.hxx"
/* Internal helpers for string conversion, and conversion implementations.
*
* Do not include this header directly. The libpqxx headers do it for you.
*/
namespace pqxx::internal
{
inline constexpr char number_to_digit(int i) noexcept
{
return static_cast<char>(i + '0');
}
constexpr int digit_to_number(char c) noexcept
{
return c - '0';
}
std::string PQXX_LIBEXPORT
state_buffer_overrun(int have_bytes, int need_bytes);
template<typename HAVE, typename NEED>
inline std::string state_buffer_overrun(HAVE have_bytes, NEED need_bytes)
{
return state_buffer_overrun(
static_cast<int>(have_bytes), static_cast<int>(need_bytes));
}
[[noreturn]] PQXX_LIBEXPORT PQXX_COLD void
throw_null_conversion(std::string const &type);
[[noreturn]] PQXX_LIBEXPORT PQXX_COLD void
throw_null_conversion(std::string_view type);
template<typename CHAR_TYPE> struct disallowed_ambiguous_char_conversion
{
static constexpr bool converts_to_string{false};
static constexpr bool converts_from_string{false};
static char *into_buf(char *, char *, CHAR_TYPE) = delete;
static constexpr zview
to_buf(char *, char *, CHAR_TYPE const &) noexcept = delete;
static constexpr std::size_t
size_buffer(CHAR_TYPE const &) noexcept = delete;
static CHAR_TYPE from_string(std::string_view) = delete;
};
template<typename T> PQXX_LIBEXPORT extern std::string to_string_float(T);
template<typename T>
inline char *generic_into_buf(char *begin, char *end, T const &value)
{
zview const text{string_traits<T>::to_buf(begin, end, value)};
auto const space{end - begin};
// Include the trailing zero.
auto const len = std::size(text) + 1;
if (internal::cmp_greater(len, space))
throw conversion_overrun{
"Not enough buffer space to insert " + type_name<T> + ". " +
state_buffer_overrun(space, len)};
std::memmove(begin, text.data(), len);
return begin + len;
}
// C++20: Guard with concept?
template<typename T> struct integral_traits
{
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{true};
static PQXX_LIBEXPORT T from_string(std::string_view text);
static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value);
static PQXX_LIBEXPORT char *into_buf(char *begin, char *end, T const &value);
static constexpr std::size_t size_buffer(T const &) noexcept
{
return std::is_signed_v<T> + std::numeric_limits<T>::digits10 + 1 + 1;
}
};
// C++20: Guard with concept?
template<typename T> struct float_traits
{
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{true};
static PQXX_LIBEXPORT T from_string(std::string_view text);
static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value);
static PQXX_LIBEXPORT char *into_buf(char *begin, char *end, T const &value);
// Return a nonnegative integral value's number of decimal digits.
static constexpr std::size_t digits10(std::size_t value) noexcept
{
if (value < 10)
return 1;
else
return 1 + digits10(value / 10);
}
static constexpr std::size_t size_buffer(T const &) noexcept
{
using lims = std::numeric_limits<T>;
// See #328 for a detailed discussion on the maximum number of digits.
//
// In a nutshell: for the big cases, the scientific notation is always
// the shortest one, and therefore the one that to_chars will pick.
//
// So... How long can the scientific notation get? 1 (for sign) + 1 (for
// decimal point) + 1 (for 'e') + 1 (for exponent sign) + max_digits10 +
// max number of digits in the exponent + 1 (terminating zero).
//
// What's the max number of digits in the exponent? It's the max number of
// digits out of the most negative exponent and the most positive one.
//
// The longest positive exponent is easy: 1 + ceil(log10(max_exponent10)).
// (The extra 1 is because 10^n takes up 1 + n digits, not n.)
//
// The longest negative exponent is a bit harder: min_exponent10 gives us
// the smallest power of 10 which a normalised version of T can represent.
// But the smallest denormalised power of 10 that T can represent is
// another max_digits10 powers of 10 below that.
// needs a minus sign.
//
// All this stuff messes with my head a bit because it's on the order of
// log10(log10(n)). It's easy to get the number of logs wrong.
auto const max_pos_exp{digits10(lims::max_exponent10)};
// Really want std::abs(lims::min_exponent10), but MSVC 2017 apparently has
// problems with std::abs. So we use -lims::min_exponent10 instead.
auto const max_neg_exp{
digits10(lims::max_digits10 - lims::min_exponent10)};
return 1 + // Sign.
1 + // Decimal point.
std::numeric_limits<T>::max_digits10 + // Mantissa digits.
1 + // Exponent "e".
1 + // Exponent sign.
// Spell this weirdly to stop Windows compilers from reading this as
// a call to their "max" macro when NOMINMAX is not defined.
(std::max)(max_pos_exp, max_neg_exp) + // Exponent digits.
1; // Terminating zero.
}
};
} // namespace pqxx::internal
namespace pqxx
{
template<typename T>
struct nullness<T, std::enable_if_t<std::is_arithmetic_v<T>>> : no_null<T>
{};
template<> struct string_traits<short> : internal::integral_traits<short>
{};
template<> inline constexpr bool is_unquoted_safe<short>{true};
template<>
struct string_traits<unsigned short>
: internal::integral_traits<unsigned short>
{};
template<> inline constexpr bool is_unquoted_safe<unsigned short>{true};
template<> struct string_traits<int> : internal::integral_traits<int>
{};
template<> inline constexpr bool is_unquoted_safe<int>{true};
template<> struct string_traits<unsigned> : internal::integral_traits<unsigned>
{};
template<> inline constexpr bool is_unquoted_safe<unsigned>{true};
template<> struct string_traits<long> : internal::integral_traits<long>
{};
template<> inline constexpr bool is_unquoted_safe<long>{true};
template<>
struct string_traits<unsigned long> : internal::integral_traits<unsigned long>
{};
template<> inline constexpr bool is_unquoted_safe<unsigned long>{true};
template<>
struct string_traits<long long> : internal::integral_traits<long long>
{};
template<> inline constexpr bool is_unquoted_safe<long long>{true};
template<>
struct string_traits<unsigned long long>
: internal::integral_traits<unsigned long long>
{};
template<> inline constexpr bool is_unquoted_safe<unsigned long long>{true};
template<> struct string_traits<float> : internal::float_traits<float>
{};
template<> inline constexpr bool is_unquoted_safe<float>{true};
template<> struct string_traits<double> : internal::float_traits<double>
{};
template<> inline constexpr bool is_unquoted_safe<double>{true};
template<>
struct string_traits<long double> : internal::float_traits<long double>
{};
template<> inline constexpr bool is_unquoted_safe<long double>{true};
template<> struct string_traits<bool>
{
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{true};
static PQXX_LIBEXPORT bool from_string(std::string_view text);
static constexpr zview to_buf(char *, char *, bool const &value) noexcept
{
return value ? "true"_zv : "false"_zv;
}
static char *into_buf(char *begin, char *end, bool const &value)
{
return pqxx::internal::generic_into_buf(begin, end, value);
}
static constexpr std::size_t size_buffer(bool const &) noexcept { return 6; }
};
template<> inline constexpr bool is_unquoted_safe<bool>{true};
template<typename T> struct nullness<std::optional<T>>
{
static constexpr bool has_null = true;
static constexpr bool always_null = nullness<T>::always_null;
static constexpr bool is_null(std::optional<T> const &v) noexcept
{
return ((not v.has_value()) or pqxx::is_null(*v));
}
static constexpr std::optional<T> null() { return {}; }
};
template<typename T>
inline constexpr format param_format(std::optional<T> const &value)
{
return param_format(*value);
}
template<typename T> struct string_traits<std::optional<T>>
{
static constexpr bool converts_to_string{
string_traits<T>::converts_to_string};
static constexpr bool converts_from_string{
string_traits<T>::converts_from_string};
static char *into_buf(char *begin, char *end, std::optional<T> const &value)
{
return string_traits<T>::into_buf(begin, end, *value);
}
static zview to_buf(char *begin, char *end, std::optional<T> const &value)
{
if (pqxx::is_null(value))
return {};
else
return string_traits<T>::to_buf(begin, end, *value);
}
static std::optional<T> from_string(std::string_view text)
{
return std::optional<T>{
std::in_place, string_traits<T>::from_string(text)};
}
static std::size_t size_buffer(std::optional<T> const &value) noexcept
{
if (pqxx::is_null(value))
return 0;
else
return pqxx::size_buffer(value.value());
}
};
template<typename T>
inline constexpr bool is_unquoted_safe<std::optional<T>>{is_unquoted_safe<T>};
template<typename... T> struct nullness<std::variant<T...>>
{
static constexpr bool has_null = (nullness<T>::has_null or ...);
static constexpr bool always_null = (nullness<T>::always_null and ...);
static constexpr bool is_null(std::variant<T...> const &value) noexcept
{
return value.valueless_by_exception() or
std::visit(
[](auto const &i) noexcept {
return nullness<strip_t<decltype(i)>>::is_null(i);
},
value);
}
// We don't support `null()` for `std::variant`.
static constexpr std::variant<T...> null() = delete;
};
template<typename... T> struct string_traits<std::variant<T...>>
{
static constexpr bool converts_to_string{
(string_traits<T>::converts_to_string and ...)};
static char *
into_buf(char *begin, char *end, std::variant<T...> const &value)
{
return std::visit(
[begin, end](auto const &i) {
return string_traits<strip_t<decltype(i)>>::into_buf(begin, end, i);
},
value);
}
static zview to_buf(char *begin, char *end, std::variant<T...> const &value)
{
return std::visit(
[begin, end](auto const &i) {
return string_traits<strip_t<decltype(i)>>::to_buf(begin, end, i);
},
value);
}
static std::size_t size_buffer(std::variant<T...> const &value) noexcept
{
if (pqxx::is_null(value))
return 0;
else
return std::visit(
[](auto const &i) noexcept { return pqxx::size_buffer(i); }, value);
}
static std::variant<T...> from_string(std::string_view) = delete;
};
template<typename... Args>
inline constexpr format param_format(std::variant<Args...> const &value)
{
return std::visit([](auto &v) { return param_format(v); }, value);
}
template<typename... T>
inline constexpr bool is_unquoted_safe<std::variant<T...>>{
(is_unquoted_safe<T> and ...)};
template<typename T> inline T from_string(std::stringstream const &text)
{
return from_string<T>(text.str());
}
template<> struct string_traits<std::nullptr_t>
{
static constexpr bool converts_to_string{false};
static constexpr bool converts_from_string{false};
static char *into_buf(char *, char *, std::nullptr_t) = delete;
[[deprecated("Do not convert nulls.")]] static constexpr zview
to_buf(char *, char *, std::nullptr_t const &) noexcept
{
return {};
}
[[deprecated("Do not convert nulls.")]] static constexpr std::size_t
size_buffer(std::nullptr_t = nullptr) noexcept
{
return 0;
}
static std::nullptr_t from_string(std::string_view) = delete;
};
template<> struct string_traits<std::nullopt_t>
{
static constexpr bool converts_to_string{false};
static constexpr bool converts_from_string{false};
static char *into_buf(char *, char *, std::nullopt_t) = delete;
[[deprecated("Do not convert nulls.")]] static constexpr zview
to_buf(char *, char *, std::nullopt_t const &) noexcept
{
return {};
}
[[deprecated("Do not convert nulls.")]] static constexpr std::size_t
size_buffer(std::nullopt_t) noexcept
{
return 0;
}
static std::nullopt_t from_string(std::string_view) = delete;
};
template<> struct string_traits<std::monostate>
{
static constexpr bool converts_to_string{false};
static constexpr bool converts_from_string{false};
static char *into_buf(char *, char *, std::monostate) = delete;
[[deprecated("Do not convert nulls.")]] static constexpr zview
to_buf(char *, char *, std::monostate const &) noexcept
{
return {};
}
[[deprecated("Do not convert nulls.")]] static constexpr std::size_t
size_buffer(std::monostate) noexcept
{
return 0;
}
[[deprecated("Do not convert nulls.")]] static std::monostate
from_string(std::string_view) = delete;
};
template<> inline constexpr bool is_unquoted_safe<std::nullptr_t>{true};
template<> struct nullness<char const *>
{
static constexpr bool has_null = true;
static constexpr bool always_null = false;
static constexpr bool is_null(char const *t) noexcept
{
return t == nullptr;
}
static constexpr char const *null() noexcept { return nullptr; }
};
template<> struct string_traits<char const *>
{
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{true};
static char const *from_string(std::string_view text) { return text.data(); }
static zview to_buf(char *begin, char *end, char const *const &value)
{
return generic_to_buf(begin, end, value);
}
static char *into_buf(char *begin, char *end, char const *const &value)
{
auto const space{end - begin};
// Count the trailing zero, even though std::strlen() and friends don't.
auto const len{std::strlen(value) + 1};
if (space < ptrdiff_t(len))
throw conversion_overrun{
"Could not copy string: buffer too small. " +
pqxx::internal::state_buffer_overrun(space, len)};
std::memmove(begin, value, len);
return begin + len;
}
static std::size_t size_buffer(char const *const &value) noexcept
{
if (pqxx::is_null(value))
return 0;
else
return std::strlen(value) + 1;
}
};
template<> struct nullness<char *>
{
static constexpr bool has_null = true;
static constexpr bool always_null = false;
static constexpr bool is_null(char const *t) noexcept
{
return t == nullptr;
}
static constexpr char const *null() { return nullptr; }
};
template<> struct string_traits<char *>
{
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{false};
static char *into_buf(char *begin, char *end, char *const &value)
{
return string_traits<char const *>::into_buf(begin, end, value);
}
static zview to_buf(char *begin, char *end, char *const &value)
{
return string_traits<char const *>::to_buf(begin, end, value);
}
static std::size_t size_buffer(char *const &value) noexcept
{
if (pqxx::is_null(value))
return 0;
else
return string_traits<char const *>::size_buffer(value);
}
static char *from_string(std::string_view) = delete;
};
template<std::size_t N> struct nullness<char[N]> : no_null<char[N]>
{};
template<std::size_t N> struct string_traits<char[N]>
{
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{false};
static constexpr zview
to_buf(char *, char *, char const (&value)[N]) noexcept
{
return zview{value, N - 1};
}
static char *into_buf(char *begin, char *end, char const (&value)[N])
{
if (internal::cmp_less(end - begin, size_buffer(value)))
throw conversion_overrun{
"Could not convert char[] to string: too long for buffer."};
std::memcpy(begin, value, N);
return begin + N;
}
static constexpr std::size_t size_buffer(char const (&)[N]) noexcept
{
return N;
}
static void from_string(std::string_view) = delete;
};
template<> struct nullness<std::string> : no_null<std::string>
{};
template<> struct string_traits<std::string>
{
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{true};
static std::string from_string(std::string_view text)
{
return std::string{text};
}
static char *into_buf(char *begin, char *end, std::string const &value)
{
if (internal::cmp_greater_equal(std::size(value), end - begin))
throw conversion_overrun{
"Could not convert string to string: too long for buffer."};
// Include the trailing zero.
value.copy(begin, std::size(value));
begin[std::size(value)] = '\0';
return begin + std::size(value) + 1;
}
static zview to_buf(char *begin, char *end, std::string const &value)
{
return generic_to_buf(begin, end, value);
}
static std::size_t size_buffer(std::string const &value) noexcept
{
return std::size(value) + 1;
}
};
template<> struct nullness<std::string_view> : no_null<std::string_view>
{};
template<> struct string_traits<std::string_view>
{
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{false};
static constexpr std::size_t
size_buffer(std::string_view const &value) noexcept
{
return std::size(value) + 1;
}
static char *into_buf(char *begin, char *end, std::string_view const &value)
{
if (internal::cmp_greater_equal(std::size(value), end - begin))
throw conversion_overrun{
"Could not store string_view: too long for buffer."};
value.copy(begin, std::size(value));
begin[std::size(value)] = '\0';
return begin + std::size(value) + 1;
}
static zview to_buf(char *begin, char *end, std::string_view const &value)
{
// You'd think we could just return the same view but alas, there's no
// zero-termination on a string_view.
return generic_to_buf(begin, end, value);
}
static std::string_view from_string(std::string_view) = delete;
};
template<> struct nullness<zview> : no_null<zview>
{};
template<> struct string_traits<zview>
{
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{false};
static constexpr std::size_t
size_buffer(std::string_view const &value) noexcept
{
return std::size(value) + 1;
}
static char *into_buf(char *begin, char *end, zview const &value)
{
auto const size{std::size(value)};
if (internal::cmp_less_equal(end - begin, std::size(value)))
throw conversion_overrun{"Not enough buffer space to store this zview."};
value.copy(begin, size);
begin[size] = '\0';
return begin + size + 1;
}
static std::string_view to_buf(char *begin, char *end, zview const &value)
{
char *const stop{into_buf(begin, end, value)};
return {begin, static_cast<std::size_t>(stop - begin - 1)};
}
static zview from_string(std::string_view) = delete;
};
template<> struct nullness<std::stringstream> : no_null<std::stringstream>
{};
template<> struct string_traits<std::stringstream>
{
static constexpr bool converts_to_string{false};
static constexpr bool converts_from_string{true};
static std::size_t size_buffer(std::stringstream const &) = delete;
static std::stringstream from_string(std::string_view text)
{
std::stringstream stream;
stream.write(text.data(), std::streamsize(std::size(text)));
return stream;
}
static char *into_buf(char *, char *, std::stringstream const &) = delete;
static std::string_view
to_buf(char *, char *, std::stringstream const &) = delete;
};
template<> struct nullness<std::nullptr_t>
{
static constexpr bool has_null = true;
static constexpr bool always_null = true;
static constexpr bool is_null(std::nullptr_t const &) noexcept
{
return true;
}
static constexpr std::nullptr_t null() noexcept { return nullptr; }
};
template<> struct nullness<std::nullopt_t>
{
static constexpr bool has_null = true;
static constexpr bool always_null = true;
static constexpr bool is_null(std::nullopt_t const &) noexcept
{
return true;
}
static constexpr std::nullopt_t null() noexcept { return std::nullopt; }
};
template<> struct nullness<std::monostate>
{
static constexpr bool has_null = true;
static constexpr bool always_null = true;
static constexpr bool is_null(std::monostate const &) noexcept
{
return true;
}
static constexpr std::monostate null() noexcept { return {}; }
};
template<typename T> struct nullness<std::unique_ptr<T>>
{
static constexpr bool has_null = true;
static constexpr bool always_null = false;
static constexpr bool is_null(std::unique_ptr<T> const &t) noexcept
{
return not t or pqxx::is_null(*t);
}
static constexpr std::unique_ptr<T> null() { return {}; }
};
template<typename T, typename... Args>
struct string_traits<std::unique_ptr<T, Args...>>
{
static constexpr bool converts_to_string{
string_traits<T>::converts_to_string};
static constexpr bool converts_from_string{
string_traits<T>::converts_from_string};
static std::unique_ptr<T> from_string(std::string_view text)
{
return std::make_unique<T>(string_traits<T>::from_string(text));
}
static char *
into_buf(char *begin, char *end, std::unique_ptr<T, Args...> const &value)
{
return string_traits<T>::into_buf(begin, end, *value);
}
static zview
to_buf(char *begin, char *end, std::unique_ptr<T, Args...> const &value)
{
if (value)
return string_traits<T>::to_buf(begin, end, *value);
else
return {};
}
static std::size_t
size_buffer(std::unique_ptr<T, Args...> const &value) noexcept
{
if (pqxx::is_null(value))
return 0;
else
return pqxx::size_buffer(*value.get());
}
};
template<typename T, typename... Args>
inline format param_format(std::unique_ptr<T, Args...> const &value)
{
return param_format(*value);
}
template<typename T, typename... Args>
inline constexpr bool is_unquoted_safe<std::unique_ptr<T, Args...>>{
is_unquoted_safe<T>};
template<typename T> struct nullness<std::shared_ptr<T>>
{
static constexpr bool has_null = true;
static constexpr bool always_null = false;
static constexpr bool is_null(std::shared_ptr<T> const &t) noexcept
{
return not t or pqxx::is_null(*t);
}
static constexpr std::shared_ptr<T> null() { return {}; }
};
template<typename T> struct string_traits<std::shared_ptr<T>>
{
static constexpr bool converts_to_string{
string_traits<T>::converts_to_string};
static constexpr bool converts_from_string{
string_traits<T>::converts_from_string};
static std::shared_ptr<T> from_string(std::string_view text)
{
return std::make_shared<T>(string_traits<T>::from_string(text));
}
static zview to_buf(char *begin, char *end, std::shared_ptr<T> const &value)
{
return string_traits<T>::to_buf(begin, end, *value);
}
static char *
into_buf(char *begin, char *end, std::shared_ptr<T> const &value)
{
return string_traits<T>::into_buf(begin, end, *value);
}
static std::size_t size_buffer(std::shared_ptr<T> const &value) noexcept
{
if (pqxx::is_null(value))
return 0;
else
return pqxx::size_buffer(*value);
}
};
template<typename T> format param_format(std::shared_ptr<T> const &value)
{
return param_format(*value);
}
template<typename T>
inline constexpr bool is_unquoted_safe<std::shared_ptr<T>>{
is_unquoted_safe<T>};
template<> struct nullness<bytes> : no_null<bytes>
{};
#if defined(PQXX_HAVE_CONCEPTS)
template<binary DATA> struct nullness<DATA> : no_null<DATA>
{};
template<binary DATA> inline constexpr format param_format(DATA const &)
{
return format::binary;
}
template<binary DATA> struct string_traits<DATA>
{
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{true};
static std::size_t size_buffer(DATA const &value) noexcept
{
return internal::size_esc_bin(std::size(value));
}
static zview to_buf(char *begin, char *end, DATA const &value)
{
return generic_to_buf(begin, end, value);
}
static char *into_buf(char *begin, char *end, DATA const &value)
{
auto const budget{size_buffer(value)};
if (internal::cmp_less(end - begin, budget))
throw conversion_overrun{
"Not enough buffer space to escape binary data."};
internal::esc_bin(value, begin);
return begin + budget;
}
static DATA from_string(std::string_view text)
{
auto const size{pqxx::internal::size_unesc_bin(std::size(text))};
bytes buf;
buf.resize(size);
pqxx::internal::unesc_bin(text, reinterpret_cast<std::byte *>(buf.data()));
return buf;
}
};
#endif // PQXX_HAVE_CONCEPTS
template<> struct string_traits<bytes>
{
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{true};
static std::size_t size_buffer(bytes const &value) noexcept
{
return internal::size_esc_bin(std::size(value));
}
static zview to_buf(char *begin, char *end, bytes const &value)
{
return generic_to_buf(begin, end, value);
}
static char *into_buf(char *begin, char *end, bytes const &value)
{
auto const budget{size_buffer(value)};
if (internal::cmp_less(end - begin, budget))
throw conversion_overrun{
"Not enough buffer space to escape binary data."};
internal::esc_bin(value, begin);
return begin + budget;
}
static bytes from_string(std::string_view text)
{
auto const size{pqxx::internal::size_unesc_bin(std::size(text))};
bytes buf;
buf.resize(size);
pqxx::internal::unesc_bin(text, reinterpret_cast<std::byte *>(buf.data()));
return buf;
}
};
template<> inline constexpr format param_format(bytes const &)
{
return format::binary;
}
template<> struct nullness<bytes_view> : no_null<bytes_view>
{};
template<> struct string_traits<bytes_view>
{
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{false};
static std::size_t size_buffer(bytes_view const &value) noexcept
{
return internal::size_esc_bin(std::size(value));
}
static zview to_buf(char *begin, char *end, bytes_view const &value)
{
return generic_to_buf(begin, end, value);
}
static char *into_buf(char *begin, char *end, bytes_view const &value)
{
auto const budget{size_buffer(value)};
if (internal::cmp_less(end - begin, budget))
throw conversion_overrun{
"Not enough buffer space to escape binary data."};
internal::esc_bin(value, begin);
return begin + budget;
}
// There's no from_string, because there's nobody to hold the data.
};
template<> inline constexpr format param_format(bytes_view const &)
{
return format::binary;
}
} // namespace pqxx
namespace pqxx::internal
{
// C++20: Use concepts to identify arrays.
template<typename Container> struct array_string_traits
{
private:
using elt_type = strip_t<value_type<Container>>;
using elt_traits = string_traits<elt_type>;
static constexpr zview s_null{"NULL"};
public:
static constexpr bool converts_to_string{true};
static constexpr bool converts_from_string{false};
static zview to_buf(char *begin, char *end, Container const &value)
{
return generic_to_buf(begin, end, value);
}
static char *into_buf(char *begin, char *end, Container const &value)
{
assert(begin <= end);
std::size_t const budget{size_buffer(value)};
if (internal::cmp_less(end - begin, budget))
throw conversion_overrun{
"Not enough buffer space to convert array to string."};
char *here = begin;
*here++ = '{';
bool nonempty{false};
for (auto const &elt : value)
{
if (is_null(elt))
{
s_null.copy(here, std::size(s_null));
here += std::size(s_null);
}
else if constexpr (is_sql_array<elt_type>)
{
// Render nested array in-place. Then erase the trailing zero.
here = elt_traits::into_buf(here, end, elt) - 1;
}
else if constexpr (is_unquoted_safe<elt_type>)
{
// No need to quote or escape. Just convert the value straight into
// its place in the array, and "backspace" the trailing zero.
here = elt_traits::into_buf(here, end, elt) - 1;
}
else
{
*here++ = '"';
// Use the tail end of the destination buffer as an intermediate
// buffer.
auto const elt_budget{pqxx::size_buffer(elt)};
assert(elt_budget < static_cast<std::size_t>(end - here));
for (char const c : elt_traits::to_buf(end - elt_budget, end, elt))
{
// We copy the intermediate buffer into the final buffer, char by
// char, with escaping where necessary.
// TODO: This will not work for all encodings. UTF8 & ASCII are OK.
if (c == '\\' or c == '"')
*here++ = '\\';
*here++ = c;
}
*here++ = '"';
}
*here++ = array_separator<elt_type>;
nonempty = true;
}
// Erase that last comma, if present.
if (nonempty)
here--;
*here++ = '}';
*here++ = '\0';
return here;
}
static std::size_t size_buffer(Container const &value) noexcept
{
if constexpr (is_unquoted_safe<elt_type>)
return 3 + std::accumulate(
std::begin(value), std::end(value), std::size_t{},
[](std::size_t acc, elt_type const &elt) {
// Budget for each element includes a terminating zero.
// We won't actually be wanting those, but don't subtract
// that one byte: we want room for a separator instead.
return acc + (pqxx::is_null(elt) ?
std::size(s_null) :
elt_traits::size_buffer(elt));
});
else
return 3 + std::accumulate(
std::begin(value), std::end(value), std::size_t{},
[](std::size_t acc, elt_type const &elt) {
// Opening and closing quotes, plus worst-case escaping,
// and the one byte for the trailing zero becomes room
// for a separator.
std::size_t const elt_size{
pqxx::is_null(elt) ? std::size(s_null) :
elt_traits::size_buffer(elt)};
return acc + 2 * elt_size + 2;
});
}
// We don't yet support parsing of array types using from_string. Doing so
// would require a reference to the connection.
};
} // namespace pqxx::internal
namespace pqxx
{
template<typename T, typename... Args>
struct nullness<std::vector<T, Args...>> : no_null<std::vector<T>>
{};
template<typename T, typename... Args>
struct string_traits<std::vector<T, Args...>>
: internal::array_string_traits<std::vector<T, Args...>>
{};
template<typename T, typename... Args>
inline constexpr format param_format(std::vector<T, Args...> const &)
{
return format::text;
}
template<typename... Args>
inline constexpr format param_format(std::vector<std::byte, Args...> const &)
{
return format::binary;
}
template<typename T> inline constexpr bool is_sql_array<std::vector<T>>{true};
#if defined(PQXX_HAVE_SPAN) && __has_include(<span>)
template<typename T, size_t Extent>
struct nullness<std::span<T, Extent>> : no_null<std::span<T, Extent>>
{};
template<typename T, size_t Extent>
struct string_traits<std::span<T, Extent>>
: internal::array_string_traits<std::span<T, Extent>>
{};
template<typename T, size_t Extent>
inline constexpr format param_format(std::span<T, Extent> const &)
{
return format::text;
}
template<size_t Extent>
inline constexpr format param_format(std::span<std::byte, Extent> const &)
{
return format::binary;
}
template<typename T, size_t Extent>
inline constexpr bool is_sql_array<std::span<T, Extent>>{true};
#endif
template<typename T, std::size_t N>
struct nullness<std::array<T, N>> : no_null<std::array<T, N>>
{};
template<typename T, std::size_t N>
struct string_traits<std::array<T, N>>
: internal::array_string_traits<std::array<T, N>>
{};
template<typename T, typename... Args, Args... args>
inline constexpr format param_format(std::array<T, args...> const &)
{
return format::text;
}
template<typename... Args, Args... args>
inline constexpr format param_format(std::array<std::byte, args...> const &)
{
return format::binary;
}
template<typename T, std::size_t N>
inline constexpr bool is_sql_array<std::array<T, N>>{true};
} // namespace pqxx
namespace pqxx
{
template<typename T> inline std::string to_string(T const &value)
{
if (is_null(value))
throw conversion_error{
"Attempt to convert null " + std::string{type_name<T>} +
" to a string."};
std::string buf;
// We can't just reserve() space; modifying the terminating zero leads to
// undefined behaviour.
buf.resize(size_buffer(value));
auto const data{buf.data()};
auto const end{
string_traits<T>::into_buf(data, data + std::size(buf), value)};
buf.resize(static_cast<std::size_t>(end - data - 1));
return buf;
}
template<> inline std::string to_string(float const &value)
{
return internal::to_string_float(value);
}
template<> inline std::string to_string(double const &value)
{
return internal::to_string_float(value);
}
template<> inline std::string to_string(long double const &value)
{
return internal::to_string_float(value);
}
template<> inline std::string to_string(std::stringstream const &value)
{
return value.str();
}
template<typename T> inline void into_string(T const &value, std::string &out)
{
if (is_null(value))
throw conversion_error{
"Attempt to convert null " + type_name<T> + " to a string."};
// We can't just reserve() data; modifying the terminating zero leads to
// undefined behaviour.
out.resize(size_buffer(value) + 1);
auto const data{out.data()};
auto const end{
string_traits<T>::into_buf(data, data + std::size(out), value)};
out.resize(static_cast<std::size_t>(end - data - 1));
}
} // namespace pqxx
pqxx::array
An SQL array received from the database.
Definition array.hxx:56
pqxx::zview
Marker-type wrapper: zero-terminated std::string_view.
Definition zview.hxx:38
pqxx::conversion_error
Value conversion failed, e.g. when converting "Hello" to int.
Definition except.hxx:283
pqxx::conversion_overrun
Could not convert value to string: not enough buffer space.
Definition except.hxx:313
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::throw_null_conversion
void throw_null_conversion(std::string const &type)
Throw exception for attempt to convert SQL NULL to given type.
Definition strconv.cxx:248
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::number_to_digit
constexpr char number_to_digit(int i) noexcept
Convert a number in [0, 9] to its ASCII digit.
Definition conversions.hxx:27
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::digit_to_number
constexpr int digit_to_number(char c) noexcept
Compute numeric value of given textual digit (assuming that it is a digit).
Definition conversions.hxx:34
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::to_string_float
std::string to_string_float(T value)
Floating-point implementations for pqxx::to_string().
Definition strconv.cxx:661
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::state_buffer_overrun
std::string state_buffer_overrun(int have_bytes, int need_bytes)
Summarize buffer overrun.
Definition strconv.cxx:260
pqxx::internal::generic_into_buf
char * generic_into_buf(char *begin, char *end, T const &value)
Generic implementation for into_buf, on top of to_buf.
Definition conversions.hxx:95
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::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::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::type_name
std::string const type_name
A human-readable name for a type, used in error messages and such.
Definition strconv.hxx:80
pqxx::to_buf
std::vector< std::string_view > to_buf(char *here, char const *end, TYPE... value)
Convert multiple values to strings inside a single buffer.
Definition strconv.hxx:493
pqxx::array_separator
constexpr char array_separator
Element separator between SQL array elements of this type.
Definition strconv.hxx:559
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:383
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:373
pqxx::is_unquoted_safe
constexpr bool is_unquoted_safe
Can we use this type in arrays and composite types without quoting them?
Definition strconv.hxx:555
pqxx::size_buffer
std::size_t size_buffer(TYPE const &...value) noexcept
Estimate how much buffer space is needed to represent values as a string.
Definition strconv.hxx:526
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::is_null
constexpr bool is_null(TYPE const &value) noexcept
Is value null?
Definition strconv.hxx:515
pqxx::generic_to_buf
zview generic_to_buf(char *begin, char *end, TYPE const &value)
Implement string_traits<TYPE>::to_buf by calling into_buf.
Definition strconv.hxx:587
pqxx::is_sql_array
constexpr bool is_sql_array
Does this type translate to an SQL array?
Definition strconv.hxx:539
pqxx::from_string
T from_string(field const &value)
Convert a field's value to type T.
Definition field.hxx:548
pqxx::format
format
Format code: is data text or binary?
Definition types.hxx:70
pqxx::internal::array_string_traits
String traits for SQL arrays.
Definition conversions.hxx:1036
pqxx::internal::disallowed_ambiguous_char_conversion
Deliberately nonfunctional conversion traits for char types.
Definition conversions.hxx:77
pqxx::internal::float_traits
String traits for builtin floating-point types.
Definition conversions.hxx:134
pqxx::internal::float_traits::to_buf
static PQXX_LIBEXPORT zview to_buf(char *begin, char *end, T const &value)
Floating-point to_buf implemented in terms of to_string.
Definition strconv.cxx:593
pqxx::internal::integral_traits
String traits for builtin integral types (though not bool).
Definition conversions.hxx:113
pqxx::no_null
Nullness traits describing a type which does not have a null value.
Definition strconv.hxx:113
pqxx::nullness< std::optional< T > >::always_null
static constexpr bool always_null
Technically, you could have an optional of an always-null type.
Definition conversions.hxx:276
pqxx::nullness< std::variant< T... > >::null
static constexpr std::variant< T... > null()=delete
pqxx::nullness
Traits describing a type's "null value," if any.
Definition strconv.hxx:91
pqxx::nullness::is_null
static bool is_null(TYPE const &value)
Is value a null?
pqxx::nullness::null
static TYPE null()
Return a null value.
pqxx::nullness::has_null
static bool has_null
Does this type have a null value?
Definition strconv.hxx:93
pqxx::nullness::always_null
static bool always_null
Is this type always null?
Definition strconv.hxx:96
pqxx::string_traits< char * >::from_string
static char * from_string(std::string_view)=delete
Don't allow conversion to this type since it breaks const-safety.
pqxx::string_traits< char[N]>::from_string
static void from_string(std::string_view)=delete
Don't allow conversion to this type.
pqxx::string_traits< std::string_view >::from_string
static std::string_view from_string(std::string_view)=delete
Don't convert to this type; it has nowhere to store its contents.
pqxx::string_traits< std::variant< T... > >::from_string
static std::variant< T... > from_string(std::string_view)=delete
pqxx::string_traits< zview >::from_string
static zview from_string(std::string_view)=delete
Don't convert to this type; it has nowhere to store its contents.
pqxx::string_traits
Traits class for use in string conversions.
Definition strconv.hxx:154
pqxx::string_traits::from_string
static TYPE from_string(std::string_view text)
Parse a string representation of a TYPE value.
pqxx::string_traits::size_buffer
static std::size_t size_buffer(TYPE const &value) noexcept
Estimate how much buffer space is needed to represent value.
pqxx::string_traits::to_buf
static zview to_buf(char *begin, char *end, TYPE const &value)
Return a string_view representing value, plus terminating zero.
pqxx::string_traits::converts_to_string
static constexpr bool converts_to_string
Is conversion from TYPE to strings supported?
Definition strconv.hxx:159
pqxx::string_traits::into_buf
static char * into_buf(char *begin, char *end, TYPE const &value)
Write value's string representation into buffer at begin.
pqxx::string_traits::converts_from_string
static constexpr bool converts_from_string
Is conversion from string_view to TYPE supported?
Definition strconv.hxx:165