123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672 |
- /** Large Objects interface.
- *
- * Allows access to large objects directly, or through I/O streams.
- *
- * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/largeobject instead.
- *
- * Copyright (c) 2000-2019, 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
- #include "pqxx/compiler-public.hxx"
- #include "pqxx/compiler-internal-pre.hxx"
- #include <streambuf>
- #include "pqxx/dbtransaction.hxx"
- namespace pqxx
- {
- /// Identity of a large object
- /** This class encapsulates the identity of a large object. To access the
- * contents of the object, create a largeobjectaccess, a largeobject_streambuf,
- * or an ilostream, an olostream or a lostream around the largeobject.
- *
- * 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)
- largeobject() noexcept =default; //[t48]
- /// Create new large object
- /** @param T Backend transaction in which the object is to be created
- */
- explicit largeobject(dbtransaction &T); //[t48]
- /// 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
- */
- explicit largeobject(oid O) noexcept : m_id{O} {} //[t48]
- /// 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
- */
- largeobject(dbtransaction &T, const std::string &File); //[t53]
- /// 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
- */
- largeobject(const largeobjectaccess &O) noexcept; //[t50]
- /// 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).
- */
- oid id() const noexcept { return m_id; } //[t48]
- /**
- * @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. */
- bool operator==(const largeobject &other) const //[t51]
- { return m_id == other.m_id; }
- /// Compare object identities
- /** @warning Only valid between large objects in the same database. */
- bool operator!=(const largeobject &other) const //[t51]
- { return m_id != other.m_id; }
- /// Compare object identities
- /** @warning Only valid between large objects in the same database. */
- bool operator<=(const largeobject &other) const //[t51]
- { return m_id <= other.m_id; }
- /// Compare object identities
- /** @warning Only valid between large objects in the same database. */
- bool operator>=(const largeobject &other) const //[t51]
- { return m_id >= other.m_id; }
- /// Compare object identities
- /** @warning Only valid between large objects in the same database. */
- bool operator<(const largeobject &other) const //[t51]
- { return m_id < other.m_id; }
- /// Compare object identities
- /** @warning Only valid between large objects in the same database. */
- bool operator>(const largeobject &other) const //[t51]
- { 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, const std::string &File) const; //[t52]
- /// 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; //[t48]
- protected:
- PQXX_PURE static internal::pq::PGconn *raw_connection(
- const dbtransaction &T);
- PQXX_PRIVATE std::string reason(const connection_base &, int err) const;
- private:
- oid m_id = oid_none;
- };
- // TODO: New hierarchy with separate read / write / mixed-mode access
- /// Accessor for large object's contents.
- class PQXX_LIBEXPORT largeobjectaccess : private largeobject
- {
- public:
- using largeobject::size_type;
- using off_type = long;
- using pos_type = size_type;
- /// Open mode: @c in, @c out (can be combined with the "or" operator)
- /** According to the C++ standard, these should be in @c std::ios_base. We
- * take them from @c std::ios instead, which should be safe because it
- * inherits the same definition, to accommodate gcc 2.95 & 2.96.
- */
- using openmode = std::ios::openmode;
- /// Seek direction: @c beg, @c cur, @c end
- /** According to the C++ standard, these should be in @c std::ios_base. We
- * take them from @c std::ios instead, which should be safe because it
- * inherits the same definition, to accommodate gcc 2.95 & 2.96.
- */
- 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
- */
- explicit largeobjectaccess( //[t51]
- dbtransaction &T,
- openmode mode=std::ios::in|std::ios::out);
- /// 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
- */
- largeobjectaccess( //[t52]
- dbtransaction &T,
- oid O,
- openmode mode=std::ios::in|std::ios::out);
- /// 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
- */
- largeobjectaccess( //[t50]
- dbtransaction &T,
- largeobject O,
- openmode mode=std::ios::in|std::ios::out);
- /// 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
- */
- largeobjectaccess( //[t55]
- dbtransaction &T,
- const std::string &File,
- openmode mode=std::ios::in|std::ios::out);
- ~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(const std::string &File) const //[t54]
- { largeobject::to_file(m_trans, File); }
- using largeobject::to_file;
- /**
- * @name High-level access to object contents
- */
- //@{
- /// Write data to large object
- /** If not all bytes could be written, an exception is thrown.
- * @param Buf Data to write
- * @param Len Number of bytes from Buf to write
- */
- void write(const char Buf[], size_type Len); //[t51]
- /// 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(const std::string &Buf) //[t50]
- { write(Buf.c_str(), static_cast<size_type>(Buf.size())); }
- /// 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[], size_type Len); //[t50]
- /// 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); //[t51]
- /// Report current position in large object's data stream
- /** Throws an exception if an error occurs.
- * @return The current position in the large object
- */
- size_type tell() const; //[t50]
- //@}
- /**
- * @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.
- */
- //@{
- /// Seek in large object's data stream
- /** Does not throw exception in case of error; inspect return value and
- * @c 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; //[t50]
- /// Write to large object's data stream
- /** Does not throw exception in case of error; inspect return value and
- * @c 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(const char Buf[], size_type Len) noexcept; //[t50]
- /// Read from large object's data stream
- /** Does not throw exception in case of error; inspect return value and
- * @c 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[], size_type Len) noexcept; //[t50]
- /// Report current position in large object's data stream
- /** Does not throw exception in case of error; inspect return value and
- * @c errno instead.
- * @return Current position in large object, of -1 if an error occurred.
- */
- pos_type ctell() const noexcept; //[t50]
- //@}
- /**
- * @name Error/warning output
- */
- //@{
- /// Issue message to transaction's notice processor
- void process_notice(const std::string &) noexcept; //[t50]
- //@}
- using largeobject::remove;
- using largeobject::operator==;
- using largeobject::operator!=;
- using largeobject::operator<;
- using largeobject::operator<=;
- using largeobject::operator>;
- using largeobject::operator>=;
- 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;
- largeobjectaccess() =delete;
- largeobjectaccess(const largeobjectaccess &) =delete;
- largeobjectaccess operator=(const largeobjectaccess &) =delete;
- };
- /// Streambuf to use large objects in standard I/O streams
- /** 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 provides similar access to
- * large objects, so they can be read and written using the same stream classes.
- *
- * @warning This class may not work properly in compiler environments that don't
- * fully support Standard-compliant streambufs, such as g++ 2.95 or older.
- */
- template<typename CHAR=char, typename TRAITS=std::char_traits<CHAR>>
- class largeobject_streambuf :
- public std::basic_streambuf<CHAR, TRAITS>
- {
- using size_type = long;
- 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;
- largeobject_streambuf( //[t48]
- dbtransaction &T,
- largeobject O,
- openmode mode=std::ios::in|std::ios::out,
- size_type BufSize=512) :
- m_bufsize{BufSize},
- m_obj{T, O, mode},
- m_g{nullptr},
- m_p{nullptr}
- { initialize(mode); }
- largeobject_streambuf( //[t48]
- dbtransaction &T,
- oid O,
- openmode mode=std::ios::in|std::ios::out,
- size_type BufSize=512) :
- m_bufsize{BufSize},
- 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(const std::string &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 AdjustEOF(m_obj.cseek(largeobjectaccess::off_type(offset), dir));
- }
- virtual pos_type seekpos(pos_type pos, openmode) override
- {
- const largeobjectaccess::pos_type newpos = m_obj.cseek(
- largeobjectaccess::off_type(pos),
- std::ios::beg);
- return AdjustEOF(newpos);
- }
- virtual int_type overflow(int_type ch = EoF()) override
- {
- char *const pp = this->pptr();
- if (pp == nullptr) return EoF();
- char *const pb = this->pbase();
- int_type res = 0;
- if (pp > pb) res = int_type(AdjustEOF(m_obj.cwrite(pb, pp-pb)));
- this->setp(m_p, m_p + m_bufsize);
- // Write that one more character, if it's there.
- if (ch != EoF())
- {
- *this->pptr() = char(ch);
- this->pbump(1);
- }
- return res;
- }
- virtual int_type underflow() override
- {
- if (this->gptr() == nullptr) return EoF();
- char *const eb = this->eback();
- const int_type res(static_cast<int_type>(
- AdjustEOF(m_obj.cread(this->eback(), m_bufsize))));
- this->setg(eb, eb, eb + ((res==EoF()) ? 0 : res));
- return ((res == 0) or (res == EoF())) ? EoF() : *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<typename INTYPE>
- static std::streampos AdjustEOF(INTYPE pos)
- { return (pos==-1) ? std::streampos(EoF()) : std::streampos(pos); }
- void initialize(openmode mode)
- {
- if (mode & std::ios::in)
- {
- m_g = new char_type[unsigned(m_bufsize)];
- this->setg(m_g, m_g, m_g);
- }
- if (mode & std::ios::out)
- {
- m_p = new char_type[unsigned(m_bufsize)];
- this->setp(m_p, m_p + m_bufsize);
- }
- }
- const size_type m_bufsize;
- largeobjectaccess m_obj;
- /// Get & put buffers.
- char_type *m_g, *m_p;
- };
- /// Input stream that gets its data from a large object.
- /** Use this class exactly as you would any other istream to read data from a
- * large object. All formatting and streaming operations of @c std::istream are
- * supported. What you'll typically want to use, however, is the ilostream
- * alias (which defines a basic_ilostream for @c char). This is similar to
- * how e.g. @c std::ifstream relates to @c std::basic_ifstream.
- *
- * Currently only works for <tt><char, std::char_traits<char>></tt>.
- */
- template<typename CHAR=char, typename TRAITS=std::char_traits<CHAR>>
- class basic_ilostream :
- public std::basic_istream<CHAR, TRAITS>
- {
- using super = std::basic_istream<CHAR, TRAITS>;
- 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_ilostream
- /**
- * @param T Transaction in which this stream is to exist
- * @param O Large object to access
- * @param BufSize Size of buffer to use internally (optional)
- */
- basic_ilostream( //[t57]
- dbtransaction &T,
- largeobject O,
- largeobject::size_type BufSize=512) :
- super{nullptr},
- m_buf{T, O, std::ios::in, BufSize}
- { super::init(&m_buf); }
- /// Create a basic_ilostream
- /**
- * @param T Transaction in which this stream is to exist
- * @param O Identifier of a large object to access
- * @param BufSize Size of buffer to use internally (optional)
- */
- basic_ilostream( //[t48]
- dbtransaction &T,
- oid O,
- largeobject::size_type BufSize=512) :
- super{nullptr},
- m_buf{T, O, std::ios::in, BufSize}
- { super::init(&m_buf); }
- private:
- largeobject_streambuf<CHAR,TRAITS> m_buf;
- };
- using ilostream = basic_ilostream<char>;
- /// Output stream that writes data back to a large object
- /** Use this class exactly as you would any other ostream to write data to a
- * large object. All formatting and streaming operations of @c std::ostream are
- * supported. What you'll typically want to use, however, is the olostream
- * alias (which defines a basic_olostream for @c char). This is similar to
- * how e.g. @c std::ofstream is related to @c std::basic_ofstream.
- *
- * Currently only works for <tt><char, std::char_traits<char>></tt>.
- */
- template<typename CHAR=char, typename TRAITS=std::char_traits<CHAR>>
- class basic_olostream :
- public std::basic_ostream<CHAR, TRAITS>
- {
- using super = std::basic_ostream<CHAR, TRAITS>;
- 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_olostream
- /**
- * @param T transaction in which this stream is to exist
- * @param O a large object to access
- * @param BufSize size of buffer to use internally (optional)
- */
- basic_olostream( //[t48]
- dbtransaction &T,
- largeobject O,
- largeobject::size_type BufSize=512) :
- super{nullptr},
- m_buf{T, O, std::ios::out, BufSize}
- { super::init(&m_buf); }
- /// Create a basic_olostream
- /**
- * @param T transaction in which this stream is to exist
- * @param O a large object to access
- * @param BufSize size of buffer to use internally (optional)
- */
- basic_olostream( //[t57]
- dbtransaction &T,
- oid O,
- largeobject::size_type BufSize=512) :
- super{nullptr},
- m_buf{T, O, std::ios::out, BufSize}
- { super::init(&m_buf); }
- ~basic_olostream()
- {
- try
- {
- m_buf.pubsync(); m_buf.pubsync();
- }
- catch (const std::exception &e)
- {
- m_buf.process_notice(e.what());
- }
- }
- private:
- largeobject_streambuf<CHAR,TRAITS> m_buf;
- };
- using olostream = basic_olostream<char>;
- /// Stream that reads and writes a large object
- /** Use this class exactly as you would a std::iostream to read data from, or
- * write data to a large object. All formatting and streaming operations of
- * @c std::iostream are supported. What you'll typically want to use, however,
- * is the lostream alias (which defines a basic_lostream for @c char). This
- * is similar to how e.g. @c std::fstream is related to @c std::basic_fstream.
- *
- * Currently only works for <tt><char, std::char_traits<char>></tt>.
- */
- template<typename CHAR=char, typename TRAITS=std::char_traits<CHAR>>
- class basic_lostream :
- public std::basic_iostream<CHAR, TRAITS>
- {
- using super = std::basic_iostream<CHAR, TRAITS>;
- 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 BufSize Size of buffer to use internally (optional)
- */
- basic_lostream( //[t59]
- dbtransaction &T,
- largeobject O,
- largeobject::size_type BufSize=512) :
- super{nullptr},
- m_buf{T, O, std::ios::in | std::ios::out, BufSize}
- { 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 BufSize Size of buffer to use internally (optional)
- */
- basic_lostream( //[t59]
- dbtransaction &T,
- oid O,
- largeobject::size_type BufSize=512) :
- super{nullptr},
- m_buf{T, O, std::ios::in | std::ios::out, BufSize}
- { super::init(&m_buf); }
- ~basic_lostream()
- {
- try
- {
- m_buf.pubsync(); m_buf.pubsync();
- }
- catch (const std::exception &e)
- {
- m_buf.process_notice(e.what());
- }
- }
- private:
- largeobject_streambuf<CHAR,TRAITS> m_buf;
- };
- using lostream = basic_lostream<char>;
- } // namespace pqxx
- #include "pqxx/compiler-internal-post.hxx"
- #endif
|