test_escape.cxx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. #include <iostream>
  2. #include <pqxx/transaction>
  3. #include "../test_helpers.hxx"
  4. namespace
  5. {
  6. using namespace std::literals;
  7. void compare_esc(
  8. pqxx::connection &c, pqxx::transaction_base &t, char const text[])
  9. {
  10. std::size_t const len{std::size(std::string{text})};
  11. PQXX_CHECK_EQUAL(
  12. c.esc(std::string_view{text, len}), t.esc(std::string_view{text, len}),
  13. "Connection & transaction escape differently.");
  14. PQXX_CHECK_EQUAL(
  15. t.esc(std::string_view{text, len}), t.esc(text),
  16. "Length argument to esc() changes result.");
  17. PQXX_CHECK_EQUAL(
  18. t.esc(std::string{text}), t.esc(text),
  19. "esc(std::string()) differs from esc(char const[]).");
  20. PQXX_CHECK_EQUAL(
  21. text,
  22. t.query_value<std::string>(
  23. "SELECT '" + t.esc(std::string_view{text, len}) + "'"),
  24. "esc() is not idempotent.");
  25. PQXX_CHECK_EQUAL(
  26. t.esc(std::string_view{text, len}), t.esc(text),
  27. "Oversized buffer affects esc().");
  28. }
  29. void test_esc(pqxx::connection &c, pqxx::transaction_base &t)
  30. {
  31. PQXX_CHECK_EQUAL(
  32. t.esc(std::string_view{"", 0}), "",
  33. "Empty string doesn't escape properly.");
  34. PQXX_CHECK_EQUAL(
  35. t.esc(std::string_view{"'", 1}), "''",
  36. "Single quote escaped incorrectly.");
  37. PQXX_CHECK_EQUAL(
  38. t.esc(std::string_view{"hello"}), "hello", "Trivial escape went wrong.");
  39. char const *const escstrings[]{"x", " ", "", nullptr};
  40. for (std::size_t i{0}; escstrings[i] != nullptr; ++i)
  41. compare_esc(c, t, escstrings[i]);
  42. }
  43. void test_quote(pqxx::connection &c, pqxx::transaction_base &t)
  44. {
  45. PQXX_CHECK_EQUAL(t.quote("x"), "'x'", "Basic quote() fails.");
  46. PQXX_CHECK_EQUAL(
  47. t.quote(1), "'1'", "quote() not dealing with int properly.");
  48. PQXX_CHECK_EQUAL(t.quote(0), "'0'", "Quoting zero is a problem.");
  49. char const *const null_ptr{nullptr};
  50. PQXX_CHECK_EQUAL(t.quote(null_ptr), "NULL", "Not quoting NULL correctly.");
  51. PQXX_CHECK_EQUAL(
  52. t.quote(std::string{"'"}), "''''", "Escaping quotes goes wrong.");
  53. PQXX_CHECK_EQUAL(
  54. t.quote("x"), c.quote("x"),
  55. "Connection and transaction quote differently.");
  56. char const *test_strings[]{"", "x", "\\", "\\\\", "'",
  57. "''", "\\'", "\t", "\n", nullptr};
  58. for (std::size_t i{0}; test_strings[i] != nullptr; ++i)
  59. {
  60. auto r{t.query_value<std::string>("SELECT " + t.quote(test_strings[i]))};
  61. PQXX_CHECK_EQUAL(
  62. r, test_strings[i], "Selecting quoted string does not come back equal.");
  63. }
  64. }
  65. void test_quote_name(pqxx::transaction_base &t)
  66. {
  67. PQXX_CHECK_EQUAL(
  68. "\"A b\"", t.quote_name("A b"), "Escaped identifier is not as expected.");
  69. PQXX_CHECK_EQUAL(
  70. std::string{"A b"},
  71. t.exec("SELECT 1 AS " + t.quote_name("A b")).column_name(0),
  72. "Escaped identifier does not work in SQL.");
  73. }
  74. void test_esc_raw_unesc_raw(pqxx::transaction_base &t)
  75. {
  76. constexpr char binary[]{"1\0023\\4x5"};
  77. std::basic_string<std::byte> const data(
  78. reinterpret_cast<std::byte const *>(binary), std::size(binary));
  79. std::string const escaped{t.esc_raw(
  80. std::basic_string_view<std::byte>{std::data(data), std::size(binary)})};
  81. for (auto const i : escaped)
  82. {
  83. PQXX_CHECK_GREATER(
  84. static_cast<unsigned>(static_cast<unsigned char>(i)), 7u,
  85. "Non-ASCII character in escaped data: " + escaped);
  86. PQXX_CHECK_LESS(
  87. static_cast<unsigned>(static_cast<unsigned char>(i)), 127u,
  88. "Non-ASCII character in escaped data: " + escaped);
  89. }
  90. for (auto const i : escaped)
  91. PQXX_CHECK(
  92. isprint(i), "Unprintable character in escaped data: " + escaped);
  93. PQXX_CHECK_EQUAL(
  94. escaped, "\\x3102335c34783500", "Binary data escaped wrong.");
  95. PQXX_CHECK_EQUAL(
  96. std::size(t.unesc_bin(escaped)), std::size(data),
  97. "Wrong size after unescaping.");
  98. auto unescaped{t.unesc_bin(escaped)};
  99. PQXX_CHECK_EQUAL(
  100. std::size(unescaped), std::size(data),
  101. "Unescaping did not restore original size.");
  102. for (std::size_t i{0}; i < std::size(unescaped); ++i)
  103. PQXX_CHECK_EQUAL(
  104. int(unescaped[i]), int(data[i]),
  105. "Unescaping binary data did not restore byte #" + pqxx::to_string(i) +
  106. ".");
  107. }
  108. void test_esc_like(pqxx::transaction_base &tx)
  109. {
  110. PQXX_CHECK_EQUAL(tx.esc_like(""), "", "esc_like breaks on empty string.");
  111. PQXX_CHECK_EQUAL(tx.esc_like("abc"), "abc", "esc_like is broken.");
  112. PQXX_CHECK_EQUAL(tx.esc_like("_"), "\\_", "esc_like fails on underscore.");
  113. PQXX_CHECK_EQUAL(tx.esc_like("%"), "\\%", "esc_like fails on %.");
  114. PQXX_CHECK_EQUAL(
  115. tx.esc_like("a%b_c"), "a\\%b\\_c", "esc_like breaks on mix.");
  116. PQXX_CHECK_EQUAL(
  117. tx.esc_like("_", '+'), "+_", "esc_like ignores escape character.");
  118. }
  119. void test_escaping()
  120. {
  121. pqxx::connection conn;
  122. pqxx::work tx{conn};
  123. test_esc(conn, tx);
  124. test_quote(conn, tx);
  125. test_quote_name(tx);
  126. test_esc_raw_unesc_raw(tx);
  127. test_esc_like(tx);
  128. }
  129. void test_esc_escapes_into_buffer()
  130. {
  131. #if defined(PQXX_HAVE_CONCEPTS)
  132. pqxx::connection conn;
  133. pqxx::work tx{conn};
  134. std::string buffer;
  135. buffer.resize(20);
  136. auto const text{"Ain't"sv};
  137. auto escaped_text{tx.esc(text, buffer)};
  138. PQXX_CHECK_EQUAL(escaped_text, "Ain''t", "Escaping into buffer went wrong.");
  139. std::basic_string<std::byte> const data{std::byte{0x22}, std::byte{0x43}};
  140. auto escaped_data(tx.esc(data, buffer));
  141. PQXX_CHECK_EQUAL(escaped_data, "\\x2243", "Binary data escaped wrong.");
  142. #endif
  143. }
  144. void test_esc_accepts_various_types()
  145. {
  146. #if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN)
  147. pqxx::connection conn;
  148. pqxx::work tx{conn};
  149. std::string buffer;
  150. buffer.resize(20);
  151. std::string const text{"it's"};
  152. auto escaped_text{tx.esc(text, buffer)};
  153. PQXX_CHECK_EQUAL(escaped_text, "it''s", "Escaping into buffer went wrong.");
  154. std::vector<std::byte> const data{std::byte{0x23}, std::byte{0x44}};
  155. auto escaped_data(tx.esc(data, buffer));
  156. PQXX_CHECK_EQUAL(escaped_data, "\\x2344", "Binary data escaped wrong.");
  157. #endif
  158. }
  159. void test_binary_esc_checks_buffer_length()
  160. {
  161. #if defined(PQXX_HAVE_CONCEPTS) && defined(PQXX_HAVE_SPAN)
  162. pqxx::connection conn;
  163. pqxx::work tx{conn};
  164. std::string buf;
  165. std::basic_string<std::byte> bin{
  166. std::byte{'b'}, std::byte{'o'}, std::byte{'o'}};
  167. buf.resize(2 * std::size(bin) + 3);
  168. pqxx::ignore_unused(tx.esc(bin, buf));
  169. PQXX_CHECK_EQUAL(int{buf[0]}, int{'\\'}, "Unexpected binary escape format.");
  170. PQXX_CHECK_NOT_EQUAL(
  171. int(buf[std::size(buf) - 2]), int('\0'), "Escaped binary ends too soon.");
  172. PQXX_CHECK_EQUAL(
  173. int(buf[std::size(buf) - 1]), int('\0'), "Terminating zero is missing.");
  174. buf.resize(2 * std::size(bin) + 2);
  175. PQXX_CHECK_THROWS(
  176. pqxx::ignore_unused(tx.esc(bin, buf)), pqxx::range_error,
  177. "Didn't get expected exception from escape overrun.");
  178. #endif
  179. }
  180. PQXX_REGISTER_TEST(test_escaping);
  181. PQXX_REGISTER_TEST(test_esc_escapes_into_buffer);
  182. PQXX_REGISTER_TEST(test_esc_accepts_various_types);
  183. PQXX_REGISTER_TEST(test_binary_esc_checks_buffer_length);
  184. } // namespace