From 90f9c7cd59030e6f240514912cab751dadaf2411 Mon Sep 17 00:00:00 2001 From: Raul Tambre Date: Fri, 9 Feb 2024 23:05:45 +0100 Subject: [PATCH] pqxx::byte and pqxx::bytes_view to support lack of std::char_traits (#751) The standard doesn't specify a generic implementation of std::char_traits, only specializations for certain types. For a good reason: it's unlikely to be correct for all types that it'll compile with it. All standard libraries today however do provide a generic implementation as an extension, though it's bound to be incorrect for some types. libc++ has deprecated its in version 18 and will remove it in version 19 to eliminate hard to find correctness issues stemming from this. Replace with type aliases that will use a custom char_traits when the standard library lacks such a generic implementation. Note that we don't unconditionally use the custom char_traits variant as it's a source-breaking change requiring the users to update all usages of `std::string` to `pqxx::bytes` (i.e. `std::string`). Ditto `std::string_view` and `pqxx::bytes_view`. But for implementations lacking a generic implementation `std::string` and `std::string_view` wouldn't compile anyway so it's fine. The aliases are named as `bytes` and `bytes_view` with the intetion of switching them to `std::vector` and `std::span` in a future major release that requires C++20. Fixes: #726 By Raul Tambre --- NEWS | 7 ++ include/pqxx/binarystring.hxx | 13 ++- include/pqxx/blob.hxx | 19 ++-- include/pqxx/connection.hxx | 25 +++--- include/pqxx/doc/accessing-results.md | 8 +- include/pqxx/doc/binary-data.md | 9 +- include/pqxx/doc/prepared-statement.md | 4 +- include/pqxx/field.hxx | 2 +- include/pqxx/internal/conversions.hxx | 34 +++---- include/pqxx/params.hxx | 17 ++-- include/pqxx/strconv.hxx | 13 ++- include/pqxx/transaction_base.hxx | 21 ++--- include/pqxx/util.hxx | 119 ++++++++++++++++++++++--- src/blob.cxx | 16 ++-- src/connection.cxx | 8 +- src/params.cxx | 6 +- src/util.cxx | 11 +-- test/test62.cxx | 8 +- test/unit/test_binarystring.cxx | 16 ++-- test/unit/test_blob.cxx | 115 +++++++++++------------- test/unit/test_escape.cxx | 11 ++- test/unit/test_prepared_statement.cxx | 24 ++--- 22 files changed, 278 insertions(+), 228 deletions(-) diff --git a/NEWS b/NEWS index cf14c193c..723e298d6 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,13 @@ - Implement conversion from `string_view` to string. (#728) - Rename `splitconfig` to `splitconfig.py`. (#763) - Document need for conversions more clearly, link to docs. (#757) + - `std::basic_string` and `std::basic_string_view` have + been replaced with `pqxx::bytes` and `pqxx::bytes_view` respectively to + support the removal of generic `std::char_traits` in libc++ 19. Users must + switch to these aliases to support versions from libc++ 18 onward. + In a future major release that start requiring C++20 these are likely to + become aliases for `std::vector` and `std::span` to + better match the intent of the interfaces. (#726) 7.8.1 - Regenerate build files. Should fix ARM Mac build. (#715) - Reinstate `` that MSVC can't live with or without. (#713) diff --git a/include/pqxx/binarystring.hxx b/include/pqxx/binarystring.hxx index 0f71d46dd..48948a297 100644 --- a/include/pqxx/binarystring.hxx +++ b/include/pqxx/binarystring.hxx @@ -30,9 +30,8 @@ template<> struct string_traits; /// Binary data corresponding to PostgreSQL's "BYTEA" binary-string type. /** @ingroup escaping-functions - * @deprecated Use @c std::basic_string and - * @c std::basic_string_view for binary data. In C++20 or better, - * any @c contiguous_range of @c std::byte will do. + * @deprecated Use @c bytes and @c bytes_view for binary data. In C++20 or + * better, any @c contiguous_range of @c std::byte will do. * * This class represents a binary string as stored in a field of type @c bytea. * @@ -59,7 +58,7 @@ class PQXX_LIBEXPORT binarystring { public: using char_type = unsigned char; - using value_type = std::char_traits::char_type; + using value_type = char_type; using size_type = std::size_t; using difference_type = long; using const_reference = value_type const &; @@ -174,10 +173,10 @@ public: return reinterpret_cast(get()); } - /// Read data as a @c std::basic_string_view. - [[nodiscard]] std::basic_string_view bytes_view() const + /// Read data as a @c bytes_view. + [[nodiscard]] pqxx::bytes_view bytes_view() const { - return std::basic_string_view{bytes(), size()}; + return pqxx::bytes_view{bytes(), size()}; } private: diff --git a/include/pqxx/blob.hxx b/include/pqxx/blob.hxx index f18d710a0..6d9a569b1 100644 --- a/include/pqxx/blob.hxx +++ b/include/pqxx/blob.hxx @@ -104,7 +104,7 @@ public: * @warning The underlying protocol only supports reads up to 2GB at a time. * If you need to read more, try making repeated calls to @ref append_to_buf. */ - std::size_t read(std::basic_string &buf, std::size_t size); + std::size_t read(bytes &buf, std::size_t size); #if defined(PQXX_HAVE_SPAN) /// Read up to `std::size(buf)` bytes from the object. @@ -145,8 +145,7 @@ public: * * Returns the filled portion of `buf`. This may be empty. */ - template - std::basic_string_view read(std::vector &buf) + template bytes_view read(std::vector &buf) { return {std::data(buf), raw_read(std::data(buf), std::size(buf))}; } @@ -234,14 +233,12 @@ public: /** You may optionally specify an oid for the new object. If you do, and an * object with that oid already exists, creation will fail. */ - static oid from_buf( - dbtransaction &tx, std::basic_string_view data, oid id = 0); + static oid from_buf(dbtransaction &tx, bytes_view data, oid id = 0); /// Append `data` to binary large object. /** The underlying protocol only supports appending blocks up to 2 GB. */ - static void append_from_buf( - dbtransaction &tx, std::basic_string_view data, oid id); + static void append_from_buf(dbtransaction &tx, bytes_view data, oid id); /// Read client-side file and store it server-side as a binary large object. [[nodiscard]] static oid from_file(dbtransaction &, char const path[]); @@ -283,9 +280,7 @@ public: /** You could easily do this yourself using the @ref open_r and @ref read * functions, but it can save you a bit of code to do it this way. */ - static void to_buf( - dbtransaction &, oid, std::basic_string &, - std::size_t max_size); + static void to_buf(dbtransaction &, oid, bytes &, std::size_t max_size); /// Read part of the binary large object with `id`, and append it to `buf`. /** Use this to break up a large read from one binary large object into one @@ -295,8 +290,8 @@ public: * `append_max` says how much to try and read in one go. */ static std::size_t append_to_buf( - dbtransaction &tx, oid id, std::int64_t offset, - std::basic_string &buf, std::size_t append_max); + dbtransaction &tx, oid id, std::int64_t offset, bytes &buf, + std::size_t append_max); /// Write a binary large object's contents to a client-side file. static void to_file(dbtransaction &, oid, char const path[]); diff --git a/include/pqxx/connection.hxx b/include/pqxx/connection.hxx index 2c84c85e3..18c331b74 100644 --- a/include/pqxx/connection.hxx +++ b/include/pqxx/connection.hxx @@ -732,7 +732,7 @@ public: "Not enough room to escape binary string of ", size, " byte(s): need ", needed, " bytes of buffer space, but buffer size is ", space, ".")}; - std::basic_string_view view{std::data(data), std::size(data)}; + bytes_view view{std::data(data), std::size(data)}; auto const out{std::data(buffer)}; // Actually, in the modern format, we know beforehand exactly how many // bytes we're going to fill. Just leave out the trailing zero. @@ -747,13 +747,12 @@ public: /// Escape binary string for use as SQL string literal on this connection. /** You can also just use @ref esc with a binary string. */ - [[nodiscard]] std::string esc_raw(std::basic_string_view) const; + [[nodiscard]] std::string esc_raw(bytes_view) const; #if defined(PQXX_HAVE_SPAN) /// Escape binary string for use as SQL string literal, into `buffer`. /** You can also just use @ref esc with a binary string. */ - [[nodiscard]] std::string - esc_raw(std::basic_string_view, std::span buffer) const; + [[nodiscard]] std::string esc_raw(bytes_view, std::span buffer) const; #endif #if defined(PQXX_HAVE_CONCEPTS) @@ -762,8 +761,7 @@ public: template [[nodiscard]] std::string esc_raw(DATA const &data) const { - return esc_raw( - std::basic_string_view{std::data(data), std::size(data)}); + return esc_raw(bytes_view{std::data(data), std::size(data)}); } #endif @@ -785,17 +783,16 @@ public: * "bytea" escape format, used prior to PostgreSQL 9.0, is no longer * supported.) */ - [[nodiscard]] std::basic_string - unesc_bin(std::string_view text) const + [[nodiscard]] bytes unesc_bin(std::string_view text) const { - std::basic_string buf; + bytes buf; buf.resize(pqxx::internal::size_unesc_bin(std::size(text))); pqxx::internal::unesc_bin(text, buf.data()); return buf; } /// Escape and quote a string of binary data. - std::string quote_raw(std::basic_string_view) const; + std::string quote_raw(bytes_view) const; #if defined(PQXX_HAVE_CONCEPTS) /// Escape and quote a string of binary data. @@ -803,8 +800,7 @@ public: template [[nodiscard]] std::string quote_raw(DATA const &data) const { - return quote_raw( - std::basic_string_view{std::data(data), std::size(data)}); + return quote_raw(bytes_view{std::data(data), std::size(data)}); } #endif @@ -856,8 +852,7 @@ public: // TODO: Make "into buffer" variant to eliminate a string allocation. /// Escape and quote binary data for use as a BYTEA value in SQL statement. - [[nodiscard]] std::string - quote(std::basic_string_view bytes) const; + [[nodiscard]] std::string quote(bytes_view bytes) const; // TODO: Make "into buffer" variant to eliminate a string allocation. /// Escape string for literal LIKE match. @@ -920,7 +915,7 @@ public: unesc_raw(char const text[]) const; /// Escape and quote a string of binary data. - [[deprecated("Use quote(std::basic_string_view).")]] std::string + [[deprecated("Use quote(bytes_view).")]] std::string quote_raw(unsigned char const bin[], std::size_t len) const; //@} diff --git a/include/pqxx/doc/accessing-results.md b/include/pqxx/doc/accessing-results.md index 5bcc2c5da..53f28ae3d 100644 --- a/include/pqxx/doc/accessing-results.md +++ b/include/pqxx/doc/accessing-results.md @@ -67,10 +67,10 @@ commands: `SELECT`, `VALUES`, or an `INSERT`, `UPDATE`, or `DELETE` with a ](https://www.postgresql.org/docs/current/sql-copy.html). **Three,** when you convert a field to a "view" type (such as -`std::string_view` or `std::basic_string_view`), the view points to -underlying data which only stays valid until you iterate to the next row or -exit the loop. So if you want to use that data for longer than a single -iteration of the streaming loop, you'll have to store it somewhere yourself. +`std::string_view` or `pqxx::bytes_view`), the view points to underlying data +which only stays valid until you iterate to the next row or exit the loop. So +if you want to use that data for longer than a single iteration of the +streaming loop, you'll have to store it somewhere yourself. Now for the good news. Streaming does make it very easy to query data and loop over it, and often faster than with the "query" or "exec" functions: diff --git a/include/pqxx/doc/binary-data.md b/include/pqxx/doc/binary-data.md index 99955ace6..7c85cf4b8 100644 --- a/include/pqxx/doc/binary-data.md +++ b/include/pqxx/doc/binary-data.md @@ -9,13 +9,12 @@ Generally you'll want to use `BYTEA` for reasonably-sized values, and large objects for very large values. That's the database side. On the C++ side, in libpqxx, all binary data must be -either `std::basic_string` or `std::basic_string_view`; -or if you're building in C++20 or better, anything that's a block of -contiguous `std::byte` in memory. +either `pqxx::bytes` or `pqxx::bytes_view`; or if you're building in C++20 or +better, anything that's a block of contiguous `std::byte` in memory. So for example, if you want to write a large object, you'd create a `pqxx::blob` object. And you might use that to write data in the form of -`std::basic_string_view`. +`pqxx::bytes_view`. Your particular binary data may look different though. You may have it in a `std::string`, or a `std::vector`, or a pointer to `char` @@ -24,7 +23,7 @@ different widths). Sometimes that's your choice, or sometimes some other library will dictate what form it takes. So long as it's _basically_ still a block of bytes though, you can use -`pqxx::binary_cast` to construct a `std::basic_string_view` from it. +`pqxx::binary_cast` to construct a `pqxx::bytes_view` from it. There are two forms of `binary_cast`. One takes a single argument that must support `std::data()` and `std::size()`: diff --git a/include/pqxx/doc/prepared-statement.md b/include/pqxx/doc/prepared-statement.md index 3d52c361b..f3c139b5d 100644 --- a/include/pqxx/doc/prepared-statement.md +++ b/include/pqxx/doc/prepared-statement.md @@ -121,5 +121,5 @@ data as the `BYTEA` type, or in binary large objects ("blobs"). In libpqxx, you represent binary data as a range of `std::byte`. They must be contiguous in memory, so that libpqxx can pass pointers to the underlying C -library. So you might use `std::basic_string`, or -`std::basic_string_view`, or `std::vector`. +library. So you might use `pqxx::bytes`, or `pqxx::bytes_view`, or +`std::vector`. diff --git a/include/pqxx/field.hxx b/include/pqxx/field.hxx index 5d20dce6f..00d8b25b3 100644 --- a/include/pqxx/field.hxx +++ b/include/pqxx/field.hxx @@ -121,7 +121,7 @@ public: * * Do not use this for BYTEA values, or other binary values. To read those, * convert the value to your desired type using `to()` or `as()`. For - * example: `f.as>()`. + * example: `f.as()`. */ [[nodiscard]] PQXX_PURE char const *c_str() const &; diff --git a/include/pqxx/internal/conversions.hxx b/include/pqxx/internal/conversions.hxx index 5a6fba3ad..dc14f70b2 100644 --- a/include/pqxx/internal/conversions.hxx +++ b/include/pqxx/internal/conversions.hxx @@ -872,8 +872,8 @@ inline constexpr bool is_unquoted_safe>{ template<> -struct nullness> - : no_null> +struct nullness + : no_null {}; @@ -916,7 +916,7 @@ template struct string_traits static DATA from_string(std::string_view text) { auto const size{pqxx::internal::size_unesc_bin(std::size(text))}; - std::basic_string buf; + bytes buf; buf.resize(size); pqxx::internal::unesc_bin(text, reinterpret_cast(buf.data())); return buf; @@ -925,25 +925,25 @@ template struct string_traits #endif // PQXX_HAVE_CONCEPTS -template<> struct string_traits> +template<> struct string_traits { static constexpr bool converts_to_string{true}; static constexpr bool converts_from_string{true}; static std::size_t - size_buffer(std::basic_string const &value) noexcept + size_buffer(bytes const &value) noexcept { return internal::size_esc_bin(std::size(value)); } static zview - to_buf(char *begin, char *end, std::basic_string const &value) + to_buf(char *begin, char *end, bytes const &value) { return generic_to_buf(begin, end, value); } static char * - into_buf(char *begin, char *end, std::basic_string const &value) + into_buf(char *begin, char *end, bytes const &value) { auto const budget{size_buffer(value)}; if (internal::cmp_less(end - begin, budget)) @@ -953,10 +953,10 @@ template<> struct string_traits> return begin + budget; } - static std::basic_string from_string(std::string_view text) + static bytes from_string(std::string_view text) { auto const size{pqxx::internal::size_unesc_bin(std::size(text))}; - std::basic_string buf; + bytes buf; buf.resize(size); pqxx::internal::unesc_bin(text, reinterpret_cast(buf.data())); return buf; @@ -965,37 +965,37 @@ template<> struct string_traits> template<> -inline constexpr format param_format(std::basic_string const &) +inline constexpr format param_format(bytes const &) { return format::binary; } template<> -struct nullness> - : no_null> +struct nullness + : no_null {}; -template<> struct string_traits> +template<> struct string_traits { static constexpr bool converts_to_string{true}; static constexpr bool converts_from_string{false}; static std::size_t - size_buffer(std::basic_string_view const &value) noexcept + size_buffer(bytes_view const &value) noexcept { return internal::size_esc_bin(std::size(value)); } static zview to_buf( - char *begin, char *end, std::basic_string_view const &value) + char *begin, char *end, bytes_view const &value) { return generic_to_buf(begin, end, value); } static char *into_buf( - char *begin, char *end, std::basic_string_view const &value) + char *begin, char *end, bytes_view const &value) { auto const budget{size_buffer(value)}; if (internal::cmp_less(end - begin, budget)) @@ -1009,7 +1009,7 @@ template<> struct string_traits> }; template<> -inline constexpr format param_format(std::basic_string_view const &) +inline constexpr format param_format(bytes_view const &) { return format::binary; } diff --git a/include/pqxx/params.hxx b/include/pqxx/params.hxx index ec7f7373f..b7135007c 100644 --- a/include/pqxx/params.hxx +++ b/include/pqxx/params.hxx @@ -98,14 +98,13 @@ public: /** The underlying data must stay valid for as long as the `params` * remains active. */ - void append(std::basic_string_view) &; + void append(bytes_view) &; /// Append a non-null binary parameter. /** Copies the underlying data into internal storage. For best efficiency, - * use the `std::basic_string_view` variant if you can, or - * `std::move()`. + * use the `pqxx::bytes_view` variant if you can, or `std::move()`. */ - void append(std::basic_string const &) &; + void append(bytes const &) &; #if defined(PQXX_HAVE_CONCEPTS) /// Append a non-null binary parameter. @@ -114,13 +113,12 @@ public: */ template void append(DATA const &data) & { - append( - std::basic_string_view{std::data(data), std::size(data)}); + append(bytes_view{std::data(data), std::size(data)}); } #endif // PQXX_HAVE_CONCEPTS /// Append a non-null binary parameter. - void append(std::basic_string &&) &; + void append(bytes &&) &; /// @deprecated Append binarystring parameter. /** The binarystring must stay valid for as long as the `params` remains @@ -197,9 +195,8 @@ private: // The way we store a parameter depends on whether it's binary or text // (most types are text), and whether we're responsible for storing the // contents. - using entry = std::variant< - std::nullptr_t, zview, std::string, std::basic_string_view, - std::basic_string>; + using entry = + std::variant; std::vector m_params; static constexpr std::string_view s_overflow{ diff --git a/include/pqxx/strconv.hxx b/include/pqxx/strconv.hxx index 6a6693eaa..b28f54bde 100644 --- a/include/pqxx/strconv.hxx +++ b/include/pqxx/strconv.hxx @@ -280,8 +280,7 @@ template struct forbidden_conversion * If you wanted a single-character string, use `std::string_view` (or a * similar type such as `std::string`). * - * Or if you had a raw byte in mind, try `std::basic_string_view` - * instead. + * Or if you had a raw byte in mind, try `pqxx::bytes_view` instead. */ template<> struct string_traits : forbidden_conversion {}; @@ -299,8 +298,7 @@ template<> struct string_traits : forbidden_conversion * If you wanted a single-character string, use `std::string_view` (or a * similar type such as `std::string`). * - * Or if you had a raw byte in mind, try `std::basic_string_view` - * instead. + * Or if you had a raw byte in mind, try `pqxx::bytes_view` instead. */ template<> struct string_traits : forbidden_conversion @@ -319,8 +317,7 @@ struct string_traits : forbidden_conversion * If you wanted a single-character string, use `std::string_view` (or a * similar type such as `std::string`). * - * Or if you had a raw byte in mind, try `std::basic_string_view` - * instead. + * Or if you had a raw byte in mind, try `pqxx::bytes_view` instead. */ template<> struct string_traits : forbidden_conversion @@ -328,10 +325,10 @@ struct string_traits : forbidden_conversion /// You cannot convert a `std::byte` to/from SQL. -/** To convert a raw byte value, use a `std::basic_string_view`. +/** To convert a raw byte value, use a `bytes_view`. * * For example, to convert a byte `b` from C++ to SQL, convert the value - * `std::basic_string_view{&b, 1}` instead. + * `pqxx::bytes_view{&b, 1}` instead. */ template<> struct string_traits : forbidden_conversion {}; diff --git a/include/pqxx/transaction_base.hxx b/include/pqxx/transaction_base.hxx index 8f008b14a..e1a10b35d 100644 --- a/include/pqxx/transaction_base.hxx +++ b/include/pqxx/transaction_base.hxx @@ -166,10 +166,7 @@ public: /** Takes a binary string as escaped by PostgreSQL, and returns a restored * copy of the original binary data. */ - [[nodiscard]] std::basic_string unesc_bin(zview text) - { - return conn().unesc_bin(text); - } + [[nodiscard]] bytes unesc_bin(zview text) { return conn().unesc_bin(text); } /// Unescape binary data, e.g. from a table field or notification payload. /** Takes a binary string as escaped by PostgreSQL, and returns a restored @@ -187,7 +184,7 @@ public: /** Takes a binary string as escaped by PostgreSQL, and returns a restored * copy of the original binary data. */ - [[nodiscard]] std::basic_string unesc_bin(char const text[]) + [[nodiscard]] bytes unesc_bin(char const text[]) { return conn().unesc_bin(text); } @@ -199,22 +196,21 @@ public: return conn().quote(t); } - [[deprecated( - "Use std::basic_string instead of binarystring.")]] std::string + [[deprecated("Use bytes instead of binarystring.")]] std::string quote(binarystring const &t) const { return conn().quote(t.bytes_view()); } /// Binary-escape and quote a binary string for use as an SQL constant. - [[deprecated("Use quote(std::basic_string_view).")]] std::string + [[deprecated("Use quote(pqxx::bytes_view).")]] std::string quote_raw(unsigned char const bin[], std::size_t len) const { return quote(binary_cast(bin, len)); } /// Binary-escape and quote a binary string for use as an SQL constant. - [[deprecated("Use quote(std::basic_string_view).")]] std::string + [[deprecated("Use quote(pqxx::bytes_view).")]] std::string quote_raw(zview bin) const; #if defined(PQXX_HAVE_CONCEPTS) @@ -867,10 +863,9 @@ public: * a zero byte, the last byte in the value will be the one just before the * zero. If you need a zero byte, you're dealing with binary strings, not * regular strings. Represent binary strings on the SQL side as `BYTEA` - * (or as large objects). On the C++ side, use types like - * `std::basic_string` or `std::basic_string_view` - * or (in C++20) `std::vector`. Also, consider large objects on - * the SQL side and @ref blob on the C++ side. + * (or as large objects). On the C++ side, use types like `pqxx::bytes` or + * `pqxx::bytes_view` or (in C++20) `std::vector`. Also, consider + * large objects on the SQL side and @ref blob on the C++ side. * * @warning Passing the wrong number of parameters to a prepared or * parameterised statement will _break the connection._ The usual exception diff --git a/include/pqxx/util.hxx b/include/pqxx/util.hxx index 19217b71c..2f0f509bb 100644 --- a/include/pqxx/util.hxx +++ b/include/pqxx/util.hxx @@ -280,6 +280,103 @@ struct PQXX_LIBEXPORT thread_safety_model # define PQXX_POTENTIAL_BINARY_ARG typename #endif +// A custom std::char_traits if the standard library lacks a generic +// implementation or a specialisation for std::byte. Notably they aren't +// required to provide either. +// libc++ 19 removed its generic implementation. +struct byte_char_traits : std::char_traits +{ + 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. + // But std::char_traits requires us to implement this so we treat 0 as a + // terminator for our "string". + static size_t length(const std::byte *data) + { + return std::strlen(reinterpret_cast(data)); + } + + static const std::byte * + find(const std::byte *data, std::size_t size, const std::byte &value) + { + return static_cast(std::memchr(data, static_cast(value), size)); + } + + static std::byte * + move(std::byte *dest, const std::byte *src, std::size_t size) + { + return static_cast(std::memmove(dest, src, size)); + } + + static std::byte * + copy(std::byte *dest, const std::byte *src, std::size_t size) + { + return static_cast(std::memcpy(dest, src, size)); + } + + static std::byte *assign(std::byte *dest, std::size_t size, std::byte value) + { + return static_cast(std::memset(dest, static_cast(value), size)); + } + + static int_type not_eof(int_type value) + { + return eq_int_type(value, eof()) ? ~eof() : 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() { return int_type(EOF); } +}; + +template +struct has_generic_char_traits : std::false_type +{}; + +template +struct has_generic_char_traits< + TYPE, std::void_t::eof)>> : std::true_type +{}; + +inline constexpr bool has_generic_bytes_char_traits = + has_generic_char_traits::value; + +// Supress warnings from potentially using a deprecated generic +// std::char_traits. +// Necessary for libc++ 18. +#include "pqxx/internal/ignore-deprecated-pre.hxx" + +// Type alias for a container containing bytes. +// Required to support standard libraries without a generic implementation for +// std::char_traits. +// WARNING: Will change to std::vector in the next major release. +using bytes = std::conditional< + has_generic_bytes_char_traits, std::basic_string, + std::basic_string>::type; + +// Type alias for a view of bytes. +// Required to support standard libraries without a generic implementation for +// std::char_traits. +// WARNING: Will change to std::span in the next major release. +using bytes_view = std::conditional< + has_generic_bytes_char_traits, std::basic_string_view, + std::basic_string_view>::type; + +#include "pqxx/internal/ignore-deprecated-post.hxx" /// Cast binary data to a type that libpqxx will recognise as binary. /** There are many different formats for storing binary data in memory. You @@ -287,11 +384,11 @@ struct PQXX_LIBEXPORT thread_safety_model * many other types. * * But for libpqxx to recognise your data as binary, it needs to be a - * `std::basic_string`, or a `std::basic_string_view`; - * or in C++20 or better, any contiguous block of `std::byte`. + * `pqxx::bytes`, or a `pqxx::bytes_view`; 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`. + * `pqxx::bytes_view`. * * @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 @@ -300,7 +397,7 @@ struct PQXX_LIBEXPORT thread_safety_model * return value. */ template -std::basic_string_view binary_cast(TYPE const &data) +bytes_view binary_cast(TYPE const &data) { static_assert(sizeof(value_type) == 1); // C++20: Use std::as_bytes. @@ -322,14 +419,13 @@ concept char_sized = (sizeof(CHAR) == 1); /// 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` pointing to - * the same data. + * types, and constructs a `pqxx::bytes_view` 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 -std::basic_string_view binary_cast(CHAR const *data, SIZE size) +bytes_view binary_cast(CHAR const *data, SIZE size) { static_assert(sizeof(CHAR) == 1); return { @@ -430,13 +526,11 @@ inline constexpr std::size_t size_unesc_bin(std::size_t escaped_bytes) noexcept * exactly that number of bytes into the buffer. This includes a trailing * zero. */ -void PQXX_LIBEXPORT -esc_bin(std::basic_string_view binary_data, char buffer[]) noexcept; +void PQXX_LIBEXPORT esc_bin(bytes_view binary_data, char buffer[]) noexcept; /// Hex-escape binary data into a std::string. -std::string PQXX_LIBEXPORT -esc_bin(std::basic_string_view binary_data); +std::string PQXX_LIBEXPORT esc_bin(bytes_view binary_data); /// Reconstitute binary data from its escaped version. @@ -445,8 +539,7 @@ unesc_bin(std::string_view escaped_data, std::byte buffer[]); /// Reconstitute binary data from its escaped version. -std::basic_string - PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data); +bytes PQXX_LIBEXPORT unesc_bin(std::string_view escaped_data); /// Transitional: std::ssize(), or custom implementation if not available. diff --git a/src/blob.cxx b/src/blob.cxx index 9e16fdaa1..be61fe643 100644 --- a/src/blob.cxx +++ b/src/blob.cxx @@ -151,8 +151,7 @@ std::size_t pqxx::blob::raw_read(std::byte buf[], std::size_t size) } -std::size_t -pqxx::blob::read(std::basic_string &buf, std::size_t size) +std::size_t pqxx::blob::read(bytes &buf, std::size_t size) { buf.resize(size); auto const received{raw_read(std::data(buf), size)}; @@ -228,8 +227,7 @@ std::int64_t pqxx::blob::seek_end(std::int64_t offset) } -pqxx::oid pqxx::blob::from_buf( - dbtransaction &tx, std::basic_string_view data, oid id) +pqxx::oid pqxx::blob::from_buf(dbtransaction &tx, bytes_view data, oid id) { oid actual_id{create(tx, id)}; try @@ -259,8 +257,7 @@ pqxx::oid pqxx::blob::from_buf( } -void pqxx::blob::append_from_buf( - dbtransaction &tx, std::basic_string_view data, oid id) +void pqxx::blob::append_from_buf(dbtransaction &tx, bytes_view data, oid id) { if (std::size(data) > chunk_limit) throw range_error{ @@ -272,16 +269,15 @@ void pqxx::blob::append_from_buf( void pqxx::blob::to_buf( - dbtransaction &tx, oid id, std::basic_string &buf, - std::size_t max_size) + dbtransaction &tx, oid id, bytes &buf, std::size_t max_size) { open_r(tx, id).read(buf, max_size); } std::size_t pqxx::blob::append_to_buf( - dbtransaction &tx, oid id, std::int64_t offset, - std::basic_string &buf, std::size_t append_max) + dbtransaction &tx, oid id, std::int64_t offset, bytes &buf, + std::size_t append_max) { if (append_max > chunk_limit) throw range_error{ diff --git a/src/connection.cxx b/src/connection.cxx index fada90081..da50a12b3 100644 --- a/src/connection.cxx +++ b/src/connection.cxx @@ -941,8 +941,7 @@ pqxx::connection::esc_raw(unsigned char const bin[], std::size_t len) const } -std::string -pqxx::connection::esc_raw(std::basic_string_view bin) const +std::string pqxx::connection::esc_raw(bytes_view bin) const { return pqxx::internal::esc_bin(bin); } @@ -980,8 +979,7 @@ pqxx::connection::quote_raw(unsigned char const bin[], std::size_t len) const } -std::string -pqxx::connection::quote_raw(std::basic_string_view bytes) const +std::string pqxx::connection::quote_raw(bytes_view bytes) const { return internal::concat("'", esc_raw(bytes), "'::bytea"); } @@ -993,7 +991,7 @@ std::string PQXX_COLD pqxx::connection::quote(binarystring const &b) const } -std::string pqxx::connection::quote(std::basic_string_view b) const +std::string pqxx::connection::quote(bytes_view b) const { return internal::concat("'", esc_raw(b), "'::bytea"); } diff --git a/src/params.cxx b/src/params.cxx index 4c34a45ef..1c3d448c9 100644 --- a/src/params.cxx +++ b/src/params.cxx @@ -60,19 +60,19 @@ void pqxx::params::append(params const &value) & } -void pqxx::params::append(std::basic_string_view value) & +void pqxx::params::append(bytes_view value) & { m_params.emplace_back(value); } -void pqxx::params::append(std::basic_string const &value) & +void pqxx::params::append(bytes const &value) & { m_params.emplace_back(value); } -void pqxx::params::append(std::basic_string &&value) & +void pqxx::params::append(bytes &&value) & { m_params.emplace_back(std::move(value)); } diff --git a/src/util.cxx b/src/util.cxx index 875e5ceb8..d4edf8980 100644 --- a/src/util.cxx +++ b/src/util.cxx @@ -123,8 +123,7 @@ constexpr int nibble(int c) noexcept } // namespace -void pqxx::internal::esc_bin( - std::basic_string_view binary_data, char buffer[]) noexcept +void pqxx::internal::esc_bin(bytes_view binary_data, char buffer[]) noexcept { auto here{buffer}; *here++ = '\\'; @@ -142,8 +141,7 @@ void pqxx::internal::esc_bin( } -std::string -pqxx::internal::esc_bin(std::basic_string_view binary_data) +std::string pqxx::internal::esc_bin(bytes_view binary_data) { auto const bytes{size_esc_bin(std::size(binary_data))}; std::string buf; @@ -183,11 +181,10 @@ void pqxx::internal::unesc_bin( } -std::basic_string -pqxx::internal::unesc_bin(std::string_view escaped_data) +pqxx::bytes pqxx::internal::unesc_bin(std::string_view escaped_data) { auto const bytes{size_unesc_bin(std::size(escaped_data))}; - std::basic_string buf; + pqxx::bytes buf; buf.resize(bytes); unesc_bin(escaped_data, buf.data()); return buf; diff --git a/test/test62.cxx b/test/test62.cxx index d23e49e5a..36b3c443a 100644 --- a/test/test62.cxx +++ b/test/test62.cxx @@ -23,7 +23,7 @@ void test_062() tx.exec0("CREATE TEMP TABLE pqxxbin (binfield bytea)"); - std::string const Esc{tx.esc_raw(std::basic_string{ + std::string const Esc{tx.esc_raw(bytes{ reinterpret_cast(std::data(TestStr)), std::size(TestStr)})}; @@ -32,15 +32,15 @@ void test_062() result R{tx.exec("SELECT * from pqxxbin")}; tx.exec0("DELETE FROM pqxxbin"); - auto const B{R.at(0).at(0).as>()}; + auto const B{R.at(0).at(0).as()}; PQXX_CHECK(not std::empty(B), "Binary string became empty in conversion."); PQXX_CHECK_EQUAL( std::size(B), std::size(TestStr), "Binary string was mangled."); - std::basic_string::const_iterator c; - std::basic_string::size_type i; + bytes::const_iterator c; + bytes::size_type i; for (i = 0, c = std::begin(B); i < std::size(B); ++i, ++c) { PQXX_CHECK(c != std::end(B), "Premature end to binary string."); diff --git a/test/unit/test_binarystring.cxx b/test/unit/test_binarystring.cxx index e6097d039..7a319e7f2 100644 --- a/test/unit/test_binarystring.cxx +++ b/test/unit/test_binarystring.cxx @@ -66,10 +66,9 @@ void test_binarystring() PQXX_CHECK_EQUAL( std::size(b), std::size(simple), "Escaping confuses length."); - std::string const simple_escaped{ - tx.esc_raw(std::basic_string_view{ - reinterpret_cast(std::data(simple)), - std::size(simple)})}; + std::string const simple_escaped{tx.esc_raw(pqxx::bytes_view{ + reinterpret_cast(std::data(simple)), + std::size(simple)})}; for (auto c : simple_escaped) { auto const uc{static_cast(c)}; @@ -152,8 +151,7 @@ void test_binarystring_stream() to.complete(); auto ptr{reinterpret_cast(std::data(data))}; - auto expect{ - tx.quote(std::basic_string_view{ptr, std::size(data)})}; + auto expect{tx.quote(pqxx::bytes_view{ptr, std::size(data)})}; PQXX_CHECK( tx.query_value("SELECT bin = " + expect + " FROM pqxxbinstream"), "binarystring did not stream_to properly."); @@ -187,10 +185,8 @@ void test_binarystring_array_stream() auto ptr1{reinterpret_cast(std::data(data1))}, ptr2{reinterpret_cast(std::data(data2))}; - auto expect1{ - tx.quote(std::basic_string_view{ptr1, std::size(data1)})}, - expect2{ - tx.quote(std::basic_string_view{ptr2, std::size(data2)})}; + auto expect1{tx.quote(pqxx::bytes_view{ptr1, std::size(data1)})}, + expect2{tx.quote(pqxx::bytes_view{ptr2, std::size(data2)})}; PQXX_CHECK( tx.query_value("SELECT vec[1] = " + expect1 + " FROM pqxxbinstream"), "Bytea in array came out wrong."); diff --git a/test/unit/test_blob.cxx b/test/unit/test_blob.cxx index 75d4eeb64..f57a87792 100644 --- a/test/unit/test_blob.cxx +++ b/test/unit/test_blob.cxx @@ -12,7 +12,7 @@ namespace void test_blob_is_useless_by_default() { pqxx::blob b{}; - std::basic_string buf; + pqxx::bytes buf; PQXX_CHECK_THROWS( b.read(buf, 1), pqxx::usage_error, "Read on default-constructed blob did not throw failure."); @@ -103,7 +103,7 @@ void test_blob_checks_open_mode() pqxx::blob b_w{pqxx::blob::open_w(tx, id)}; pqxx::blob b_rw{pqxx::blob::open_rw(tx, id)}; - std::basic_string buf{std::byte{3}, std::byte{2}, std::byte{1}}; + pqxx::bytes buf{std::byte{3}, std::byte{2}, std::byte{1}}; // These are all allowed: b_w.write(buf); @@ -123,7 +123,7 @@ void test_blob_checks_open_mode() void test_blob_supports_move() { - std::basic_string buf; + pqxx::bytes buf; buf.push_back(std::byte{'x'}); pqxx::connection conn; @@ -151,29 +151,26 @@ void test_blob_supports_move() void test_blob_read_reads_data() { - std::basic_string const data{ - std::byte{'a'}, std::byte{'b'}, std::byte{'c'}}; + pqxx::bytes const data{std::byte{'a'}, std::byte{'b'}, std::byte{'c'}}; pqxx::connection conn; pqxx::work tx{conn}; pqxx::oid id{pqxx::blob::from_buf(tx, data)}; - std::basic_string buf; + pqxx::bytes buf; auto b{pqxx::blob::open_rw(tx, id)}; PQXX_CHECK_EQUAL( b.read(buf, 2), 2u, "Full read() returned an unexpected value."); PQXX_CHECK_EQUAL( - buf, (std::basic_string{std::byte{'a'}, std::byte{'b'}}), + buf, (pqxx::bytes{std::byte{'a'}, std::byte{'b'}}), "Read back the wrong data."); PQXX_CHECK_EQUAL( b.read(buf, 2), 1u, "Partial read() returned an unexpected value."); PQXX_CHECK_EQUAL( - buf, (std::basic_string{std::byte{'c'}}), - "Continued read produced wrong data."); + buf, (pqxx::bytes{std::byte{'c'}}), "Continued read produced wrong data."); PQXX_CHECK_EQUAL( b.read(buf, 2), 0u, "read at end returned an unexpected value."); - PQXX_CHECK_EQUAL( - buf, (std::basic_string{}), "Read past end produced data."); + PQXX_CHECK_EQUAL(buf, (pqxx::bytes{}), "Read past end produced data."); } @@ -187,16 +184,15 @@ template inline unsigned byte_val(BYTE val) void test_blob_read_span() { #if defined(PQXX_HAVE_SPAN) - std::basic_string const data{std::byte{'u'}, std::byte{'v'}, - std::byte{'w'}, std::byte{'x'}, - std::byte{'y'}, std::byte{'z'}}; + pqxx::bytes const data{std::byte{'u'}, std::byte{'v'}, std::byte{'w'}, + std::byte{'x'}, std::byte{'y'}, std::byte{'z'}}; pqxx::connection conn; pqxx::work tx{conn}; pqxx::oid id{pqxx::blob::from_buf(tx, data)}; auto b{pqxx::blob::open_r(tx, id)}; - std::basic_string string_buf; + pqxx::bytes string_buf; string_buf.resize(2); std::span output; @@ -244,7 +240,7 @@ void test_blob_reads_vector() pqxx::connection conn; pqxx::work tx{conn}; auto id{pqxx::blob::from_buf( - tx, std::basic_string_view{ + tx, pqxx::bytes_view{ reinterpret_cast(content), std::size(content)})}; std::vector buf; buf.resize(10); @@ -264,29 +260,26 @@ void test_blob_write_appends_at_insertion_point() auto id{pqxx::blob::create(tx)}; auto b{pqxx::blob::open_rw(tx, id)}; - b.write(std::basic_string{std::byte{'z'}}); - b.write(std::basic_string{std::byte{'a'}}); + b.write(pqxx::bytes{std::byte{'z'}}); + b.write(pqxx::bytes{std::byte{'a'}}); - std::basic_string buf; + pqxx::bytes buf; b.read(buf, 5); - PQXX_CHECK_EQUAL( - buf, (std::basic_string{}), "Found data at the end."); + PQXX_CHECK_EQUAL(buf, (pqxx::bytes{}), "Found data at the end."); b.seek_abs(0); b.read(buf, 5); PQXX_CHECK_EQUAL( - buf, (std::basic_string{std::byte{'z'}, std::byte{'a'}}), + buf, (pqxx::bytes{std::byte{'z'}, std::byte{'a'}}), "Consecutive writes did not append correctly."); - b.write(std::basic_string{std::byte{'x'}}); + b.write(pqxx::bytes{std::byte{'x'}}); // Blob now contains "zax". That's not we wanted... Rewind and rewrite. b.seek_abs(1); - b.write(std::basic_string{std::byte{'y'}}); + b.write(pqxx::bytes{std::byte{'y'}}); b.seek_abs(0); b.read(buf, 5); PQXX_CHECK_EQUAL( - buf, - (std::basic_string{ - std::byte{'z'}, std::byte{'y'}, std::byte{'x'}}), + buf, (pqxx::bytes{std::byte{'z'}, std::byte{'y'}, std::byte{'x'}}), "Rewriting in the middle did not work right."); } @@ -297,7 +290,7 @@ void test_blob_writes_span() pqxx::connection conn; pqxx::work tx{conn}; constexpr char content[]{"gfbltk"}; - std::basic_string data{ + pqxx::bytes data{ reinterpret_cast(content), std::size(content)}; auto id{pqxx::blob::create(tx)}; @@ -320,7 +313,7 @@ void test_blob_writes_span() void test_blob_resize_shortens_to_desired_length() { - std::basic_string const data{ + pqxx::bytes const data{ std::byte{'w'}, std::byte{'o'}, std::byte{'r'}, std::byte{'k'}}; pqxx::connection conn; @@ -328,10 +321,10 @@ void test_blob_resize_shortens_to_desired_length() auto id{pqxx::blob::from_buf(tx, data)}; pqxx::blob::open_w(tx, id).resize(2); - std::basic_string buf; + pqxx::bytes buf; pqxx::blob::to_buf(tx, id, buf, 10); PQXX_CHECK_EQUAL( - buf, (std::basic_string{std::byte{'w'}, std::byte{'o'}}), + buf, (pqxx::bytes{std::byte{'w'}, std::byte{'o'}}), "Truncate did not shorten correctly."); } @@ -340,14 +333,12 @@ void test_blob_resize_extends_to_desired_length() { pqxx::connection conn; pqxx::work tx{conn}; - auto id{ - pqxx::blob::from_buf(tx, std::basic_string{std::byte{100}})}; + auto id{pqxx::blob::from_buf(tx, pqxx::bytes{std::byte{100}})}; pqxx::blob::open_w(tx, id).resize(3); - std::basic_string buf; + pqxx::bytes buf; pqxx::blob::to_buf(tx, id, buf, 10); PQXX_CHECK_EQUAL( - buf, - (std::basic_string{std::byte{100}, std::byte{0}, std::byte{0}}), + buf, (pqxx::bytes{std::byte{100}, std::byte{0}, std::byte{0}}), "Resize did not zero-extend correctly."); } @@ -361,7 +352,7 @@ void test_blob_tell_tracks_position() PQXX_CHECK_EQUAL( b.tell(), 0, "Empty blob started out in non-zero position."); - b.write(std::basic_string{std::byte{'e'}, std::byte{'f'}}); + b.write(pqxx::bytes{std::byte{'e'}, std::byte{'f'}}); PQXX_CHECK_EQUAL( b.tell(), 2, "Empty blob started out in non-zero position."); b.seek_abs(1); @@ -371,15 +362,15 @@ void test_blob_tell_tracks_position() void test_blob_seek_sets_positions() { - std::basic_string data{ - std::byte{0}, std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4}, - std::byte{5}, std::byte{6}, std::byte{7}, std::byte{8}, std::byte{9}}; + pqxx::bytes data{std::byte{0}, std::byte{1}, std::byte{2}, std::byte{3}, + std::byte{4}, std::byte{5}, std::byte{6}, std::byte{7}, + std::byte{8}, std::byte{9}}; pqxx::connection conn; pqxx::work tx{conn}; auto id{pqxx::blob::from_buf(tx, data)}; auto b{pqxx::blob::open_r(tx, id)}; - std::basic_string buf; + pqxx::bytes buf; b.seek_rel(3); b.read(buf, 1u); PQXX_CHECK_EQUAL( @@ -402,8 +393,8 @@ void test_blob_seek_sets_positions() void test_blob_from_buf_interoperates_with_to_buf() { - std::basic_string const data{std::byte{'h'}, std::byte{'i'}}; - std::basic_string buf; + pqxx::bytes const data{std::byte{'h'}, std::byte{'i'}}; + pqxx::bytes buf; pqxx::connection conn; pqxx::work tx{conn}; pqxx::blob::to_buf(tx, pqxx::blob::from_buf(tx, data), buf, 10); @@ -413,13 +404,13 @@ void test_blob_from_buf_interoperates_with_to_buf() void test_blob_append_from_buf_appends() { - std::basic_string const data{std::byte{'h'}, std::byte{'o'}}; + pqxx::bytes const data{std::byte{'h'}, std::byte{'o'}}; pqxx::connection conn; pqxx::work tx{conn}; auto id{pqxx::blob::create(tx)}; pqxx::blob::append_from_buf(tx, data, id); pqxx::blob::append_from_buf(tx, data, id); - std::basic_string buf; + pqxx::bytes buf; pqxx::blob::to_buf(tx, id, buf, 10); PQXX_CHECK_EQUAL(buf, data + data, "append_from_buf() wrote wrong data?"); } @@ -445,8 +436,7 @@ my_fopen(char const *path, char const *mode) } -void read_file( - char const path[], std::size_t len, std::basic_string &buf) +void read_file(char const path[], std::size_t len, pqxx::bytes &buf) { buf.resize(len); auto f{my_fopen(path, "rb")}; @@ -458,7 +448,7 @@ void read_file( } -void write_file(char const path[], std::basic_string_view data) +void write_file(char const path[], pqxx::bytes_view data) { try { @@ -482,8 +472,7 @@ class TempFile { public: /// Create (and later clean up) a file at path containing data. - TempFile(char const path[], std::basic_string_view data) : - m_path(path) + TempFile(char const path[], pqxx::bytes_view data) : m_path(path) { write_file(path, data); } @@ -499,11 +488,11 @@ class TempFile void test_blob_from_file_creates_blob_from_file_contents() { char const temp_file[] = "blob-test-from_file.tmp"; - std::basic_string const data{std::byte{'4'}, std::byte{'2'}}; + pqxx::bytes const data{std::byte{'4'}, std::byte{'2'}}; pqxx::connection conn; pqxx::work tx{conn}; - std::basic_string buf; + pqxx::bytes buf; pqxx::oid id; { @@ -517,9 +506,9 @@ void test_blob_from_file_creates_blob_from_file_contents() void test_blob_from_file_with_oid_writes_blob() { - std::basic_string const data{std::byte{'6'}, std::byte{'9'}}; + pqxx::bytes const data{std::byte{'6'}, std::byte{'9'}}; char const temp_file[] = "blob-test-from_file-oid.tmp"; - std::basic_string buf; + pqxx::bytes buf; pqxx::connection conn; pqxx::work tx{conn}; @@ -539,14 +528,14 @@ void test_blob_from_file_with_oid_writes_blob() void test_blob_append_to_buf_appends() { - std::basic_string const data{ + pqxx::bytes const data{ std::byte{'b'}, std::byte{'l'}, std::byte{'u'}, std::byte{'b'}}; pqxx::connection conn; pqxx::work tx{conn}; auto id{pqxx::blob::from_buf(tx, data)}; - std::basic_string buf; + pqxx::bytes buf; PQXX_CHECK_EQUAL( pqxx::blob::append_to_buf(tx, id, 0u, buf, 1u), 1u, "append_to_buf() returned unexpected value."); @@ -563,14 +552,13 @@ void test_blob_append_to_buf_appends() void test_blob_to_file_writes_file() { - std::basic_string const data{ - std::byte{'C'}, std::byte{'+'}, std::byte{'+'}}; + pqxx::bytes const data{std::byte{'C'}, std::byte{'+'}, std::byte{'+'}}; char const temp_file[] = "blob-test-to_file.tmp"; pqxx::connection conn; pqxx::work tx{conn}; auto id{pqxx::blob::from_buf(tx, data)}; - std::basic_string buf; + pqxx::bytes buf; try { @@ -591,11 +579,10 @@ void test_blob_close_leaves_blob_unusable() { pqxx::connection conn; pqxx::work tx{conn}; - auto id{ - pqxx::blob::from_buf(tx, std::basic_string{std::byte{1}})}; + auto id{pqxx::blob::from_buf(tx, pqxx::bytes{std::byte{1}})}; auto b{pqxx::blob::open_rw(tx, id)}; b.close(); - std::basic_string buf; + pqxx::bytes buf; PQXX_CHECK_THROWS( b.read(buf, 1), pqxx::usage_error, "Reading from closed blob did not fail right."); @@ -609,11 +596,11 @@ void test_blob_accepts_std_filesystem_path() # if !defined(__GNUC__) || (__GNUC__ > 8) char const temp_file[] = "blob-test-filesystem-path.tmp"; - std::basic_string const data{std::byte{'4'}, std::byte{'2'}}; + pqxx::bytes const data{std::byte{'4'}, std::byte{'2'}}; pqxx::connection conn; pqxx::work tx{conn}; - std::basic_string buf; + pqxx::bytes buf; TempFile f{temp_file, data}; std::filesystem::path const path{temp_file}; diff --git a/test/unit/test_escape.cxx b/test/unit/test_escape.cxx index baff4d62c..60ac1af8d 100644 --- a/test/unit/test_escape.cxx +++ b/test/unit/test_escape.cxx @@ -94,10 +94,10 @@ void test_quote_name(pqxx::transaction_base &t) void test_esc_raw_unesc_raw(pqxx::transaction_base &t) { constexpr char binary[]{"1\0023\\4x5"}; - std::basic_string const data( + pqxx::bytes const data( reinterpret_cast(binary), std::size(binary)); - std::string const escaped{t.esc_raw( - std::basic_string_view{std::data(data), std::size(binary)})}; + std::string const escaped{ + t.esc_raw(pqxx::bytes_view{std::data(data), std::size(binary)})}; for (auto const i : escaped) { @@ -168,7 +168,7 @@ void test_esc_escapes_into_buffer() auto escaped_text{tx.esc(text, buffer)}; PQXX_CHECK_EQUAL(escaped_text, "Ain''t", "Escaping into buffer went wrong."); - std::basic_string const data{std::byte{0x22}, std::byte{0x43}}; + pqxx::bytes const data{std::byte{0x22}, std::byte{0x43}}; auto escaped_data(tx.esc(data, buffer)); PQXX_CHECK_EQUAL(escaped_data, "\\x2243", "Binary data escaped wrong."); #endif @@ -202,8 +202,7 @@ void test_binary_esc_checks_buffer_length() pqxx::work tx{conn}; std::string buf; - std::basic_string bin{ - std::byte{'b'}, std::byte{'o'}, std::byte{'o'}}; + pqxx::bytes bin{std::byte{'b'}, std::byte{'o'}, std::byte{'o'}}; buf.resize(2 * std::size(bin) + 3); pqxx::ignore_unused(tx.esc(bin, buf)); diff --git a/test/unit/test_prepared_statement.cxx b/test/unit/test_prepared_statement.cxx index c55b7e5c9..4ae830d4f 100644 --- a/test/unit/test_prepared_statement.cxx +++ b/test/unit/test_prepared_statement.cxx @@ -184,10 +184,10 @@ void test_binary() #include "pqxx/internal/ignore-deprecated-post.hxx" { - std::basic_string bytes{ + pqxx::bytes bytes{ reinterpret_cast(raw_bytes), std::size(raw_bytes)}; auto bp{tx.exec_prepared1("EchoBin", bytes)}; - auto bval{bp[0].as>()}; + auto bval{bp[0].as()}; PQXX_CHECK_EQUAL( (std::string_view{ reinterpret_cast(bval.c_str()), std::size(bval)}), @@ -195,15 +195,15 @@ void test_binary() } // Now try it with a complex type that ultimately uses the conversions of - // std::basic_string, but complex enough that the call may - // convert the data to a text string on the libpqxx side. Which would be - // okay, except of course it's likely to be slower. + // pqx::bytes, but complex enough that the call may convert the data to a + // text string on the libpqxx side. Which would be okay, except of course + // it's likely to be slower. { - auto ptr{std::make_shared>( + auto ptr{std::make_shared( reinterpret_cast(raw_bytes), std::size(raw_bytes))}; auto rp{tx.exec_prepared1("EchoBin", ptr)}; - auto pval{rp[0].as>()}; + auto pval{rp[0].as()}; PQXX_CHECK_EQUAL( (std::string_view{ reinterpret_cast(pval.c_str()), std::size(pval)}), @@ -211,11 +211,11 @@ void test_binary() } { - auto opt{std::optional>{ + auto opt{std::optional{ std::in_place, reinterpret_cast(raw_bytes), std::size(raw_bytes)}}; auto op{tx.exec_prepared1("EchoBin", opt)}; - auto oval{op[0].as>()}; + auto oval{op[0].as()}; PQXX_CHECK_EQUAL( (std::string_view{ reinterpret_cast(oval.c_str()), std::size(oval)}), @@ -223,12 +223,12 @@ void test_binary() } #if defined(PQXX_HAVE_CONCEPTS) - // By the way, it doesn't have to be a std::basic_string. Any contiguous - // range will do. + // By the way, it doesn't have to be a pqxx::bytes. Any contiguous range + // will do. { std::vector data{std::byte{'x'}, std::byte{'v'}}; auto op{tx.exec_prepared1("EchoBin", data)}; - auto oval{op[0].as>()}; + auto oval{op[0].as()}; PQXX_CHECK_EQUAL( std::size(oval), 2u, "Binary data came back as wrong length."); PQXX_CHECK_EQUAL(static_cast(oval[0]), int('x'), "Wrong data.");