test29.cxx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. #include <cstdio>
  2. #include <iostream>
  3. #include <vector>
  4. #include <pqxx/nontransaction>
  5. #include <pqxx/transaction>
  6. #include "test_helpers.hxx"
  7. using namespace pqxx;
  8. // Test program for libpqxx. Open connection to database, start a transaction,
  9. // abort it, and verify that it "never happened."
  10. //
  11. // The program will attempt to add an entry to a table called "pqxxevents",
  12. // with a key column called "year"--and then abort the change.
  13. namespace
  14. {
  15. // Let's take a boring year that is not going to be in the "pqxxevents" table
  16. constexpr int BoringYear{1977};
  17. std::string const Table{"pqxxevents"};
  18. // Count events, and boring events, in table
  19. std::pair<int, int> CountEvents(transaction_base &tx)
  20. {
  21. std::string const events_query{"SELECT count(*) FROM " + Table};
  22. std::string const boring_query{
  23. events_query + " WHERE year=" + to_string(BoringYear)};
  24. return std::make_pair(
  25. tx.query_value<int>(events_query), tx.query_value<int>(boring_query));
  26. }
  27. // Try adding a record, then aborting it, and check whether the abort was
  28. // performed correctly.
  29. void Test(connection &conn, bool ExplicitAbort)
  30. {
  31. std::vector<std::string> BoringRow{to_string(BoringYear), "yawn"};
  32. std::pair<int, int> EventCounts;
  33. // First run our doomed transaction. This will refuse to run if an event
  34. // exists for our Boring Year.
  35. {
  36. // Begin a transaction acting on our current connection; we'll abort it
  37. // later though.
  38. work Doomed(conn, "Doomed");
  39. // Verify that our Boring Year was not yet in the events table
  40. EventCounts = CountEvents(Doomed);
  41. PQXX_CHECK_EQUAL(
  42. EventCounts.second, 0,
  43. "Can't run; " + to_string(BoringYear) + " is already in the table.");
  44. // Now let's try to introduce a row for our Boring Year
  45. Doomed.exec0(
  46. "INSERT INTO " + Table +
  47. "(year, event) "
  48. "VALUES (" +
  49. to_string(BoringYear) + ", 'yawn')");
  50. auto Recount{CountEvents(Doomed)};
  51. PQXX_CHECK_EQUAL(Recount.second, 1, "Unexpected number of events.");
  52. PQXX_CHECK_EQUAL(
  53. Recount.first, EventCounts.first + 1, "Number of events changed.");
  54. // Okay, we've added an entry but we don't really want to. Abort it
  55. // explicitly if requested, or simply let the Transaction object "expire."
  56. if (ExplicitAbort)
  57. Doomed.abort();
  58. // If now explicit abort requested, Doomed Transaction still ends here
  59. }
  60. // Now check that we're back in the original state. Note that this may go
  61. // wrong if somebody managed to change the table between our two
  62. // transactions.
  63. work Checkup(conn, "Checkup");
  64. auto NewEvents{CountEvents(Checkup)};
  65. PQXX_CHECK_EQUAL(
  66. NewEvents.first, EventCounts.first, "Wrong number of events.");
  67. PQXX_CHECK_EQUAL(NewEvents.second, 0, "Found unexpected events.");
  68. }
  69. void test_029()
  70. {
  71. connection conn;
  72. {
  73. nontransaction tx{conn};
  74. test::create_pqxxevents(tx);
  75. }
  76. // Test abort semantics, both with explicit and implicit abort
  77. Test(conn, true);
  78. Test(conn, false);
  79. }
  80. PQXX_REGISTER_TEST(test_029);
  81. } // namespace