1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
|
#include <pqxx/binarystring>
#include <pqxx/stream_to>
#include <pqxx/transaction>
#include "../test_helpers.hxx"
#include "../test_types.hxx"
namespace
{
pqxx::binarystring
make_binarystring(pqxx::transaction_base &T, std::string content)
{
#include "pqxx/internal/ignore-deprecated-pre.hxx"
return pqxx::binarystring(
T.exec("SELECT " + T.quote_raw(content)).one_field());
#include "pqxx/internal/ignore-deprecated-post.hxx"
}
void test_binarystring()
{
pqxx::connection cx;
pqxx::work tx{cx};
auto b{make_binarystring(tx, "")};
PQXX_CHECK(std::empty(b), "Empty binarystring is not empty.");
PQXX_CHECK_EQUAL(b.str(), "", "Empty binarystring doesn't work.");
PQXX_CHECK_EQUAL(std::size(b), 0u, "Empty binarystring has nonzero size.");
PQXX_CHECK_EQUAL(b.length(), 0u, "Length/size mismatch.");
PQXX_CHECK(std::begin(b) == std::end(b), "Empty binarystring iterates.");
PQXX_CHECK(
std::cbegin(b) == std::begin(b), "Wrong cbegin for empty binarystring.");
PQXX_CHECK(
std::rbegin(b) == std::rend(b), "Empty binarystring reverse-iterates.");
PQXX_CHECK(
std::crbegin(b) == std::rbegin(b),
"Wrong crbegin for empty binarystring.");
PQXX_CHECK_THROWS(
b.at(0), std::out_of_range, "Empty binarystring accepts at().");
b = make_binarystring(tx, "z");
PQXX_CHECK_EQUAL(b.str(), "z", "Basic nonempty binarystring is broken.");
PQXX_CHECK(not std::empty(b), "Nonempty binarystring is empty.");
PQXX_CHECK_EQUAL(std::size(b), 1u, "Bad binarystring size.");
PQXX_CHECK_EQUAL(b.length(), 1u, "Length/size mismatch.");
PQXX_CHECK(
std::begin(b) != std::end(b), "Nonempty binarystring does not iterate.");
PQXX_CHECK(
std::rbegin(b) != std::rend(b),
"Nonempty binarystring does not reverse-iterate.");
PQXX_CHECK(std::begin(b) + 1 == std::end(b), "Bad iteration.");
PQXX_CHECK(std::rbegin(b) + 1 == std::rend(b), "Bad reverse iteration.");
PQXX_CHECK(std::cbegin(b) == std::begin(b), "Wrong cbegin.");
PQXX_CHECK(std::cend(b) == std::end(b), "Wrong cend.");
PQXX_CHECK(std::crbegin(b) == std::rbegin(b), "Wrong crbegin.");
PQXX_CHECK(std::crend(b) == std::rend(b), "Wrong crend.");
PQXX_CHECK(b.front() == 'z', "Unexpected front().");
PQXX_CHECK(b.back() == 'z', "Unexpected back().");
PQXX_CHECK(b.at(0) == 'z', "Unexpected data at index 0.");
PQXX_CHECK_THROWS(
b.at(1), std::out_of_range, "Failed to catch range error.");
std::string const simple{"ab"};
b = make_binarystring(tx, simple);
PQXX_CHECK_EQUAL(
b.str(), simple, "Binary (un)escaping went wrong somewhere.");
PQXX_CHECK_EQUAL(
std::size(b), std::size(simple), "Escaping confuses length.");
std::string const simple_escaped{tx.esc_raw(pqxx::bytes_view{
reinterpret_cast<std::byte const *>(std::data(simple)),
std::size(simple)})};
for (auto c : simple_escaped)
{
auto const uc{static_cast<unsigned char>(c)};
PQXX_CHECK(uc <= 127, "Non-ASCII byte in escaped string.");
}
#include "pqxx/internal/ignore-deprecated-pre.hxx"
PQXX_CHECK_EQUAL(
tx.quote_raw(
reinterpret_cast<unsigned char const *>(simple.c_str()),
std::size(simple)),
tx.quote(b), "quote_raw is broken");
PQXX_CHECK_EQUAL(
tx.quote(b), tx.quote_raw(simple), "Binary quoting is broken.");
PQXX_CHECK_EQUAL(
pqxx::binarystring(
tx.query_value<std::string>("SELECT $1", pqxx::params{b}))
.str(),
simple, "Binary string is not idempotent.");
#include "pqxx/internal/ignore-deprecated-post.hxx"
std::string const bytes("\x01\x23\x23\xa1\x2b\x0c\xff");
b = make_binarystring(tx, bytes);
PQXX_CHECK_EQUAL(b.str(), bytes, "Binary data breaks (un)escaping.");
std::string const nully("a\0b", 3);
b = make_binarystring(tx, nully);
PQXX_CHECK_EQUAL(b.str(), nully, "Nul byte broke binary (un)escaping.");
PQXX_CHECK_EQUAL(std::size(b), 3u, "Nul byte broke binarystring size.");
b = make_binarystring(tx, "foo");
PQXX_CHECK_EQUAL(std::string(b.get(), 3), "foo", "get() appears broken.");
auto b1{make_binarystring(tx, "1")}, b2{make_binarystring(tx, "2")};
PQXX_CHECK_NOT_EQUAL(b1.get(), b2.get(), "Madness rules.");
PQXX_CHECK_NOT_EQUAL(b1.str(), b2.str(), "Logic has no more meaning.");
b1.swap(b2);
PQXX_CHECK_NOT_EQUAL(b1.str(), b2.str(), "swap() equalized binarystrings.");
PQXX_CHECK_NOT_EQUAL(b1.str(), "1", "swap() did not happen.");
PQXX_CHECK_EQUAL(b1.str(), "2", "swap() is broken.");
PQXX_CHECK_EQUAL(b2.str(), "1", "swap() went insane.");
b = make_binarystring(tx, "bar");
b.swap(b);
PQXX_CHECK_EQUAL(b.str(), "bar", "Self-swap confuses binarystring.");
b = make_binarystring(tx, "\\x");
PQXX_CHECK_EQUAL(b.str(), "\\x", "Hex-escape header confused (un)escaping.");
}
void test_binarystring_conversion()
{
constexpr char bytes[]{"f\to\0o\n\0"};
std::string_view const data{bytes, std::size(bytes) - 1};
#include "pqxx/internal/ignore-deprecated-pre.hxx"
pqxx::binarystring bin{data};
#include "pqxx/internal/ignore-deprecated-post.hxx"
auto const escaped{pqxx::to_string(bin)};
PQXX_CHECK_EQUAL(
escaped, std::string_view{"\\x66096f006f0a00"}, "Unexpected hex escape.");
auto const restored{pqxx::from_string<pqxx::binarystring>(escaped)};
PQXX_CHECK_EQUAL(
std::size(restored), std::size(data), "Unescaping produced wrong length.");
}
void test_binarystring_stream()
{
constexpr char bytes[]{"a\tb\0c"};
std::string_view const data{bytes, std::size(bytes) - 1};
#include "pqxx/internal/ignore-deprecated-pre.hxx"
pqxx::binarystring bin{data};
#include "pqxx/internal/ignore-deprecated-post.hxx"
pqxx::connection cx;
pqxx::transaction tx{cx};
tx.exec("CREATE TEMP TABLE pqxxbinstream(id integer, bin bytea)").no_rows();
auto to{pqxx::stream_to::table(tx, {"pqxxbinstream"})};
to.write_values(0, bin);
to.complete();
auto ptr{reinterpret_cast<std::byte const *>(std::data(data))};
auto expect{tx.quote(pqxx::bytes_view{ptr, std::size(data)})};
PQXX_CHECK(
tx.query_value<bool>("SELECT bin = " + expect + " FROM pqxxbinstream"),
"binarystring did not stream_to properly.");
PQXX_CHECK_EQUAL(
tx.query_value<std::size_t>("SELECT octet_length(bin) FROM pqxxbinstream"),
std::size(data), "Did the terminating zero break the bytea?");
}
void test_binarystring_array_stream()
{
// This test won't compile on clang in maintainer mode. For some reason,
// clang seems to ignore the ignore-deprecated headers in just this one
// function, where we create the vector of binarystring.
#if !defined(__clang__)
pqxx::connection cx;
pqxx::transaction tx{cx};
tx.exec("CREATE TEMP TABLE pqxxbinstream(id integer, vec bytea[])")
.no_rows();
constexpr char bytes1[]{"a\tb\0c"}, bytes2[]{"1\0.2"};
std::string_view const data1{bytes1}, data2{bytes2};
# include "pqxx/internal/ignore-deprecated-pre.hxx"
pqxx::binarystring bin1{data1}, bin2{data2};
std::vector<pqxx::binarystring> const vec{bin1, bin2};
# include "pqxx/internal/ignore-deprecated-post.hxx"
auto to{pqxx::stream_to::table(tx, {"pqxxbinstream"})};
to.write_values(0, vec);
to.complete();
PQXX_CHECK_EQUAL(
tx.query_value<std::size_t>(
"SELECT array_length(vec, 1) FROM pqxxbinstream"),
std::size(vec), "Array came out with wrong length.");
auto ptr1{reinterpret_cast<std::byte const *>(std::data(data1))},
ptr2{reinterpret_cast<std::byte const *>(std::data(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<bool>("SELECT vec[1] = " + expect1 + " FROM pqxxbinstream"),
"Bytea in array came out wrong.");
PQXX_CHECK(
tx.query_value<bool>("SELECT vec[2] = " + expect2 + " FROM pqxxbinstream"),
"First bytea in array worked, but second did not.");
PQXX_CHECK_EQUAL(
tx.query_value<std::size_t>(
"SELECT octet_length(vec[1]) FROM pqxxbinstream"),
std::size(data1), "Bytea length broke inside array.");
#endif // __clang__
}
PQXX_REGISTER_TEST(test_binarystring);
PQXX_REGISTER_TEST(test_binarystring_conversion);
PQXX_REGISTER_TEST(test_binarystring_stream);
PQXX_REGISTER_TEST(test_binarystring_array_stream);
} // namespace
|