test_binarystring.cxx 7.8 KB

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