mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-21 05:53:09 +00:00
be7ce4110e
This reverts commit e96515433d
.
645 lines
18 KiB
C++
645 lines
18 KiB
C++
#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
|