/* Large Objects interface. Deprecated; use blob instead. * * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/largeobject instead. * * Copyright (c) 2000-2022, Jeroen T. Vermeulen. * * See COPYING for copyright license. If you did not receive a file called * COPYING with this source code, please notify the distributor of this * mistake, or contact the author. */ #ifndef PQXX_H_LARGEOBJECT #define PQXX_H_LARGEOBJECT #if !defined(PQXX_HEADER_PRE) # error "Include libpqxx headers as , not ." #endif #include #include "pqxx/dbtransaction.hxx" namespace pqxx { /// Identity of a large object. /** @deprecated Use the @ref blob class instead. * * Encapsulates the identity of a large object. * * A largeobject must be accessed only from within a backend transaction, but * the object's identity remains valid as long as the object exists. */ class PQXX_LIBEXPORT largeobject { public: using size_type = large_object_size_type; /// Refer to a nonexistent large object (similar to what a null pointer /// does). [[deprecated("Use blob instead.")]] largeobject() noexcept = default; /// Create new large object. /** @param t Backend transaction in which the object is to be created. */ [[deprecated("Use blob instead.")]] explicit largeobject(dbtransaction &t); /// Wrap object with given oid. /** Convert combination of a transaction and object identifier into a * large object identity. Does not affect the database. * @param o Object identifier for the given object. */ [[deprecated("Use blob instead.")]] explicit largeobject(oid o) noexcept : m_id{o} {} /// Import large object from a local file. /** Creates a large object containing the data found in the given file. * @param t Backend transaction in which the large object is to be created. * @param file A filename on the client program's filesystem. */ [[deprecated("Use blob instead.")]] largeobject( dbtransaction &t, std::string_view file); /// Take identity of an opened large object. /** Copy identity of already opened large object. Note that this may be done * as an implicit conversion. * @param o Already opened large object to copy identity from. */ [[deprecated("Use blob instead.")]] largeobject( largeobjectaccess const &o) noexcept; /// Object identifier. /** The number returned by this function identifies the large object in the * database we're connected to (or oid_none is returned if we refer to the * null object). */ [[nodiscard]] oid id() const noexcept { return m_id; } /** * @name Identity comparisons * * These operators compare the object identifiers of large objects. This has * nothing to do with the objects' actual contents; use them only for keeping * track of containers of references to large objects and such. */ //@{ /// Compare object identities /** @warning Only valid between large objects in the same database. */ [[nodiscard]] bool operator==(largeobject const &other) const { return m_id == other.m_id; } /// Compare object identities /** @warning Only valid between large objects in the same database. */ [[nodiscard]] bool operator!=(largeobject const &other) const { return m_id != other.m_id; } /// Compare object identities /** @warning Only valid between large objects in the same database. */ [[nodiscard]] bool operator<=(largeobject const &other) const { return m_id <= other.m_id; } /// Compare object identities /** @warning Only valid between large objects in the same database. */ [[nodiscard]] bool operator>=(largeobject const &other) const { return m_id >= other.m_id; } /// Compare object identities /** @warning Only valid between large objects in the same database. */ [[nodiscard]] bool operator<(largeobject const &other) const { return m_id < other.m_id; } /// Compare object identities /** @warning Only valid between large objects in the same database. */ [[nodiscard]] bool operator>(largeobject const &other) const { return m_id > other.m_id; } //@} /// Export large object's contents to a local file /** Writes the data stored in the large object to the given file. * @param t Transaction in which the object is to be accessed * @param file A filename on the client's filesystem */ void to_file(dbtransaction &t, std::string_view file) const; /// Delete large object from database /** Unlike its low-level equivalent cunlink, this will throw an exception if * deletion fails. * @param t Transaction in which the object is to be deleted */ void remove(dbtransaction &t) const; protected: PQXX_PURE static internal::pq::PGconn * raw_connection(dbtransaction const &T); PQXX_PRIVATE std::string reason(connection const &, int err) const; private: oid m_id = oid_none; }; /// Accessor for large object's contents. /** @deprecated Use the `blob` class instead. */ class PQXX_LIBEXPORT largeobjectaccess : private largeobject { public: using largeobject::size_type; using off_type = size_type; using pos_type = size_type; /// Open mode: `in`, `out` (can be combined using "bitwise or"). /** According to the C++ standard, these should be in `std::ios_base`. We * take them from derived class `std::ios` instead, which is easier on the * eyes. * * Historical note: taking it from std::ios was originally a workaround for a * problem with gcc 2.95. */ using openmode = std::ios::openmode; /// Default open mode: in, out, binary. static constexpr auto default_mode{ std::ios::in | std::ios::out | std::ios::binary}; /// Seek direction: `beg`, `cur`, `end`. using seekdir = std::ios::seekdir; /// Create new large object and open it. /** * @param t Backend transaction in which the object is to be created. * @param mode Access mode, defaults to ios_base::in | ios_base::out | * ios_base::binary. */ [[deprecated("Use blob instead.")]] explicit largeobjectaccess( dbtransaction &t, openmode mode = default_mode); /// Open large object with given oid. /** Convert combination of a transaction and object identifier into a * large object identity. Does not affect the database. * @param t Transaction in which the object is to be accessed. * @param o Object identifier for the given object. * @param mode Access mode, defaults to ios_base::in | ios_base::out | * ios_base::binary. */ [[deprecated("Use blob instead.")]] largeobjectaccess( dbtransaction &t, oid o, openmode mode = default_mode); /// Open given large object. /** Open a large object with the given identity for reading and/or writing. * @param t Transaction in which the object is to be accessed. * @param o Identity for the large object to be accessed. * @param mode Access mode, defaults to ios_base::in | ios_base::out | * ios_base::binary. */ [[deprecated("Use blob instead.")]] largeobjectaccess( dbtransaction &t, largeobject o, openmode mode = default_mode); /// Import large object from a local file and open it. /** Creates a large object containing the data found in the given file. * @param t Backend transaction in which the large object is to be created. * @param file A filename on the client program's filesystem. * @param mode Access mode, defaults to ios_base::in | ios_base::out. */ [[deprecated("Use blob instead.")]] largeobjectaccess( dbtransaction &t, std::string_view file, openmode mode = default_mode); ~largeobjectaccess() noexcept { close(); } /// Object identifier. /** The number returned by this function uniquely identifies the large object * in the context of the database we're connected to. */ using largeobject::id; /// Export large object's contents to a local file. /** Writes the data stored in the large object to the given file. * @param file A filename on the client's filesystem. */ void to_file(std::string_view file) const { largeobject::to_file(m_trans, file); } using largeobject::to_file; /** * @name High-level access to object contents. */ //@{ /// Write data to large object. /** @warning The size of a write is currently limited to 2GB. * * @param buf Data to write. * @param len Number of bytes from Buf to write. */ void write(char const buf[], std::size_t len); /// Write string to large object. /** If not all bytes could be written, an exception is thrown. * @param buf Data to write; no terminating zero is written. */ void write(std::string_view buf) { write(std::data(buf), std::size(buf)); } /// Read data from large object. /** Throws an exception if an error occurs while reading. * @param buf Location to store the read data in. * @param len Number of bytes to try and read. * @return Number of bytes read, which may be less than the number requested * if the end of the large object is reached. */ size_type read(char buf[], std::size_t len); /// Seek in large object's data stream. /** Throws an exception if an error occurs. * @return The new position in the large object */ size_type seek(size_type dest, seekdir dir); /// Report current position in large object's data stream. /** Throws an exception if an error occurs. * @return The current position in the large object. */ [[nodiscard]] size_type tell() const; //@} /** * @name Low-level access to object contents. * * These functions provide a more "C-like" access interface, returning * special values instead of throwing exceptions on error. These functions * are generally best avoided in favour of the high-level access functions, * which behave more like C++ functions should. * * Due to libpq's underlying API, some operations are limited to "int" * sizes, typically 2 GB, even though a large object can grow much larger. */ //@{ /// Seek in large object's data stream. /** Does not throw exception in case of error; inspect return value and * `errno` instead. * @param dest Offset to go to. * @param dir Origin to which dest is relative: ios_base::beg (from beginning * of the object), ios_base::cur (from current access position), or * ios_base;:end (from end of object). * @return New position in large object, or -1 if an error occurred. */ pos_type cseek(off_type dest, seekdir dir) noexcept; /// Write to large object's data stream. /** Does not throw exception in case of error; inspect return value and * `errno` instead. * @param buf Data to write. * @param len Number of bytes to write. * @return Number of bytes actually written, or -1 if an error occurred. */ off_type cwrite(char const buf[], std::size_t len) noexcept; /// Read from large object's data stream. /** Does not throw exception in case of error; inspect return value and * `errno` instead. * @param buf Area where incoming bytes should be stored. * @param len Number of bytes to read. * @return Number of bytes actually read, or -1 if an error occurred.. */ off_type cread(char buf[], std::size_t len) noexcept; /// Report current position in large object's data stream. /** Does not throw exception in case of error; inspect return value and * `errno` instead. * @return Current position in large object, of -1 if an error occurred. */ [[nodiscard]] pos_type ctell() const noexcept; //@} /** * @name Error/warning output */ //@{ /// Issue message to transaction's notice processor. void process_notice(zview) noexcept; //@} using largeobject::remove; using largeobject::operator==; using largeobject::operator!=; using largeobject::operator<; using largeobject::operator<=; using largeobject::operator>; using largeobject::operator>=; largeobjectaccess() = delete; largeobjectaccess(largeobjectaccess const &) = delete; largeobjectaccess operator=(largeobjectaccess const &) = delete; private: PQXX_PRIVATE std::string reason(int err) const; internal::pq::PGconn *raw_connection() const { return largeobject::raw_connection(m_trans); } PQXX_PRIVATE void open(openmode mode); void close() noexcept; dbtransaction &m_trans; int m_fd = -1; }; /// Streambuf to use large objects in standard I/O streams. /** @deprecated Access large objects directly using the @ref blob class. * * The standard streambuf classes provide uniform access to data storage such * as files or string buffers, so they can be accessed using standard input or * output streams. This streambuf implementation provided similar access to * large objects, so they could be read and written using the same stream * classes. * * This functionality was considered too fragile and complex, so it has been * replaced with a single, much simpler class. */ template> class largeobject_streambuf : public std::basic_streambuf { using size_type = largeobject::size_type; public: using char_type = CHAR; using traits_type = TRAITS; using int_type = typename traits_type::int_type; using pos_type = typename traits_type::pos_type; using off_type = typename traits_type::off_type; using openmode = largeobjectaccess::openmode; using seekdir = largeobjectaccess::seekdir; /// Default open mode: in, out, binary. static constexpr auto default_mode{ std::ios::in | std::ios::out | std::ios::binary}; #include "pqxx/internal/ignore-deprecated-pre.hxx" [[deprecated("Use blob instead.")]] largeobject_streambuf( dbtransaction &t, largeobject o, openmode mode = default_mode, size_type buf_size = 512) : m_bufsize{buf_size}, m_obj{t, o, mode}, m_g{nullptr}, m_p{nullptr} { initialize(mode); } #include "pqxx/internal/ignore-deprecated-post.hxx" [[deprecated("Use blob instead.")]] largeobject_streambuf( dbtransaction &t, oid o, openmode mode = default_mode, size_type buf_size = 512) : m_bufsize{buf_size}, m_obj{t, o, mode}, m_g{nullptr}, m_p{nullptr} { initialize(mode); } virtual ~largeobject_streambuf() noexcept { delete[] m_p; delete[] m_g; } /// For use by large object stream classes. void process_notice(zview const &s) { m_obj.process_notice(s); } protected: virtual int sync() override { // setg() sets eback, gptr, egptr. this->setg(this->eback(), this->eback(), this->egptr()); return overflow(eof()); } virtual pos_type seekoff(off_type offset, seekdir dir, openmode) override { return adjust_eof(m_obj.cseek(largeobjectaccess::off_type(offset), dir)); } virtual pos_type seekpos(pos_type pos, openmode) override { largeobjectaccess::pos_type const newpos{ m_obj.cseek(largeobjectaccess::off_type(pos), std::ios::beg)}; return adjust_eof(newpos); } virtual int_type overflow(int_type ch) override { auto *const pp{this->pptr()}; if (pp == nullptr) return eof(); auto *const pb{this->pbase()}; int_type res{0}; if (pp > pb) { auto const write_sz{pp - pb}; auto const written_sz{ m_obj.cwrite(pb, static_cast(pp - pb))}; if (internal::cmp_less_equal(written_sz, 0)) throw internal_error{ "pqxx::largeobject: write failed " "(is transaction still valid on write or flush?), " "libpq reports error"}; else if (write_sz != written_sz) throw internal_error{ "pqxx::largeobject: write failed " "(is transaction still valid on write or flush?), " + std::to_string(written_sz) + "/" + std::to_string(write_sz) + " bytes written"}; auto const out{adjust_eof(written_sz)}; if constexpr (std::is_arithmetic_v) res = check_cast(out, "largeobject position"sv); else res = int_type(out); } this->setp(m_p, m_p + m_bufsize); // Write that one more character, if it's there. if (ch != eof()) { *this->pptr() = static_cast(ch); this->pbump(1); } return res; } virtual int_type overflow() { return overflow(eof()); } virtual int_type underflow() override { if (this->gptr() == nullptr) return eof(); auto *const eb{this->eback()}; auto const res{adjust_eof( m_obj.cread(this->eback(), static_cast(m_bufsize)))}; this->setg( eb, eb, eb + (res == eof() ? 0 : static_cast(res))); return (res == eof() or res == 0) ? eof() : traits_type::to_int_type(*eb); } private: /// Shortcut for traits_type::eof(). static int_type eof() { return traits_type::eof(); } /// Helper: change error position of -1 to EOF (probably a no-op). template static std::streampos adjust_eof(INTYPE pos) { bool const at_eof{pos == -1}; if constexpr (std::is_arithmetic_v) { return check_cast( (at_eof ? eof() : pos), "large object seek"sv); } else { return std::streampos(at_eof ? eof() : pos); } } void initialize(openmode mode) { if ((mode & std::ios::in) != 0) { m_g = new char_type[unsigned(m_bufsize)]; this->setg(m_g, m_g, m_g); } if ((mode & std::ios::out) != 0) { m_p = new char_type[unsigned(m_bufsize)]; this->setp(m_p, m_p + m_bufsize); } } size_type const m_bufsize; largeobjectaccess m_obj; /// Get & put buffers. char_type *m_g, *m_p; }; /// Input stream that gets its data from a large object. /** @deprecated Access large objects directly using the @ref blob class. * * This class worked like any other istream, but to read data from a large * object. It supported all formatting and streaming operations of * `std::istream`. * * This functionality was considered too fragile and complex, so it has been * replaced with a single, much simpler class. */ template> class basic_ilostream : public std::basic_istream { using super = std::basic_istream; public: using char_type = CHAR; using traits_type = TRAITS; using int_type = typename traits_type::int_type; using pos_type = typename traits_type::pos_type; using off_type = typename traits_type::off_type; #include "pqxx/internal/ignore-deprecated-pre.hxx" /// Create a basic_ilostream. /** * @param t Transaction in which this stream is to exist. * @param o Large object to access. * @param buf_size Size of buffer to use internally (optional). */ [[deprecated("Use blob instead.")]] basic_ilostream( dbtransaction &t, largeobject o, largeobject::size_type buf_size = 512) : super{nullptr}, m_buf{t, o, std::ios::in | std::ios::binary, buf_size} { super::init(&m_buf); } #include "pqxx/internal/ignore-deprecated-post.hxx" /// Create a basic_ilostream. /** * @param t Transaction in which this stream is to exist. * @param o Identifier of a large object to access. * @param buf_size Size of buffer to use internally (optional). */ [[deprecated("Use blob instead.")]] basic_ilostream( dbtransaction &t, oid o, largeobject::size_type buf_size = 512) : super{nullptr}, m_buf{t, o, std::ios::in | std::ios::binary, buf_size} { super::init(&m_buf); } private: largeobject_streambuf m_buf; }; using ilostream = basic_ilostream; /// Output stream that writes data back to a large object. /** @deprecated Access large objects directly using the @ref blob class. * * This worked like any other ostream, but to write data to a large object. * It supported all formatting and streaming operations of `std::ostream`. * * This functionality was considered too fragile and complex, so it has been * replaced with a single, much simpler class. */ template> class basic_olostream : public std::basic_ostream { using super = std::basic_ostream; public: using char_type = CHAR; using traits_type = TRAITS; using int_type = typename traits_type::int_type; using pos_type = typename traits_type::pos_type; using off_type = typename traits_type::off_type; #include "pqxx/internal/ignore-deprecated-pre.hxx" /// Create a basic_olostream. /** * @param t transaction in which this stream is to exist. * @param o a large object to access. * @param buf_size size of buffer to use internally (optional). */ [[deprecated("Use blob instead.")]] basic_olostream( dbtransaction &t, largeobject o, largeobject::size_type buf_size = 512) : super{nullptr}, m_buf{t, o, std::ios::out | std::ios::binary, buf_size} { super::init(&m_buf); } #include "pqxx/internal/ignore-deprecated-post.hxx" /// Create a basic_olostream. /** * @param t transaction in which this stream is to exist. * @param o a large object to access. * @param buf_size size of buffer to use internally (optional). */ [[deprecated("Use blob instead.")]] basic_olostream( dbtransaction &t, oid o, largeobject::size_type buf_size = 512) : super{nullptr}, m_buf{t, o, std::ios::out | std::ios::binary, buf_size} { super::init(&m_buf); } ~basic_olostream() { try { m_buf.pubsync(); m_buf.pubsync(); } catch (std::exception const &e) { m_buf.process_notice(e.what()); } } private: largeobject_streambuf m_buf; }; using olostream = basic_olostream; /// Stream that reads and writes a large object. /** @deprecated Access large objects directly using the @ref blob class. * * This worked like a std::iostream, but to read data from, or write data to, a * large object. It supported all formatting and streaming operations of * `std::iostream`. * * This functionality was considered too fragile and complex, so it has been * replaced with a single, much simpler class. */ template> class basic_lostream : public std::basic_iostream { using super = std::basic_iostream; public: using char_type = CHAR; using traits_type = TRAITS; using int_type = typename traits_type::int_type; using pos_type = typename traits_type::pos_type; using off_type = typename traits_type::off_type; /// Create a basic_lostream. /** * @param t Transaction in which this stream is to exist. * @param o Large object to access. * @param buf_size Size of buffer to use internally (optional). */ [[deprecated("Use blob instead.")]] basic_lostream( dbtransaction &t, largeobject o, largeobject::size_type buf_size = 512) : super{nullptr}, m_buf{ t, o, std::ios::in | std::ios::out | std::ios::binary, buf_size} { super::init(&m_buf); } /// Create a basic_lostream. /** * @param t Transaction in which this stream is to exist. * @param o Large object to access. * @param buf_size Size of buffer to use internally (optional). */ [[deprecated("Use blob instead.")]] basic_lostream( dbtransaction &t, oid o, largeobject::size_type buf_size = 512) : super{nullptr}, m_buf{ t, o, std::ios::in | std::ios::out | std::ios::binary, buf_size} { super::init(&m_buf); } ~basic_lostream() { try { m_buf.pubsync(); m_buf.pubsync(); } catch (std::exception const &e) { m_buf.process_notice(e.what()); } } private: largeobject_streambuf m_buf; }; using lostream = basic_lostream; } // namespace pqxx #endif