test_blob.cxx 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. #include <cstdio>
  2. #include <pqxx/blob>
  3. #include <pqxx/transaction>
  4. #include "../test_helpers.hxx"
  5. #include "../test_types.hxx"
  6. namespace
  7. {
  8. void test_blob_is_useless_by_default()
  9. {
  10. pqxx::blob b{};
  11. std::basic_string<std::byte> buf;
  12. PQXX_CHECK_THROWS(
  13. b.read(buf, 1), pqxx::usage_error,
  14. "Read on default-constructed blob did not throw failure.");
  15. PQXX_CHECK_THROWS(
  16. b.write(buf), pqxx::usage_error,
  17. "Write on default-constructed blob did not throw failure.");
  18. }
  19. void test_blob_create_makes_empty_blob()
  20. {
  21. pqxx::connection conn;
  22. pqxx::work tx{conn};
  23. pqxx::oid id{pqxx::blob::create(tx)};
  24. auto b{pqxx::blob::open_r(tx, id)};
  25. b.seek_end(0);
  26. PQXX_CHECK_EQUAL(b.tell(), 0, "New blob is not empty.");
  27. }
  28. void test_blob_create_with_oid_requires_oid_be_free()
  29. {
  30. pqxx::connection conn;
  31. pqxx::work tx{conn};
  32. auto id{pqxx::blob::create(tx)};
  33. PQXX_CHECK_THROWS(
  34. pqxx::ignore_unused(pqxx::blob::create(tx, id)), pqxx::failure,
  35. "Not getting expected error when oid not free.");
  36. }
  37. void test_blob_create_with_oid_obeys_oid()
  38. {
  39. pqxx::connection conn;
  40. pqxx::work tx{conn};
  41. auto id{pqxx::blob::create(tx)};
  42. pqxx::blob::remove(tx, id);
  43. auto actual_id{pqxx::blob::create(tx, id)};
  44. PQXX_CHECK_EQUAL(actual_id, id, "Create with oid returned different oid.");
  45. }
  46. void test_blobs_are_transactional()
  47. {
  48. pqxx::connection conn;
  49. pqxx::work tx{conn};
  50. pqxx::oid id{pqxx::blob::create(tx)};
  51. tx.abort();
  52. pqxx::work tx2{conn};
  53. PQXX_CHECK_THROWS(
  54. pqxx::ignore_unused(pqxx::blob::open_r(tx2, id)), pqxx::failure,
  55. "Blob from aborted transaction still exists.");
  56. }
  57. void test_blob_remove_removes_blob()
  58. {
  59. pqxx::connection conn;
  60. pqxx::work tx{conn};
  61. pqxx::oid id{pqxx::blob::create(tx)};
  62. pqxx::blob::remove(tx, id);
  63. PQXX_CHECK_THROWS(
  64. pqxx::ignore_unused(pqxx::blob::open_r(tx, id)), pqxx::failure,
  65. "Attempt to open blob after removing should have failed.");
  66. }
  67. void test_blob_remove_is_not_idempotent()
  68. {
  69. pqxx::connection conn;
  70. pqxx::work tx{conn};
  71. pqxx::oid id{pqxx::blob::create(tx)};
  72. pqxx::blob::remove(tx, id);
  73. PQXX_CHECK_THROWS(
  74. pqxx::blob::remove(tx, id), pqxx::failure,
  75. "Redundant remove() did not throw failure.");
  76. }
  77. void test_blob_checks_open_mode()
  78. {
  79. pqxx::connection conn;
  80. pqxx::work tx{conn};
  81. pqxx::oid id{pqxx::blob::create(tx)};
  82. pqxx::blob b_r{pqxx::blob::open_r(tx, id)};
  83. pqxx::blob b_w{pqxx::blob::open_w(tx, id)};
  84. pqxx::blob b_rw{pqxx::blob::open_rw(tx, id)};
  85. std::basic_string<std::byte> buf{std::byte{3}, std::byte{2}, std::byte{1}};
  86. // These are all allowed:
  87. b_w.write(buf);
  88. b_r.read(buf, 3);
  89. b_rw.seek_end(0);
  90. b_rw.write(buf);
  91. b_rw.seek_abs(0);
  92. b_rw.read(buf, 6);
  93. // These are not:
  94. PQXX_CHECK_THROWS(
  95. b_r.write(buf), pqxx::failure, "Read-only blob did not stop write.");
  96. PQXX_CHECK_THROWS(
  97. b_w.read(buf, 10), pqxx::failure, "Write-only blob did not stop read.");
  98. }
  99. void test_blob_supports_move()
  100. {
  101. std::basic_string<std::byte> buf;
  102. buf.push_back(std::byte{'x'});
  103. pqxx::connection conn;
  104. pqxx::work tx{conn};
  105. pqxx::oid id{pqxx::blob::create(tx)};
  106. pqxx::blob b1{pqxx::blob::open_rw(tx, id)};
  107. b1.write(buf);
  108. pqxx::blob b2{std::move(b1)};
  109. b2.seek_abs(0);
  110. b2.read(buf, 1u);
  111. PQXX_CHECK_THROWS(
  112. b1.read(buf, 1u), pqxx::usage_error,
  113. "Blob still works after move construction.");
  114. b1 = std::move(b2);
  115. b1.read(buf, 1u);
  116. PQXX_CHECK_THROWS(
  117. b2.read(buf, 1u), pqxx::usage_error,
  118. "Blob still works after move assignment.");
  119. }
  120. void test_blob_read_reads_data()
  121. {
  122. std::basic_string<std::byte> const data{
  123. std::byte{'a'}, std::byte{'b'}, std::byte{'c'}};
  124. pqxx::connection conn;
  125. pqxx::work tx{conn};
  126. pqxx::oid id{pqxx::blob::from_buf(tx, data)};
  127. std::basic_string<std::byte> buf;
  128. auto b{pqxx::blob::open_rw(tx, id)};
  129. PQXX_CHECK_EQUAL(
  130. b.read(buf, 2), 2u, "Full read() returned an unexpected value.");
  131. PQXX_CHECK_EQUAL(
  132. buf, (std::basic_string<std::byte>{std::byte{'a'}, std::byte{'b'}}),
  133. "Read back the wrong data.");
  134. PQXX_CHECK_EQUAL(
  135. b.read(buf, 2), 1u, "Partial read() returned an unexpected value.");
  136. PQXX_CHECK_EQUAL(
  137. buf, (std::basic_string<std::byte>{std::byte{'c'}}),
  138. "Continued read produced wrong data.");
  139. PQXX_CHECK_EQUAL(
  140. b.read(buf, 2), 0u, "read at end returned an unexpected value.");
  141. PQXX_CHECK_EQUAL(
  142. buf, (std::basic_string<std::byte>{}), "Read past end produced data.");
  143. }
  144. void test_blob_read_span()
  145. {
  146. #if defined(PQXX_HAVE_SPAN)
  147. std::basic_string<std::byte> const data{std::byte{'u'}, std::byte{'v'},
  148. std::byte{'w'}, std::byte{'x'},
  149. std::byte{'y'}, std::byte{'z'}};
  150. pqxx::connection conn;
  151. pqxx::work tx{conn};
  152. pqxx::oid id{pqxx::blob::from_buf(tx, data)};
  153. auto b{pqxx::blob::open_r(tx, id)};
  154. std::basic_string<std::byte> string_buf;
  155. string_buf.resize(2);
  156. std::span<std::byte> output;
  157. output = b.read(std::span<std::byte>{});
  158. PQXX_CHECK_EQUAL(
  159. std::size(output), 0u, "Empty read produced nonempty buffer.");
  160. output = b.read(string_buf);
  161. PQXX_CHECK_EQUAL(
  162. std::size(output), 2u, "Got unexpected buf size from blob::read().");
  163. PQXX_CHECK_EQUAL(
  164. output[0], std::byte{'u'}, "Unexpected byte from blob::read().");
  165. PQXX_CHECK_EQUAL(
  166. output[1], std::byte{'v'}, "Unexpected byte from blob::read().");
  167. string_buf.resize(100);
  168. output = b.read(std::span<std::byte>{string_buf.data(), 1});
  169. PQXX_CHECK_EQUAL(
  170. std::size(output), 1u,
  171. "Did blob::read() follow string size instead of span size?");
  172. PQXX_CHECK_EQUAL(
  173. output[0], std::byte{'w'}, "Unexpected byte from blob::read().");
  174. std::vector<std::byte> vec_buf;
  175. vec_buf.resize(2);
  176. auto output2{b.read(vec_buf)};
  177. PQXX_CHECK_EQUAL(
  178. std::size(output2), 2u, "Got unexpected buf size from blob::read().");
  179. PQXX_CHECK_EQUAL(
  180. output2[0], std::byte{'x'}, "Unexpected byte from blob::read().");
  181. PQXX_CHECK_EQUAL(
  182. output2[1], std::byte{'y'}, "Unexpected byte from blob::read().");
  183. vec_buf.resize(100);
  184. output2 = b.read(vec_buf);
  185. PQXX_CHECK_EQUAL(std::size(output2), 1u, "Weird things happened at EOF.");
  186. PQXX_CHECK_EQUAL(output2[0], std::byte{'z'}, "Bad data at EOF.");
  187. #endif // PQXX_HAVE_SPAN
  188. }
  189. void test_blob_reads_vector()
  190. {
  191. char const content[]{"abcd"};
  192. pqxx::connection conn;
  193. pqxx::work tx{conn};
  194. auto id{pqxx::blob::from_buf(
  195. tx, std::basic_string_view<std::byte>{
  196. reinterpret_cast<std::byte const *>(content), std::size(content)})};
  197. std::vector<std::byte> buf;
  198. buf.resize(10);
  199. auto out{pqxx::blob::open_r(tx, id).read(buf)};
  200. PQXX_CHECK_EQUAL(
  201. std::size(out), std::size(content),
  202. "Got wrong length back when reading as vector.");
  203. PQXX_CHECK_EQUAL(
  204. out[0], std::byte{'a'}, "Got bad data when reading as vector.");
  205. }
  206. void test_blob_write_appends_at_insertion_point()
  207. {
  208. pqxx::connection conn;
  209. pqxx::work tx{conn};
  210. auto id{pqxx::blob::create(tx)};
  211. auto b{pqxx::blob::open_rw(tx, id)};
  212. b.write(std::basic_string<std::byte>{std::byte{'z'}});
  213. b.write(std::basic_string<std::byte>{std::byte{'a'}});
  214. std::basic_string<std::byte> buf;
  215. b.read(buf, 5);
  216. PQXX_CHECK_EQUAL(
  217. buf, (std::basic_string<std::byte>{}), "Found data at the end.");
  218. b.seek_abs(0);
  219. b.read(buf, 5);
  220. PQXX_CHECK_EQUAL(
  221. buf, (std::basic_string<std::byte>{std::byte{'z'}, std::byte{'a'}}),
  222. "Consecutive writes did not append correctly.");
  223. b.write(std::basic_string<std::byte>{std::byte{'x'}});
  224. // Blob now contains "zax". That's not we wanted... Rewind and rewrite.
  225. b.seek_abs(1);
  226. b.write(std::basic_string<std::byte>{std::byte{'y'}});
  227. b.seek_abs(0);
  228. b.read(buf, 5);
  229. PQXX_CHECK_EQUAL(
  230. buf,
  231. (std::basic_string<std::byte>{
  232. std::byte{'z'}, std::byte{'y'}, std::byte{'x'}}),
  233. "Rewriting in the middle did not work right.");
  234. }
  235. void test_blob_writes_span()
  236. {
  237. #if defined(PQXX_HAVE_SPAN)
  238. pqxx::connection conn;
  239. pqxx::work tx{conn};
  240. constexpr char content[]{"gfbltk"};
  241. std::basic_string<std::byte> data{
  242. reinterpret_cast<std::byte const *>(content), std::size(content)};
  243. auto id{pqxx::blob::create(tx)};
  244. auto b{pqxx::blob::open_rw(tx, id)};
  245. b.write(std::span<std::byte>{data.data() + 1, 3u});
  246. b.seek_abs(0);
  247. std::vector<std::byte> buf;
  248. buf.resize(4);
  249. auto out{b.read(std::span<std::byte>{buf.data(), 4u})};
  250. PQXX_CHECK_EQUAL(
  251. std::size(out), 3u, "Did not get expected number of bytes back.");
  252. PQXX_CHECK_EQUAL(out[0], std::byte{'f'}, "Data did not come back right.");
  253. PQXX_CHECK_EQUAL(out[2], std::byte{'l'}, "Data started right, ended wrong!");
  254. #endif // PQXX_HAVE_SPAN
  255. }
  256. void test_blob_resize_shortens_to_desired_length()
  257. {
  258. std::basic_string<std::byte> const data{
  259. std::byte{'w'}, std::byte{'o'}, std::byte{'r'}, std::byte{'k'}};
  260. pqxx::connection conn;
  261. pqxx::work tx{conn};
  262. auto id{pqxx::blob::from_buf(tx, data)};
  263. pqxx::blob::open_w(tx, id).resize(2);
  264. std::basic_string<std::byte> buf;
  265. pqxx::blob::to_buf(tx, id, buf, 10);
  266. PQXX_CHECK_EQUAL(
  267. buf, (std::basic_string<std::byte>{std::byte{'w'}, std::byte{'o'}}),
  268. "Truncate did not shorten correctly.");
  269. }
  270. void test_blob_resize_extends_to_desired_length()
  271. {
  272. pqxx::connection conn;
  273. pqxx::work tx{conn};
  274. auto id{
  275. pqxx::blob::from_buf(tx, std::basic_string<std::byte>{std::byte{100}})};
  276. pqxx::blob::open_w(tx, id).resize(3);
  277. std::basic_string<std::byte> buf;
  278. pqxx::blob::to_buf(tx, id, buf, 10);
  279. PQXX_CHECK_EQUAL(
  280. buf,
  281. (std::basic_string<std::byte>{std::byte{100}, std::byte{0}, std::byte{0}}),
  282. "Resize did not zero-extend correctly.");
  283. }
  284. void test_blob_tell_tracks_position()
  285. {
  286. pqxx::connection conn;
  287. pqxx::work tx{conn};
  288. auto id{pqxx::blob::create(tx)};
  289. auto b{pqxx::blob::open_rw(tx, id)};
  290. PQXX_CHECK_EQUAL(
  291. b.tell(), 0, "Empty blob started out in non-zero position.");
  292. b.write(std::basic_string<std::byte>{std::byte{'e'}, std::byte{'f'}});
  293. PQXX_CHECK_EQUAL(
  294. b.tell(), 2, "Empty blob started out in non-zero position.");
  295. b.seek_abs(1);
  296. PQXX_CHECK_EQUAL(b.tell(), 1, "tell() did not track seek.");
  297. }
  298. void test_blob_seek_sets_positions()
  299. {
  300. std::basic_string<std::byte> data{
  301. std::byte{0}, std::byte{1}, std::byte{2}, std::byte{3}, std::byte{4},
  302. std::byte{5}, std::byte{6}, std::byte{7}, std::byte{8}, std::byte{9}};
  303. pqxx::connection conn;
  304. pqxx::work tx{conn};
  305. auto id{pqxx::blob::from_buf(tx, data)};
  306. auto b{pqxx::blob::open_r(tx, id)};
  307. std::basic_string<std::byte> buf;
  308. b.seek_rel(3);
  309. b.read(buf, 1u);
  310. PQXX_CHECK_EQUAL(
  311. buf[0], std::byte{3},
  312. "seek_rel() from beginning did not take us to the right position.");
  313. b.seek_abs(2);
  314. b.read(buf, 1u);
  315. PQXX_CHECK_EQUAL(
  316. buf[0], std::byte{2}, "seek_abs() did not take us to the right position.");
  317. b.seek_end(-2);
  318. b.read(buf, 1u);
  319. PQXX_CHECK_EQUAL(
  320. buf[0], std::byte{8}, "seek_end() did not take us to the right position.");
  321. }
  322. void test_blob_from_buf_interoperates_with_to_buf()
  323. {
  324. std::basic_string<std::byte> const data{std::byte{'h'}, std::byte{'i'}};
  325. std::basic_string<std::byte> buf;
  326. pqxx::connection conn;
  327. pqxx::work tx{conn};
  328. pqxx::blob::to_buf(tx, pqxx::blob::from_buf(tx, data), buf, 10);
  329. PQXX_CHECK_EQUAL(buf, data, "from_buf()/to_buf() roundtrip did not work.");
  330. }
  331. void test_blob_append_from_buf_appends()
  332. {
  333. std::basic_string<std::byte> const data{std::byte{'h'}, std::byte{'o'}};
  334. pqxx::connection conn;
  335. pqxx::work tx{conn};
  336. auto id{pqxx::blob::create(tx)};
  337. pqxx::blob::append_from_buf(tx, data, id);
  338. pqxx::blob::append_from_buf(tx, data, id);
  339. std::basic_string<std::byte> buf;
  340. pqxx::blob::to_buf(tx, id, buf, 10);
  341. PQXX_CHECK_EQUAL(buf, data + data, "append_from_buf() wrote wrong data?");
  342. }
  343. namespace
  344. {
  345. /// Wrap `std::fopen`.
  346. /** This is just here to stop Visual Studio from advertising its own
  347. * alternative.
  348. */
  349. std::unique_ptr<FILE, std::function<int(FILE *)>>
  350. my_fopen(char const *path, char const *mode)
  351. {
  352. #if defined(_MSC_VER)
  353. # pragma warning(push)
  354. # pragma warning(disable : 4996)
  355. #endif
  356. return {std::fopen(path, mode), std::fclose};
  357. #if defined(_MSC_VER)
  358. # pragma warning(pop)
  359. #endif
  360. }
  361. void read_file(
  362. char const path[], std::size_t len, std::basic_string<std::byte> &buf)
  363. {
  364. buf.resize(len);
  365. auto f{my_fopen(path, "rb")};
  366. auto bytes{
  367. std::fread(reinterpret_cast<char *>(buf.data()), 1, len, f.get())};
  368. if (bytes == 0)
  369. throw std::runtime_error{"Error reading test file."};
  370. buf.resize(bytes);
  371. }
  372. void write_file(char const path[], std::basic_string_view<std::byte> data)
  373. {
  374. try
  375. {
  376. auto f{my_fopen(path, "wb")};
  377. if (
  378. std::fwrite(
  379. reinterpret_cast<char const *>(data.data()), 1, std::size(data),
  380. f.get()) < std::size(data))
  381. std::runtime_error{"File write failed."};
  382. }
  383. catch (const std::exception &)
  384. {
  385. std::remove(path);
  386. throw;
  387. }
  388. }
  389. /// Temporary file.
  390. class TempFile
  391. {
  392. public:
  393. /// Create (and later clean up) a file at path containing data.
  394. TempFile(char const path[], std::basic_string_view<std::byte> data) :
  395. m_path(path)
  396. {
  397. write_file(path, data);
  398. }
  399. ~TempFile() { std::remove(m_path.c_str()); }
  400. private:
  401. std::string m_path;
  402. };
  403. } // namespace
  404. void test_blob_from_file_creates_blob_from_file_contents()
  405. {
  406. char const temp_file[] = "blob-test-from_file.tmp";
  407. std::basic_string<std::byte> const data{std::byte{'4'}, std::byte{'2'}};
  408. pqxx::connection conn;
  409. pqxx::work tx{conn};
  410. std::basic_string<std::byte> buf;
  411. pqxx::oid id;
  412. {
  413. TempFile f{temp_file, data};
  414. id = pqxx::blob::from_file(tx, temp_file);
  415. }
  416. pqxx::blob::to_buf(tx, id, buf, 10);
  417. PQXX_CHECK_EQUAL(buf, data, "Wrong data from blob::from_file().");
  418. }
  419. void test_blob_from_file_with_oid_writes_blob()
  420. {
  421. std::basic_string<std::byte> const data{std::byte{'6'}, std::byte{'9'}};
  422. char const temp_file[] = "blob-test-from_file-oid.tmp";
  423. std::basic_string<std::byte> buf;
  424. pqxx::connection conn;
  425. pqxx::work tx{conn};
  426. // Guarantee (more or less) that id is not in use.
  427. auto id{pqxx::blob::create(tx)};
  428. pqxx::blob::remove(tx, id);
  429. {
  430. TempFile f{temp_file, data};
  431. pqxx::blob::from_file(tx, temp_file, id);
  432. }
  433. pqxx::blob::to_buf(tx, id, buf, 10);
  434. PQXX_CHECK_EQUAL(buf, data, "Wrong data from blob::from_file().");
  435. }
  436. void test_blob_append_to_buf_appends()
  437. {
  438. std::basic_string<std::byte> const data{
  439. std::byte{'b'}, std::byte{'l'}, std::byte{'u'}, std::byte{'b'}};
  440. pqxx::connection conn;
  441. pqxx::work tx{conn};
  442. auto id{pqxx::blob::from_buf(tx, data)};
  443. std::basic_string<std::byte> buf;
  444. PQXX_CHECK_EQUAL(
  445. pqxx::blob::append_to_buf(tx, id, 0u, buf, 1u), 1u,
  446. "append_to_buf() returned unexpected value.");
  447. PQXX_CHECK_EQUAL(std::size(buf), 1u, "Appended the wrong number of bytes.");
  448. PQXX_CHECK_EQUAL(
  449. pqxx::blob::append_to_buf(tx, id, 1u, buf, 5u), 3u,
  450. "append_to_buf() returned unexpected value.");
  451. PQXX_CHECK_EQUAL(std::size(buf), 4u, "Appended the wrong number of bytes.");
  452. PQXX_CHECK_EQUAL(
  453. buf, data, "Reading using append_to_buf gave us wrong data.");
  454. }
  455. void test_blob_to_file_writes_file()
  456. {
  457. std::basic_string<std::byte> const data{
  458. std::byte{'C'}, std::byte{'+'}, std::byte{'+'}};
  459. char const temp_file[] = "blob-test-to_file.tmp";
  460. pqxx::connection conn;
  461. pqxx::work tx{conn};
  462. auto id{pqxx::blob::from_buf(tx, data)};
  463. std::basic_string<std::byte> buf;
  464. try
  465. {
  466. pqxx::blob::to_file(tx, id, temp_file);
  467. read_file(temp_file, 10u, buf);
  468. std::remove(temp_file);
  469. }
  470. catch (std::exception const &)
  471. {
  472. std::remove(temp_file);
  473. throw;
  474. }
  475. PQXX_CHECK_EQUAL(buf, data, "Got wrong data from to_file().");
  476. }
  477. void test_blob_close_leaves_blob_unusable()
  478. {
  479. pqxx::connection conn;
  480. pqxx::work tx{conn};
  481. auto id{
  482. pqxx::blob::from_buf(tx, std::basic_string<std::byte>{std::byte{1}})};
  483. auto b{pqxx::blob::open_rw(tx, id)};
  484. b.close();
  485. std::basic_string<std::byte> buf;
  486. PQXX_CHECK_THROWS(
  487. b.read(buf, 1), pqxx::usage_error,
  488. "Reading from closed blob did not fail right.");
  489. }
  490. void test_blob_accepts_std_filesystem_path()
  491. {
  492. #if defined(PQXX_HAVE_PATH) && !defined(_WIN32)
  493. // A bug in gcc 8's ~std::filesystem::path() causes a run-time crash.
  494. # if !defined(__GNUC__) || (__GNUC__ > 8)
  495. char const temp_file[] = "blob-test-filesystem-path.tmp";
  496. std::basic_string<std::byte> const data{std::byte{'4'}, std::byte{'2'}};
  497. pqxx::connection conn;
  498. pqxx::work tx{conn};
  499. std::basic_string<std::byte> buf;
  500. TempFile f{temp_file, data};
  501. std::filesystem::path const path{temp_file};
  502. auto id{pqxx::blob::from_file(tx, path)};
  503. pqxx::blob::to_buf(tx, id, buf, 10);
  504. PQXX_CHECK_EQUAL(buf, data, "Wrong data from blob::from_file().");
  505. # endif
  506. #endif
  507. }
  508. PQXX_REGISTER_TEST(test_blob_is_useless_by_default);
  509. PQXX_REGISTER_TEST(test_blob_create_makes_empty_blob);
  510. PQXX_REGISTER_TEST(test_blob_create_with_oid_requires_oid_be_free);
  511. PQXX_REGISTER_TEST(test_blob_create_with_oid_obeys_oid);
  512. PQXX_REGISTER_TEST(test_blobs_are_transactional);
  513. PQXX_REGISTER_TEST(test_blob_remove_removes_blob);
  514. PQXX_REGISTER_TEST(test_blob_remove_is_not_idempotent);
  515. PQXX_REGISTER_TEST(test_blob_checks_open_mode);
  516. PQXX_REGISTER_TEST(test_blob_supports_move);
  517. PQXX_REGISTER_TEST(test_blob_read_reads_data);
  518. PQXX_REGISTER_TEST(test_blob_reads_vector);
  519. PQXX_REGISTER_TEST(test_blob_read_span);
  520. PQXX_REGISTER_TEST(test_blob_write_appends_at_insertion_point);
  521. PQXX_REGISTER_TEST(test_blob_writes_span);
  522. PQXX_REGISTER_TEST(test_blob_resize_shortens_to_desired_length);
  523. PQXX_REGISTER_TEST(test_blob_resize_extends_to_desired_length);
  524. PQXX_REGISTER_TEST(test_blob_tell_tracks_position);
  525. PQXX_REGISTER_TEST(test_blob_seek_sets_positions);
  526. PQXX_REGISTER_TEST(test_blob_from_buf_interoperates_with_to_buf);
  527. PQXX_REGISTER_TEST(test_blob_append_from_buf_appends);
  528. PQXX_REGISTER_TEST(test_blob_from_file_creates_blob_from_file_contents);
  529. PQXX_REGISTER_TEST(test_blob_from_file_with_oid_writes_blob);
  530. PQXX_REGISTER_TEST(test_blob_append_to_buf_appends);
  531. PQXX_REGISTER_TEST(test_blob_to_file_writes_file);
  532. PQXX_REGISTER_TEST(test_blob_close_leaves_blob_unusable);
  533. PQXX_REGISTER_TEST(test_blob_accepts_std_filesystem_path);
  534. } // namespace