File: test_binarystring.cxx

package info (click to toggle)
libpqxx 7.10.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 4,184 kB
  • sloc: cpp: 14,681; sh: 4,859; python: 801; makefile: 244
file content (216 lines) | stat: -rw-r--r-- 8,219 bytes parent folder | download
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