| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644 |
- #include <cstdio>
- #include <pqxx/blob>
- #include <pqxx/transaction>
- #include "../test_helpers.hxx"
- #include "../test_types.hxx"
- namespace
- {
- void test_blob_is_useless_by_default()
- {
- pqxx::blob b{};
- std::basic_string<std::byte> buf;
- PQXX_CHECK_THROWS(
- b.read(buf, 1), pqxx::usage_error,
- "Read on default-constructed blob did not throw failure.");
- PQXX_CHECK_THROWS(
- b.write(buf), pqxx::usage_error,
- "Write on default-constructed blob did not throw failure.");
- }
- void test_blob_create_makes_empty_blob()
- {
- pqxx::connection conn;
- pqxx::work tx{conn};
- pqxx::oid id{pqxx::blob::create(tx)};
- auto b{pqxx::blob::open_r(tx, id)};
- b.seek_end(0);
- PQXX_CHECK_EQUAL(b.tell(), 0, "New blob is not empty.");
- }
- void test_blob_create_with_oid_requires_oid_be_free()
- {
- pqxx::connection conn;
- pqxx::work tx{conn};
- auto id{pqxx::blob::create(tx)};
- PQXX_CHECK_THROWS(
- pqxx::ignore_unused(pqxx::blob::create(tx, id)), pqxx::failure,
- "Not getting expected error when oid not free.");
- }
- void test_blob_create_with_oid_obeys_oid()
- {
- pqxx::connection conn;
- pqxx::work tx{conn};
- auto id{pqxx::blob::create(tx)};
- pqxx::blob::remove(tx, id);
- auto actual_id{pqxx::blob::create(tx, id)};
- PQXX_CHECK_EQUAL(actual_id, id, "Create with oid returned different oid.");
- }
- void test_blobs_are_transactional()
- {
- pqxx::connection conn;
- pqxx::work tx{conn};
- pqxx::oid id{pqxx::blob::create(tx)};
- tx.abort();
- pqxx::work tx2{conn};
- PQXX_CHECK_THROWS(
- pqxx::ignore_unused(pqxx::blob::open_r(tx2, id)), pqxx::failure,
- "Blob from aborted transaction still exists.");
- }
- void test_blob_remove_removes_blob()
- {
- pqxx::connection conn;
- pqxx::work tx{conn};
- pqxx::oid id{pqxx::blob::create(tx)};
- pqxx::blob::remove(tx, id);
- PQXX_CHECK_THROWS(
- pqxx::ignore_unused(pqxx::blob::open_r(tx, id)), pqxx::failure,
- "Attempt to open blob after removing should have failed.");
- }
- void test_blob_remove_is_not_idempotent()
- {
- pqxx::connection conn;
- pqxx::work tx{conn};
- pqxx::oid id{pqxx::blob::create(tx)};
- pqxx::blob::remove(tx, id);
- PQXX_CHECK_THROWS(
- pqxx::blob::remove(tx, id), pqxx::failure,
- "Redundant remove() did not throw failure.");
- }
- void test_blob_checks_open_mode()
- {
- pqxx::connection conn;
- pqxx::work tx{conn};
- pqxx::oid id{pqxx::blob::create(tx)};
- pqxx::blob b_r{pqxx::blob::open_r(tx, id)};
- pqxx::blob b_w{pqxx::blob::open_w(tx, id)};
- pqxx::blob b_rw{pqxx::blob::open_rw(tx, id)};
- std::basic_string<std::byte> buf{std::byte{3}, std::byte{2}, std::byte{1}};
- // These are all allowed:
- b_w.write(buf);
- b_r.read(buf, 3);
- b_rw.seek_end(0);
- b_rw.write(buf);
- b_rw.seek_abs(0);
- b_rw.read(buf, 6);
- // These are not:
- PQXX_CHECK_THROWS(
- b_r.write(buf), pqxx::failure, "Read-only blob did not stop write.");
- PQXX_CHECK_THROWS(
- b_w.read(buf, 10), pqxx::failure, "Write-only blob did not stop read.");
- }
- void test_blob_supports_move()
- {
- std::basic_string<std::byte> buf;
- buf.push_back(std::byte{'x'});
- pqxx::connection conn;
- pqxx::work tx{conn};
- pqxx::oid id{pqxx::blob::create(tx)};
- pqxx::blob b1{pqxx::blob::open_rw(tx, id)};
- b1.write(buf);
- pqxx::blob b2{std::move(b1)};
- b2.seek_abs(0);
- b2.read(buf, 1u);
- PQXX_CHECK_THROWS(
- b1.read(buf, 1u), pqxx::usage_error,
- "Blob still works after move construction.");
- b1 = std::move(b2);
- b1.read(buf, 1u);
- PQXX_CHECK_THROWS(
- b2.read(buf, 1u), pqxx::usage_error,
- "Blob still works after move assignment.");
- }
- void test_blob_read_reads_data()
- {
- std::basic_string<std::byte> const data{
- std::byte{'a'}, std::byte{'b'}, std::byte{'c'}};
- pqxx::connection conn;
- pqxx::work tx{conn};
- pqxx::oid id{pqxx::blob::from_buf(tx, data)};
- std::basic_string<std::byte> buf;
- auto b{pqxx::blob::open_rw(tx, id)};
- PQXX_CHECK_EQUAL(
- b.read(buf, 2), 2u, "Full read() returned an unexpected value.");
- PQXX_CHECK_EQUAL(
- buf, (std::basic_string<std::byte>{std::byte{'a'}, std::byte{'b'}}),
- "Read back the wrong data.");
- PQXX_CHECK_EQUAL(
- b.read(buf, 2), 1u, "Partial read() returned an unexpected value.");
- PQXX_CHECK_EQUAL(
- buf, (std::basic_string<std::byte>{std::byte{'c'}}),
- "Continued read produced wrong data.");
- PQXX_CHECK_EQUAL(
- b.read(buf, 2), 0u, "read at end returned an unexpected value.");
- PQXX_CHECK_EQUAL(
- buf, (std::basic_string<std::byte>{}), "Read past end produced data.");
- }
- void test_blob_read_span()
- {
- #if defined(PQXX_HAVE_SPAN)
- std::basic_string<std::byte> const data{std::byte{'u'}, std::byte{'v'},
- std::byte{'w'}, std::byte{'x'},
- std::byte{'y'}, std::byte{'z'}};
- pqxx::connection conn;
- pqxx::work tx{conn};
- pqxx::oid id{pqxx::blob::from_buf(tx, data)};
- auto b{pqxx::blob::open_r(tx, id)};
- std::basic_string<std::byte> string_buf;
- string_buf.resize(2);
- std::span<std::byte> output;
- output = b.read(std::span<std::byte>{});
- PQXX_CHECK_EQUAL(
- std::size(output), 0u, "Empty read produced nonempty buffer.");
- output = b.read(string_buf);
- PQXX_CHECK_EQUAL(
- std::size(output), 2u, "Got unexpected buf size from blob::read().");
- PQXX_CHECK_EQUAL(
- output[0], std::byte{'u'}, "Unexpected byte from blob::read().");
- PQXX_CHECK_EQUAL(
- output[1], std::byte{'v'}, "Unexpected byte from blob::read().");
- string_buf.resize(100);
- output = b.read(std::span<std::byte>{string_buf.data(), 1});
- PQXX_CHECK_EQUAL(
- std::size(output), 1u,
- "Did blob::read() follow string size instead of span size?");
- PQXX_CHECK_EQUAL(
- output[0], std::byte{'w'}, "Unexpected byte from blob::read().");
- std::vector<std::byte> vec_buf;
- vec_buf.resize(2);
- auto output2{b.read(vec_buf)};
- PQXX_CHECK_EQUAL(
- std::size(output2), 2u, "Got unexpected buf size from blob::read().");
- PQXX_CHECK_EQUAL(
- output2[0], std::byte{'x'}, "Unexpected byte from blob::read().");
- PQXX_CHECK_EQUAL(
- output2[1], std::byte{'y'}, "Unexpected byte from blob::read().");
- vec_buf.resize(100);
- output2 = b.read(vec_buf);
- PQXX_CHECK_EQUAL(std::size(output2), 1u, "Weird things happened at EOF.");
- PQXX_CHECK_EQUAL(output2[0], std::byte{'z'}, "Bad data at EOF.");
- #endif // PQXX_HAVE_SPAN
- }
- void test_blob_reads_vector()
- {
- char const content[]{"abcd"};
- pqxx::connection conn;
- pqxx::work tx{conn};
- auto id{pqxx::blob::from_buf(
- tx, std::basic_string_view<std::byte>{
- reinterpret_cast<std::byte const *>(content), std::size(content)})};
- std::vector<std::byte> buf;
- buf.resize(10);
- auto out{pqxx::blob::open_r(tx, id).read(buf)};
- PQXX_CHECK_EQUAL(
- std::size(out), std::size(content),
- "Got wrong length back when reading as vector.");
- PQXX_CHECK_EQUAL(
- out[0], std::byte{'a'}, "Got bad data when reading as vector.");
- }
- void test_blob_write_appends_at_insertion_point()
- {
- pqxx::connection conn;
- pqxx::work tx{conn};
- auto id{pqxx::blob::create(tx)};
- auto b{pqxx::blob::open_rw(tx, id)};
- b.write(std::basic_string<std::byte>{std::byte{'z'}});
- b.write(std::basic_string<std::byte>{std::byte{'a'}});
- std::basic_string<std::byte> buf;
- b.read(buf, 5);
- PQXX_CHECK_EQUAL(
- buf, (std::basic_string<std::byte>{}), "Found data at the end.");
- b.seek_abs(0);
- b.read(buf, 5);
- PQXX_CHECK_EQUAL(
- buf, (std::basic_string<std::byte>{std::byte{'z'}, std::byte{'a'}}),
- "Consecutive writes did not append correctly.");
- b.write(std::basic_string<std::byte>{std::byte{'x'}});
- // Blob now contains "zax". That's not we wanted... Rewind and rewrite.
- b.seek_abs(1);
- b.write(std::basic_string<std::byte>{std::byte{'y'}});
- b.seek_abs(0);
- b.read(buf, 5);
- PQXX_CHECK_EQUAL(
- buf,
- (std::basic_string<std::byte>{
- std::byte{'z'}, std::byte{'y'}, std::byte{'x'}}),
- "Rewriting in the middle did not work right.");
- }
- void test_blob_writes_span()
- {
- #if defined(PQXX_HAVE_SPAN)
- pqxx::connection conn;
- pqxx::work tx{conn};
- constexpr char content[]{"gfbltk"};
- std::basic_string<std::byte> data{
- reinterpret_cast<std::byte const *>(content), std::size(content)};
- auto id{pqxx::blob::create(tx)};
- auto b{pqxx::blob::open_rw(tx, id)};
- b.write(std::span<std::byte>{data.data() + 1, 3u});
- b.seek_abs(0);
- std::vector<std::byte> buf;
- buf.resize(4);
- auto out{b.read(std::span<std::byte>{buf.data(), 4u})};
- PQXX_CHECK_EQUAL(
- std::size(out), 3u, "Did not get expected number of bytes back.");
- PQXX_CHECK_EQUAL(out[0], std::byte{'f'}, "Data did not come back right.");
- PQXX_CHECK_EQUAL(out[2], std::byte{'l'}, "Data started right, ended wrong!");
- #endif // PQXX_HAVE_SPAN
- }
- void test_blob_resize_shortens_to_desired_length()
- {
- std::basic_string<std::byte> const data{
- std::byte{'w'}, std::byte{'o'}, std::byte{'r'}, std::byte{'k'}};
- pqxx::connection conn;
- pqxx::work tx{conn};
- auto id{pqxx::blob::from_buf(tx, data)};
- pqxx::blob::open_w(tx, id).resize(2);
- std::basic_string<std::byte> buf;
- pqxx::blob::to_buf(tx, id, buf, 10);
- PQXX_CHECK_EQUAL(
- buf, (std::basic_string<std::byte>{std::byte{'w'}, std::byte{'o'}}),
- "Truncate did not shorten correctly.");
- }
- void test_blob_resize_extends_to_desired_length()
- {
- pqxx::connection conn;
- pqxx::work tx{conn};
- auto id{
- pqxx::blob::from_buf(tx, std::basic_string<std::byte>{std::byte{100}})};
- pqxx::blob::open_w(tx, id).resize(3);
- std::basic_string<std::byte> buf;
- pqxx::blob::to_buf(tx, id, buf, 10);
- PQXX_CHECK_EQUAL(
- buf,
- (std::basic_string<std::byte>{std::byte{100}, std::byte{0}, std::byte{0}}),
- "Resize did not zero-extend correctly.");
- }
- void test_blob_tell_tracks_position()
- {
- pqxx::connection conn;
- pqxx::work tx{conn};
- auto id{pqxx::blob::create(tx)};
- auto b{pqxx::blob::open_rw(tx, id)};
- PQXX_CHECK_EQUAL(
- b.tell(), 0, "Empty blob started out in non-zero position.");
- b.write(std::basic_string<std::byte>{std::byte{'e'}, std::byte{'f'}});
- PQXX_CHECK_EQUAL(
- b.tell(), 2, "Empty blob started out in non-zero position.");
- b.seek_abs(1);
- PQXX_CHECK_EQUAL(b.tell(), 1, "tell() did not track seek.");
- }
- void test_blob_seek_sets_positions()
- {
- std::basic_string<std::byte> data{
- std::byte{0}, std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4},
- std::byte{5}, std::byte{6}, std::byte{7}, std::byte{8}, std::byte{9}};
- pqxx::connection conn;
- pqxx::work tx{conn};
- auto id{pqxx::blob::from_buf(tx, data)};
- auto b{pqxx::blob::open_r(tx, id)};
- std::basic_string<std::byte> buf;
- b.seek_rel(3);
- b.read(buf, 1u);
- PQXX_CHECK_EQUAL(
- buf[0], std::byte{3},
- "seek_rel() from beginning did not take us to the right position.");
- b.seek_abs(2);
- b.read(buf, 1u);
- PQXX_CHECK_EQUAL(
- buf[0], std::byte{2}, "seek_abs() did not take us to the right position.");
- b.seek_end(-2);
- b.read(buf, 1u);
- PQXX_CHECK_EQUAL(
- buf[0], std::byte{8}, "seek_end() did not take us to the right position.");
- }
- void test_blob_from_buf_interoperates_with_to_buf()
- {
- std::basic_string<std::byte> const data{std::byte{'h'}, std::byte{'i'}};
- std::basic_string<std::byte> buf;
- pqxx::connection conn;
- pqxx::work tx{conn};
- pqxx::blob::to_buf(tx, pqxx::blob::from_buf(tx, data), buf, 10);
- PQXX_CHECK_EQUAL(buf, data, "from_buf()/to_buf() roundtrip did not work.");
- }
- void test_blob_append_from_buf_appends()
- {
- std::basic_string<std::byte> const data{std::byte{'h'}, std::byte{'o'}};
- pqxx::connection conn;
- pqxx::work tx{conn};
- auto id{pqxx::blob::create(tx)};
- pqxx::blob::append_from_buf(tx, data, id);
- pqxx::blob::append_from_buf(tx, data, id);
- std::basic_string<std::byte> buf;
- pqxx::blob::to_buf(tx, id, buf, 10);
- PQXX_CHECK_EQUAL(buf, data + data, "append_from_buf() wrote wrong data?");
- }
- namespace
- {
- /// Wrap `std::fopen`.
- /** This is just here to stop Visual Studio from advertising its own
- * alternative.
- */
- std::unique_ptr<FILE, std::function<int(FILE *)>>
- my_fopen(char const *path, char const *mode)
- {
- #if defined(_MSC_VER)
- # pragma warning(push)
- # pragma warning(disable : 4996)
- #endif
- return {std::fopen(path, mode), std::fclose};
- #if defined(_MSC_VER)
- # pragma warning(pop)
- #endif
- }
- void read_file(
- char const path[], std::size_t len, std::basic_string<std::byte> &buf)
- {
- buf.resize(len);
- auto f{my_fopen(path, "rb")};
- auto bytes{
- std::fread(reinterpret_cast<char *>(buf.data()), 1, len, f.get())};
- if (bytes == 0)
- throw std::runtime_error{"Error reading test file."};
- buf.resize(bytes);
- }
- void write_file(char const path[], std::basic_string_view<std::byte> data)
- {
- try
- {
- auto f{my_fopen(path, "wb")};
- if (
- std::fwrite(
- reinterpret_cast<char const *>(data.data()), 1, std::size(data),
- f.get()) < std::size(data))
- std::runtime_error{"File write failed."};
- }
- catch (const std::exception &)
- {
- std::remove(path);
- throw;
- }
- }
- /// Temporary file.
- class TempFile
- {
- public:
- /// Create (and later clean up) a file at path containing data.
- TempFile(char const path[], std::basic_string_view<std::byte> data) :
- m_path(path)
- {
- write_file(path, data);
- }
- ~TempFile() { std::remove(m_path.c_str()); }
- private:
- std::string m_path;
- };
- } // namespace
- void test_blob_from_file_creates_blob_from_file_contents()
- {
- char const temp_file[] = "blob-test-from_file.tmp";
- std::basic_string<std::byte> const data{std::byte{'4'}, std::byte{'2'}};
- pqxx::connection conn;
- pqxx::work tx{conn};
- std::basic_string<std::byte> buf;
- pqxx::oid id;
- {
- TempFile f{temp_file, data};
- id = pqxx::blob::from_file(tx, temp_file);
- }
- pqxx::blob::to_buf(tx, id, buf, 10);
- PQXX_CHECK_EQUAL(buf, data, "Wrong data from blob::from_file().");
- }
- void test_blob_from_file_with_oid_writes_blob()
- {
- std::basic_string<std::byte> const data{std::byte{'6'}, std::byte{'9'}};
- char const temp_file[] = "blob-test-from_file-oid.tmp";
- std::basic_string<std::byte> buf;
- pqxx::connection conn;
- pqxx::work tx{conn};
- // Guarantee (more or less) that id is not in use.
- auto id{pqxx::blob::create(tx)};
- pqxx::blob::remove(tx, id);
- {
- TempFile f{temp_file, data};
- pqxx::blob::from_file(tx, temp_file, id);
- }
- pqxx::blob::to_buf(tx, id, buf, 10);
- PQXX_CHECK_EQUAL(buf, data, "Wrong data from blob::from_file().");
- }
- void test_blob_append_to_buf_appends()
- {
- std::basic_string<std::byte> const data{
- std::byte{'b'}, std::byte{'l'}, std::byte{'u'}, std::byte{'b'}};
- pqxx::connection conn;
- pqxx::work tx{conn};
- auto id{pqxx::blob::from_buf(tx, data)};
- std::basic_string<std::byte> buf;
- PQXX_CHECK_EQUAL(
- pqxx::blob::append_to_buf(tx, id, 0u, buf, 1u), 1u,
- "append_to_buf() returned unexpected value.");
- PQXX_CHECK_EQUAL(std::size(buf), 1u, "Appended the wrong number of bytes.");
- PQXX_CHECK_EQUAL(
- pqxx::blob::append_to_buf(tx, id, 1u, buf, 5u), 3u,
- "append_to_buf() returned unexpected value.");
- PQXX_CHECK_EQUAL(std::size(buf), 4u, "Appended the wrong number of bytes.");
- PQXX_CHECK_EQUAL(
- buf, data, "Reading using append_to_buf gave us wrong data.");
- }
- void test_blob_to_file_writes_file()
- {
- std::basic_string<std::byte> const data{
- std::byte{'C'}, std::byte{'+'}, std::byte{'+'}};
- char const temp_file[] = "blob-test-to_file.tmp";
- pqxx::connection conn;
- pqxx::work tx{conn};
- auto id{pqxx::blob::from_buf(tx, data)};
- std::basic_string<std::byte> buf;
- try
- {
- pqxx::blob::to_file(tx, id, temp_file);
- read_file(temp_file, 10u, buf);
- std::remove(temp_file);
- }
- catch (std::exception const &)
- {
- std::remove(temp_file);
- throw;
- }
- PQXX_CHECK_EQUAL(buf, data, "Got wrong data from to_file().");
- }
- void test_blob_close_leaves_blob_unusable()
- {
- pqxx::connection conn;
- pqxx::work tx{conn};
- auto id{
- pqxx::blob::from_buf(tx, std::basic_string<std::byte>{std::byte{1}})};
- auto b{pqxx::blob::open_rw(tx, id)};
- b.close();
- std::basic_string<std::byte> buf;
- PQXX_CHECK_THROWS(
- b.read(buf, 1), pqxx::usage_error,
- "Reading from closed blob did not fail right.");
- }
- void test_blob_accepts_std_filesystem_path()
- {
- #if defined(PQXX_HAVE_PATH) && !defined(_WIN32)
- // A bug in gcc 8's ~std::filesystem::path() causes a run-time crash.
- # if !defined(__GNUC__) || (__GNUC__ > 8)
- char const temp_file[] = "blob-test-filesystem-path.tmp";
- std::basic_string<std::byte> const data{std::byte{'4'}, std::byte{'2'}};
- pqxx::connection conn;
- pqxx::work tx{conn};
- std::basic_string<std::byte> buf;
- TempFile f{temp_file, data};
- std::filesystem::path const path{temp_file};
- auto id{pqxx::blob::from_file(tx, path)};
- pqxx::blob::to_buf(tx, id, buf, 10);
- PQXX_CHECK_EQUAL(buf, data, "Wrong data from blob::from_file().");
- # endif
- #endif
- }
- PQXX_REGISTER_TEST(test_blob_is_useless_by_default);
- PQXX_REGISTER_TEST(test_blob_create_makes_empty_blob);
- PQXX_REGISTER_TEST(test_blob_create_with_oid_requires_oid_be_free);
- PQXX_REGISTER_TEST(test_blob_create_with_oid_obeys_oid);
- PQXX_REGISTER_TEST(test_blobs_are_transactional);
- PQXX_REGISTER_TEST(test_blob_remove_removes_blob);
- PQXX_REGISTER_TEST(test_blob_remove_is_not_idempotent);
- PQXX_REGISTER_TEST(test_blob_checks_open_mode);
- PQXX_REGISTER_TEST(test_blob_supports_move);
- PQXX_REGISTER_TEST(test_blob_read_reads_data);
- PQXX_REGISTER_TEST(test_blob_reads_vector);
- PQXX_REGISTER_TEST(test_blob_read_span);
- PQXX_REGISTER_TEST(test_blob_write_appends_at_insertion_point);
- PQXX_REGISTER_TEST(test_blob_writes_span);
- PQXX_REGISTER_TEST(test_blob_resize_shortens_to_desired_length);
- PQXX_REGISTER_TEST(test_blob_resize_extends_to_desired_length);
- PQXX_REGISTER_TEST(test_blob_tell_tracks_position);
- PQXX_REGISTER_TEST(test_blob_seek_sets_positions);
- PQXX_REGISTER_TEST(test_blob_from_buf_interoperates_with_to_buf);
- PQXX_REGISTER_TEST(test_blob_append_from_buf_appends);
- PQXX_REGISTER_TEST(test_blob_from_file_creates_blob_from_file_contents);
- PQXX_REGISTER_TEST(test_blob_from_file_with_oid_writes_blob);
- PQXX_REGISTER_TEST(test_blob_append_to_buf_appends);
- PQXX_REGISTER_TEST(test_blob_to_file_writes_file);
- PQXX_REGISTER_TEST(test_blob_close_leaves_blob_unusable);
- PQXX_REGISTER_TEST(test_blob_accepts_std_filesystem_path);
- } // namespace
|