largeobject.hxx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. /** Large Objects interface.
  2. *
  3. * Allows access to large objects directly, or through I/O streams.
  4. *
  5. * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/largeobject instead.
  6. *
  7. * Copyright (c) 2000-2019, Jeroen T. Vermeulen.
  8. *
  9. * See COPYING for copyright license. If you did not receive a file called
  10. * COPYING with this source code, please notify the distributor of this mistake,
  11. * or contact the author.
  12. */
  13. #ifndef PQXX_H_LARGEOBJECT
  14. #define PQXX_H_LARGEOBJECT
  15. #include "pqxx/compiler-public.hxx"
  16. #include "pqxx/compiler-internal-pre.hxx"
  17. #include <streambuf>
  18. #include "pqxx/dbtransaction.hxx"
  19. namespace pqxx
  20. {
  21. /// Identity of a large object
  22. /** This class encapsulates the identity of a large object. To access the
  23. * contents of the object, create a largeobjectaccess, a largeobject_streambuf,
  24. * or an ilostream, an olostream or a lostream around the largeobject.
  25. *
  26. * A largeobject must be accessed only from within a backend transaction, but
  27. * the object's identity remains valid as long as the object exists.
  28. */
  29. class PQXX_LIBEXPORT largeobject
  30. {
  31. public:
  32. using size_type = large_object_size_type;
  33. /// Refer to a nonexistent large object (similar to what a null pointer does)
  34. largeobject() noexcept =default; //[t48]
  35. /// Create new large object
  36. /** @param T Backend transaction in which the object is to be created
  37. */
  38. explicit largeobject(dbtransaction &T); //[t48]
  39. /// Wrap object with given oid
  40. /** Convert combination of a transaction and object identifier into a
  41. * large object identity. Does not affect the database.
  42. * @param O Object identifier for the given object
  43. */
  44. explicit largeobject(oid O) noexcept : m_id{O} {} //[t48]
  45. /// Import large object from a local file
  46. /** Creates a large object containing the data found in the given file.
  47. * @param T Backend transaction in which the large object is to be created
  48. * @param File A filename on the client program's filesystem
  49. */
  50. largeobject(dbtransaction &T, const std::string &File); //[t53]
  51. /// Take identity of an opened large object
  52. /** Copy identity of already opened large object. Note that this may be done
  53. * as an implicit conversion.
  54. * @param O Already opened large object to copy identity from
  55. */
  56. largeobject(const largeobjectaccess &O) noexcept; //[t50]
  57. /// Object identifier
  58. /** The number returned by this function identifies the large object in the
  59. * database we're connected to (or oid_none is returned if we refer to the
  60. * null object).
  61. */
  62. oid id() const noexcept { return m_id; } //[t48]
  63. /**
  64. * @name Identity comparisons
  65. *
  66. * These operators compare the object identifiers of large objects. This has
  67. * nothing to do with the objects' actual contents; use them only for keeping
  68. * track of containers of references to large objects and such.
  69. */
  70. //@{
  71. /// Compare object identities
  72. /** @warning Only valid between large objects in the same database. */
  73. bool operator==(const largeobject &other) const //[t51]
  74. { return m_id == other.m_id; }
  75. /// Compare object identities
  76. /** @warning Only valid between large objects in the same database. */
  77. bool operator!=(const largeobject &other) const //[t51]
  78. { return m_id != other.m_id; }
  79. /// Compare object identities
  80. /** @warning Only valid between large objects in the same database. */
  81. bool operator<=(const largeobject &other) const //[t51]
  82. { return m_id <= other.m_id; }
  83. /// Compare object identities
  84. /** @warning Only valid between large objects in the same database. */
  85. bool operator>=(const largeobject &other) const //[t51]
  86. { return m_id >= other.m_id; }
  87. /// Compare object identities
  88. /** @warning Only valid between large objects in the same database. */
  89. bool operator<(const largeobject &other) const //[t51]
  90. { return m_id < other.m_id; }
  91. /// Compare object identities
  92. /** @warning Only valid between large objects in the same database. */
  93. bool operator>(const largeobject &other) const //[t51]
  94. { return m_id > other.m_id; }
  95. //@}
  96. /// Export large object's contents to a local file
  97. /** Writes the data stored in the large object to the given file.
  98. * @param T Transaction in which the object is to be accessed
  99. * @param File A filename on the client's filesystem
  100. */
  101. void to_file(dbtransaction &T, const std::string &File) const; //[t52]
  102. /// Delete large object from database
  103. /** Unlike its low-level equivalent cunlink, this will throw an exception if
  104. * deletion fails.
  105. * @param T Transaction in which the object is to be deleted
  106. */
  107. void remove(dbtransaction &T) const; //[t48]
  108. protected:
  109. PQXX_PURE static internal::pq::PGconn *raw_connection(
  110. const dbtransaction &T);
  111. PQXX_PRIVATE std::string reason(const connection_base &, int err) const;
  112. private:
  113. oid m_id = oid_none;
  114. };
  115. // TODO: New hierarchy with separate read / write / mixed-mode access
  116. /// Accessor for large object's contents.
  117. class PQXX_LIBEXPORT largeobjectaccess : private largeobject
  118. {
  119. public:
  120. using largeobject::size_type;
  121. using off_type = long;
  122. using pos_type = size_type;
  123. /// Open mode: @c in, @c out (can be combined with the "or" operator)
  124. /** According to the C++ standard, these should be in @c std::ios_base. We
  125. * take them from @c std::ios instead, which should be safe because it
  126. * inherits the same definition, to accommodate gcc 2.95 & 2.96.
  127. */
  128. using openmode = std::ios::openmode;
  129. /// Seek direction: @c beg, @c cur, @c end
  130. /** According to the C++ standard, these should be in @c std::ios_base. We
  131. * take them from @c std::ios instead, which should be safe because it
  132. * inherits the same definition, to accommodate gcc 2.95 & 2.96.
  133. */
  134. using seekdir = std::ios::seekdir;
  135. /// Create new large object and open it
  136. /**
  137. * @param T Backend transaction in which the object is to be created
  138. * @param mode Access mode, defaults to ios_base::in | ios_base::out
  139. */
  140. explicit largeobjectaccess( //[t51]
  141. dbtransaction &T,
  142. openmode mode=std::ios::in|std::ios::out);
  143. /// Open large object with given oid
  144. /** Convert combination of a transaction and object identifier into a
  145. * large object identity. Does not affect the database.
  146. * @param T Transaction in which the object is to be accessed
  147. * @param O Object identifier for the given object
  148. * @param mode Access mode, defaults to ios_base::in | ios_base::out
  149. */
  150. largeobjectaccess( //[t52]
  151. dbtransaction &T,
  152. oid O,
  153. openmode mode=std::ios::in|std::ios::out);
  154. /// Open given large object
  155. /** Open a large object with the given identity for reading and/or writing
  156. * @param T Transaction in which the object is to be accessed
  157. * @param O Identity for the large object to be accessed
  158. * @param mode Access mode, defaults to ios_base::in | ios_base::out
  159. */
  160. largeobjectaccess( //[t50]
  161. dbtransaction &T,
  162. largeobject O,
  163. openmode mode=std::ios::in|std::ios::out);
  164. /// Import large object from a local file and open it
  165. /** Creates a large object containing the data found in the given file.
  166. * @param T Backend transaction in which the large object is to be created
  167. * @param File A filename on the client program's filesystem
  168. * @param mode Access mode, defaults to ios_base::in | ios_base::out
  169. */
  170. largeobjectaccess( //[t55]
  171. dbtransaction &T,
  172. const std::string &File,
  173. openmode mode=std::ios::in|std::ios::out);
  174. ~largeobjectaccess() noexcept { close(); }
  175. /// Object identifier
  176. /** The number returned by this function uniquely identifies the large object
  177. * in the context of the database we're connected to.
  178. */
  179. using largeobject::id;
  180. /// Export large object's contents to a local file
  181. /** Writes the data stored in the large object to the given file.
  182. * @param File A filename on the client's filesystem
  183. */
  184. void to_file(const std::string &File) const //[t54]
  185. { largeobject::to_file(m_trans, File); }
  186. using largeobject::to_file;
  187. /**
  188. * @name High-level access to object contents
  189. */
  190. //@{
  191. /// Write data to large object
  192. /** If not all bytes could be written, an exception is thrown.
  193. * @param Buf Data to write
  194. * @param Len Number of bytes from Buf to write
  195. */
  196. void write(const char Buf[], size_type Len); //[t51]
  197. /// Write string to large object
  198. /** If not all bytes could be written, an exception is thrown.
  199. * @param Buf Data to write; no terminating zero is written
  200. */
  201. void write(const std::string &Buf) //[t50]
  202. { write(Buf.c_str(), static_cast<size_type>(Buf.size())); }
  203. /// Read data from large object
  204. /** Throws an exception if an error occurs while reading.
  205. * @param Buf Location to store the read data in
  206. * @param Len Number of bytes to try and read
  207. * @return Number of bytes read, which may be less than the number requested
  208. * if the end of the large object is reached
  209. */
  210. size_type read(char Buf[], size_type Len); //[t50]
  211. /// Seek in large object's data stream
  212. /** Throws an exception if an error occurs.
  213. * @return The new position in the large object
  214. */
  215. size_type seek(size_type dest, seekdir dir); //[t51]
  216. /// Report current position in large object's data stream
  217. /** Throws an exception if an error occurs.
  218. * @return The current position in the large object
  219. */
  220. size_type tell() const; //[t50]
  221. //@}
  222. /**
  223. * @name Low-level access to object contents
  224. *
  225. * These functions provide a more "C-like" access interface, returning special
  226. * values instead of throwing exceptions on error. These functions are
  227. * generally best avoided in favour of the high-level access functions, which
  228. * behave more like C++ functions should.
  229. */
  230. //@{
  231. /// Seek in large object's data stream
  232. /** Does not throw exception in case of error; inspect return value and
  233. * @c errno instead.
  234. * @param dest Offset to go to
  235. * @param dir Origin to which dest is relative: ios_base::beg (from beginning
  236. * of the object), ios_base::cur (from current access position), or
  237. * ios_base;:end (from end of object)
  238. * @return New position in large object, or -1 if an error occurred.
  239. */
  240. pos_type cseek(off_type dest, seekdir dir) noexcept; //[t50]
  241. /// Write to large object's data stream
  242. /** Does not throw exception in case of error; inspect return value and
  243. * @c errno instead.
  244. * @param Buf Data to write
  245. * @param Len Number of bytes to write
  246. * @return Number of bytes actually written, or -1 if an error occurred.
  247. */
  248. off_type cwrite(const char Buf[], size_type Len) noexcept; //[t50]
  249. /// Read from large object's data stream
  250. /** Does not throw exception in case of error; inspect return value and
  251. * @c errno instead.
  252. * @param Buf Area where incoming bytes should be stored
  253. * @param Len Number of bytes to read
  254. * @return Number of bytes actually read, or -1 if an error occurred.
  255. */
  256. off_type cread(char Buf[], size_type Len) noexcept; //[t50]
  257. /// Report current position in large object's data stream
  258. /** Does not throw exception in case of error; inspect return value and
  259. * @c errno instead.
  260. * @return Current position in large object, of -1 if an error occurred.
  261. */
  262. pos_type ctell() const noexcept; //[t50]
  263. //@}
  264. /**
  265. * @name Error/warning output
  266. */
  267. //@{
  268. /// Issue message to transaction's notice processor
  269. void process_notice(const std::string &) noexcept; //[t50]
  270. //@}
  271. using largeobject::remove;
  272. using largeobject::operator==;
  273. using largeobject::operator!=;
  274. using largeobject::operator<;
  275. using largeobject::operator<=;
  276. using largeobject::operator>;
  277. using largeobject::operator>=;
  278. private:
  279. PQXX_PRIVATE std::string reason(int err) const;
  280. internal::pq::PGconn *raw_connection() const
  281. { return largeobject::raw_connection(m_trans); }
  282. PQXX_PRIVATE void open(openmode mode);
  283. void close() noexcept;
  284. dbtransaction &m_trans;
  285. int m_fd = -1;
  286. largeobjectaccess() =delete;
  287. largeobjectaccess(const largeobjectaccess &) =delete;
  288. largeobjectaccess operator=(const largeobjectaccess &) =delete;
  289. };
  290. /// Streambuf to use large objects in standard I/O streams
  291. /** The standard streambuf classes provide uniform access to data storage such
  292. * as files or string buffers, so they can be accessed using standard input or
  293. * output streams. This streambuf implementation provides similar access to
  294. * large objects, so they can be read and written using the same stream classes.
  295. *
  296. * @warning This class may not work properly in compiler environments that don't
  297. * fully support Standard-compliant streambufs, such as g++ 2.95 or older.
  298. */
  299. template<typename CHAR=char, typename TRAITS=std::char_traits<CHAR>>
  300. class largeobject_streambuf :
  301. public std::basic_streambuf<CHAR, TRAITS>
  302. {
  303. using size_type = long;
  304. public:
  305. using char_type = CHAR;
  306. using traits_type = TRAITS;
  307. using int_type = typename traits_type::int_type;
  308. using pos_type = typename traits_type::pos_type;
  309. using off_type = typename traits_type::off_type;
  310. using openmode = largeobjectaccess::openmode;
  311. using seekdir = largeobjectaccess::seekdir;
  312. largeobject_streambuf( //[t48]
  313. dbtransaction &T,
  314. largeobject O,
  315. openmode mode=std::ios::in|std::ios::out,
  316. size_type BufSize=512) :
  317. m_bufsize{BufSize},
  318. m_obj{T, O, mode},
  319. m_g{nullptr},
  320. m_p{nullptr}
  321. { initialize(mode); }
  322. largeobject_streambuf( //[t48]
  323. dbtransaction &T,
  324. oid O,
  325. openmode mode=std::ios::in|std::ios::out,
  326. size_type BufSize=512) :
  327. m_bufsize{BufSize},
  328. m_obj{T, O, mode},
  329. m_g{nullptr},
  330. m_p{nullptr}
  331. { initialize(mode); }
  332. virtual ~largeobject_streambuf() noexcept
  333. { delete [] m_p; delete [] m_g; }
  334. /// For use by large object stream classes
  335. void process_notice(const std::string &s) { m_obj.process_notice(s); }
  336. protected:
  337. virtual int sync() override
  338. {
  339. // setg() sets eback, gptr, egptr
  340. this->setg(this->eback(), this->eback(), this->egptr());
  341. return overflow(EoF());
  342. }
  343. virtual pos_type seekoff(
  344. off_type offset,
  345. seekdir dir,
  346. openmode)
  347. override
  348. {
  349. return AdjustEOF(m_obj.cseek(largeobjectaccess::off_type(offset), dir));
  350. }
  351. virtual pos_type seekpos(pos_type pos, openmode) override
  352. {
  353. const largeobjectaccess::pos_type newpos = m_obj.cseek(
  354. largeobjectaccess::off_type(pos),
  355. std::ios::beg);
  356. return AdjustEOF(newpos);
  357. }
  358. virtual int_type overflow(int_type ch = EoF()) override
  359. {
  360. char *const pp = this->pptr();
  361. if (pp == nullptr) return EoF();
  362. char *const pb = this->pbase();
  363. int_type res = 0;
  364. if (pp > pb) res = int_type(AdjustEOF(m_obj.cwrite(pb, pp-pb)));
  365. this->setp(m_p, m_p + m_bufsize);
  366. // Write that one more character, if it's there.
  367. if (ch != EoF())
  368. {
  369. *this->pptr() = char(ch);
  370. this->pbump(1);
  371. }
  372. return res;
  373. }
  374. virtual int_type underflow() override
  375. {
  376. if (this->gptr() == nullptr) return EoF();
  377. char *const eb = this->eback();
  378. const int_type res(static_cast<int_type>(
  379. AdjustEOF(m_obj.cread(this->eback(), m_bufsize))));
  380. this->setg(eb, eb, eb + ((res==EoF()) ? 0 : res));
  381. return ((res == 0) or (res == EoF())) ? EoF() : *eb;
  382. }
  383. private:
  384. /// Shortcut for traits_type::eof().
  385. static int_type EoF() { return traits_type::eof(); }
  386. /// Helper: change error position of -1 to EOF (probably a no-op).
  387. template<typename INTYPE>
  388. static std::streampos AdjustEOF(INTYPE pos)
  389. { return (pos==-1) ? std::streampos(EoF()) : std::streampos(pos); }
  390. void initialize(openmode mode)
  391. {
  392. if (mode & std::ios::in)
  393. {
  394. m_g = new char_type[unsigned(m_bufsize)];
  395. this->setg(m_g, m_g, m_g);
  396. }
  397. if (mode & std::ios::out)
  398. {
  399. m_p = new char_type[unsigned(m_bufsize)];
  400. this->setp(m_p, m_p + m_bufsize);
  401. }
  402. }
  403. const size_type m_bufsize;
  404. largeobjectaccess m_obj;
  405. /// Get & put buffers.
  406. char_type *m_g, *m_p;
  407. };
  408. /// Input stream that gets its data from a large object.
  409. /** Use this class exactly as you would any other istream to read data from a
  410. * large object. All formatting and streaming operations of @c std::istream are
  411. * supported. What you'll typically want to use, however, is the ilostream
  412. * alias (which defines a basic_ilostream for @c char). This is similar to
  413. * how e.g. @c std::ifstream relates to @c std::basic_ifstream.
  414. *
  415. * Currently only works for <tt><char, std::char_traits<char>></tt>.
  416. */
  417. template<typename CHAR=char, typename TRAITS=std::char_traits<CHAR>>
  418. class basic_ilostream :
  419. public std::basic_istream<CHAR, TRAITS>
  420. {
  421. using super = std::basic_istream<CHAR, TRAITS>;
  422. public:
  423. using char_type = CHAR;
  424. using traits_type = TRAITS;
  425. using int_type = typename traits_type::int_type;
  426. using pos_type = typename traits_type::pos_type;
  427. using off_type = typename traits_type::off_type;
  428. /// Create a basic_ilostream
  429. /**
  430. * @param T Transaction in which this stream is to exist
  431. * @param O Large object to access
  432. * @param BufSize Size of buffer to use internally (optional)
  433. */
  434. basic_ilostream( //[t57]
  435. dbtransaction &T,
  436. largeobject O,
  437. largeobject::size_type BufSize=512) :
  438. super{nullptr},
  439. m_buf{T, O, std::ios::in, BufSize}
  440. { super::init(&m_buf); }
  441. /// Create a basic_ilostream
  442. /**
  443. * @param T Transaction in which this stream is to exist
  444. * @param O Identifier of a large object to access
  445. * @param BufSize Size of buffer to use internally (optional)
  446. */
  447. basic_ilostream( //[t48]
  448. dbtransaction &T,
  449. oid O,
  450. largeobject::size_type BufSize=512) :
  451. super{nullptr},
  452. m_buf{T, O, std::ios::in, BufSize}
  453. { super::init(&m_buf); }
  454. private:
  455. largeobject_streambuf<CHAR,TRAITS> m_buf;
  456. };
  457. using ilostream = basic_ilostream<char>;
  458. /// Output stream that writes data back to a large object
  459. /** Use this class exactly as you would any other ostream to write data to a
  460. * large object. All formatting and streaming operations of @c std::ostream are
  461. * supported. What you'll typically want to use, however, is the olostream
  462. * alias (which defines a basic_olostream for @c char). This is similar to
  463. * how e.g. @c std::ofstream is related to @c std::basic_ofstream.
  464. *
  465. * Currently only works for <tt><char, std::char_traits<char>></tt>.
  466. */
  467. template<typename CHAR=char, typename TRAITS=std::char_traits<CHAR>>
  468. class basic_olostream :
  469. public std::basic_ostream<CHAR, TRAITS>
  470. {
  471. using super = std::basic_ostream<CHAR, TRAITS>;
  472. public:
  473. using char_type = CHAR;
  474. using traits_type = TRAITS;
  475. using int_type = typename traits_type::int_type;
  476. using pos_type = typename traits_type::pos_type;
  477. using off_type = typename traits_type::off_type;
  478. /// Create a basic_olostream
  479. /**
  480. * @param T transaction in which this stream is to exist
  481. * @param O a large object to access
  482. * @param BufSize size of buffer to use internally (optional)
  483. */
  484. basic_olostream( //[t48]
  485. dbtransaction &T,
  486. largeobject O,
  487. largeobject::size_type BufSize=512) :
  488. super{nullptr},
  489. m_buf{T, O, std::ios::out, BufSize}
  490. { super::init(&m_buf); }
  491. /// Create a basic_olostream
  492. /**
  493. * @param T transaction in which this stream is to exist
  494. * @param O a large object to access
  495. * @param BufSize size of buffer to use internally (optional)
  496. */
  497. basic_olostream( //[t57]
  498. dbtransaction &T,
  499. oid O,
  500. largeobject::size_type BufSize=512) :
  501. super{nullptr},
  502. m_buf{T, O, std::ios::out, BufSize}
  503. { super::init(&m_buf); }
  504. ~basic_olostream()
  505. {
  506. try
  507. {
  508. m_buf.pubsync(); m_buf.pubsync();
  509. }
  510. catch (const std::exception &e)
  511. {
  512. m_buf.process_notice(e.what());
  513. }
  514. }
  515. private:
  516. largeobject_streambuf<CHAR,TRAITS> m_buf;
  517. };
  518. using olostream = basic_olostream<char>;
  519. /// Stream that reads and writes a large object
  520. /** Use this class exactly as you would a std::iostream to read data from, or
  521. * write data to a large object. All formatting and streaming operations of
  522. * @c std::iostream are supported. What you'll typically want to use, however,
  523. * is the lostream alias (which defines a basic_lostream for @c char). This
  524. * is similar to how e.g. @c std::fstream is related to @c std::basic_fstream.
  525. *
  526. * Currently only works for <tt><char, std::char_traits<char>></tt>.
  527. */
  528. template<typename CHAR=char, typename TRAITS=std::char_traits<CHAR>>
  529. class basic_lostream :
  530. public std::basic_iostream<CHAR, TRAITS>
  531. {
  532. using super = std::basic_iostream<CHAR, TRAITS>;
  533. public:
  534. using char_type = CHAR;
  535. using traits_type = TRAITS;
  536. using int_type = typename traits_type::int_type;
  537. using pos_type = typename traits_type::pos_type;
  538. using off_type = typename traits_type::off_type;
  539. /// Create a basic_lostream
  540. /**
  541. * @param T Transaction in which this stream is to exist
  542. * @param O Large object to access
  543. * @param BufSize Size of buffer to use internally (optional)
  544. */
  545. basic_lostream( //[t59]
  546. dbtransaction &T,
  547. largeobject O,
  548. largeobject::size_type BufSize=512) :
  549. super{nullptr},
  550. m_buf{T, O, std::ios::in | std::ios::out, BufSize}
  551. { super::init(&m_buf); }
  552. /// Create a basic_lostream
  553. /**
  554. * @param T Transaction in which this stream is to exist
  555. * @param O Large object to access
  556. * @param BufSize Size of buffer to use internally (optional)
  557. */
  558. basic_lostream( //[t59]
  559. dbtransaction &T,
  560. oid O,
  561. largeobject::size_type BufSize=512) :
  562. super{nullptr},
  563. m_buf{T, O, std::ios::in | std::ios::out, BufSize}
  564. { super::init(&m_buf); }
  565. ~basic_lostream()
  566. {
  567. try
  568. {
  569. m_buf.pubsync(); m_buf.pubsync();
  570. }
  571. catch (const std::exception &e)
  572. {
  573. m_buf.process_notice(e.what());
  574. }
  575. }
  576. private:
  577. largeobject_streambuf<CHAR,TRAITS> m_buf;
  578. };
  579. using lostream = basic_lostream<char>;
  580. } // namespace pqxx
  581. #include "pqxx/compiler-internal-post.hxx"
  582. #endif