test_connection.cxx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. #include <numeric>
  2. #include <pqxx/transaction>
  3. #include "../test_helpers.hxx"
  4. namespace
  5. {
  6. void test_connection_string_constructor()
  7. {
  8. pqxx::connection c1{""};
  9. pqxx::connection c2{std::string{}};
  10. }
  11. void test_move_constructor()
  12. {
  13. pqxx::connection c1;
  14. PQXX_CHECK(c1.is_open(), "New connection is not open.");
  15. pqxx::connection c2{std::move(c1)};
  16. PQXX_CHECK(not c1.is_open(), "Moving did not close original connection.");
  17. PQXX_CHECK(c2.is_open(), "Moved constructor is not open.");
  18. pqxx::work tx{c2};
  19. PQXX_CHECK_EQUAL(tx.query_value<int>("SELECT 5"), 5, "Weird result!");
  20. PQXX_CHECK_THROWS(
  21. pqxx::connection c3{std::move(c2)}, pqxx::usage_error,
  22. "Moving a connection with a transaction open should be an error.");
  23. }
  24. void test_move_assign()
  25. {
  26. pqxx::connection c1;
  27. pqxx::connection c2;
  28. c2.close();
  29. c2 = std::move(c1);
  30. PQXX_CHECK(not c1.is_open(), "Connection still open after being moved out.");
  31. PQXX_CHECK(c2.is_open(), "Moved constructor is not open.");
  32. {
  33. pqxx::work tx1{c2};
  34. PQXX_CHECK_EQUAL(tx1.query_value<int>("SELECT 8"), 8, "What!?");
  35. pqxx::connection c3;
  36. PQXX_CHECK_THROWS(
  37. c3 = std::move(c2), pqxx::usage_error,
  38. "Moving a connection with a transaction open should be an error.");
  39. PQXX_CHECK_THROWS(
  40. c2 = std::move(c3), pqxx::usage_error,
  41. "Moving a connection onto one with a transaction open should be "
  42. "an error.");
  43. }
  44. // After failed move attempts, the connection is still usable.
  45. pqxx::work tx2{c2};
  46. PQXX_CHECK_EQUAL(tx2.query_value<int>("SELECT 6"), 6, "Huh!?");
  47. }
  48. void test_encrypt_password()
  49. {
  50. pqxx::connection c;
  51. auto pw{c.encrypt_password("user", "password")};
  52. PQXX_CHECK(not std::empty(pw), "Encrypted password was empty.");
  53. PQXX_CHECK_EQUAL(
  54. std::strlen(pw.c_str()), std::size(pw),
  55. "Encrypted password contains a null byte.");
  56. }
  57. void test_connection_string()
  58. {
  59. pqxx::connection c;
  60. std::string const connstr{c.connection_string()};
  61. #if defined(_MSC_VER)
  62. # pragma warning(push)
  63. # pragma warning(disable : 4996)
  64. #endif
  65. if (std::getenv("PGUSER") == nullptr)
  66. #if defined(_MSC_VER)
  67. # pragma warning(pop)
  68. #endif
  69. {
  70. PQXX_CHECK(
  71. connstr.find("user=" + std::string{c.username()}) != std::string::npos,
  72. "Connection string did not specify user name: " + connstr);
  73. }
  74. else
  75. {
  76. PQXX_CHECK(
  77. connstr.find("user=" + std::string{c.username()}) == std::string::npos,
  78. "Connection string specified user name, even when using default: " +
  79. connstr);
  80. }
  81. }
  82. #if defined(PQXX_HAVE_CONCEPTS)
  83. template<typename STR> std::size_t length(STR const &str)
  84. {
  85. return std::size(str);
  86. }
  87. std::size_t length(char const str[])
  88. {
  89. return std::strlen(str);
  90. }
  91. #endif // PQXX_HAVE_CONCEPTS
  92. template<typename MAP> void test_params_type()
  93. {
  94. #if defined(PQXX_HAVE_CONCEPTS)
  95. using item_t = std::remove_reference_t<
  96. decltype(*std::declval<std::ranges::iterator_t<MAP>>())>;
  97. using key_t = decltype(std::get<0>(std::declval<item_t>()));
  98. using value_t = decltype(std::get<1>(std::declval<item_t>()));
  99. // Set some parameters that are relatively safe to change arbitrarily.
  100. MAP const params{{
  101. {key_t{"application_name"}, value_t{"pqxx-test"}},
  102. {key_t{"connect_timeout"}, value_t{"96"}},
  103. {key_t{"keepalives_idle"}, value_t{"771"}},
  104. }};
  105. // Can we create a connection from these parameters?
  106. pqxx::connection c{params};
  107. // Check that the parameters came through in the connection string.
  108. // We don't know the exact format, but the parameters have to be in there.
  109. auto const min_size{std::accumulate(
  110. std::cbegin(params), std::cend(params), std::size(params) - 1,
  111. [](auto count, auto item) {
  112. return count + length(std::get<0>(item)) + 1 + length(std::get<1>(item));
  113. })};
  114. auto const connstr{c.connection_string()};
  115. PQXX_CHECK_GREATER_EQUAL(
  116. std::size(connstr), min_size,
  117. "Connection string can't possibly contain the options we gave.");
  118. for (auto const &[key, value] : params)
  119. {
  120. PQXX_CHECK_NOT_EQUAL(
  121. connstr.find(key), std::string::npos,
  122. "Could not find param name '" + std::string{key} +
  123. "' in connection string: " + connstr);
  124. PQXX_CHECK_NOT_EQUAL(
  125. connstr.find(value), std::string::npos,
  126. "Could not find value for '" + std::string{value} +
  127. "' in connection string: " + connstr);
  128. }
  129. #endif // PQXX_HAVE_CONCEPTS
  130. }
  131. void test_connection_params()
  132. {
  133. // Connecting in this way supports a wide variety of formats for the
  134. // parameters.
  135. test_params_type<std::map<char const *, char const *>>();
  136. test_params_type<std::map<pqxx::zview, pqxx::zview>>();
  137. test_params_type<std::map<std::string, std::string>>();
  138. test_params_type<std::map<std::string, pqxx::zview>>();
  139. test_params_type<std::map<pqxx::zview, char const *>>();
  140. test_params_type<std::vector<std::tuple<char const *, char const *>>>();
  141. test_params_type<std::vector<std::tuple<pqxx::zview, std::string>>>();
  142. test_params_type<std::vector<std::pair<std::string, char const *>>>();
  143. test_params_type<std::vector<std::array<std::string, 2>>>();
  144. }
  145. void test_raw_connection()
  146. {
  147. pqxx::connection conn1;
  148. PQXX_CHECK(conn1.is_open(), "Fresh connection is not open!");
  149. pqxx::work tx1{conn1};
  150. PQXX_CHECK_EQUAL(
  151. tx1.query_value<int>("SELECT 8"), 8, "Something weird happened.");
  152. pqxx::internal::pq::PGconn *raw{std::move(conn1).release_raw_connection()};
  153. PQXX_CHECK(raw != nullptr, "Raw connection is null.");
  154. PQXX_CHECK(
  155. not conn1.is_open(),
  156. "Releasing raw connection did not close pqxx::connection.");
  157. pqxx::connection conn2{pqxx::connection::seize_raw_connection(raw)};
  158. PQXX_CHECK(
  159. conn2.is_open(), "Can't produce open connection from raw connection.");
  160. pqxx::work tx2{conn2};
  161. PQXX_CHECK_EQUAL(
  162. tx2.query_value<int>("SELECT 9"), 9,
  163. "Raw connection did not produce a working new connection.");
  164. }
  165. PQXX_REGISTER_TEST(test_connection_string_constructor);
  166. PQXX_REGISTER_TEST(test_move_constructor);
  167. PQXX_REGISTER_TEST(test_move_assign);
  168. PQXX_REGISTER_TEST(test_encrypt_password);
  169. PQXX_REGISTER_TEST(test_connection_string);
  170. PQXX_REGISTER_TEST(test_connection_params);
  171. PQXX_REGISTER_TEST(test_raw_connection);
  172. } // namespace