123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542 |
- /* Definitions for the pqxx::field class.
- *
- * pqxx::field refers to a field in a query result.
- *
- * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/field 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_FIELD
- #define PQXX_H_FIELD
- #if !defined(PQXX_HEADER_PRE)
- # error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>."
- #endif
- #include <optional>
- #include "pqxx/array.hxx"
- #include "pqxx/composite.hxx"
- #include "pqxx/result.hxx"
- #include "pqxx/strconv.hxx"
- #include "pqxx/types.hxx"
- namespace pqxx
- {
- /// Reference to a field in a result set.
- /** A field represents one entry in a row. It represents an actual value
- * in the result set, and can be converted to various types.
- */
- class PQXX_LIBEXPORT field
- {
- public:
- using size_type = field_size_type;
- /// Constructor. Do not call this yourself; libpqxx will do it for you.
- /** Create field as reference to a field in a result set.
- * @param r Row that this field is part of.
- * @param c Column number of this field.
- */
- [[deprecated(
- "Do not construct fields yourself. Get them from the row.")]] field(row const &r, row_size_type c) noexcept;
- /// Constructor. Do not call this yourself; libpqxx will do it for you.
- [[deprecated(
- "Do not construct fields yourself. Get them from the "
- "row.")]] field() noexcept = default;
- /**
- * @name Comparison
- */
- //@{
- // TODO: noexcept. Breaks ABI.
- /// Byte-by-byte comparison of two fields (all nulls are considered equal)
- /** @warning null handling is still open to discussion and change!
- *
- * Handling of null values differs from that in SQL where a comparison
- * involving a null value yields null, so nulls are never considered equal
- * to one another or even to themselves.
- *
- * Null handling also probably differs from the closest equivalent in C++,
- * which is the NaN (Not-a-Number) value, a singularity comparable to
- * SQL's null. This is because the builtin == operator demands that a == a.
- *
- * The usefulness of this operator is questionable. No interpretation
- * whatsoever is imposed on the data; 0 and 0.0 are considered different,
- * as are null vs. the empty string, or even different (but possibly
- * equivalent and equally valid) encodings of the same Unicode character
- * etc.
- */
- [[nodiscard]] PQXX_PURE bool operator==(field const &) const;
- /// Byte-by-byte comparison (all nulls are considered equal)
- /** @warning See operator==() for important information about this operator
- */
- [[nodiscard]] PQXX_PURE bool operator!=(field const &rhs) const noexcept
- {
- return not operator==(rhs);
- }
- //@}
- /**
- * @name Column information
- */
- //@{
- /// Column name.
- [[nodiscard]] PQXX_PURE char const *name() const &;
- /// Column type.
- [[nodiscard]] oid PQXX_PURE type() const;
- /// What table did this column come from?
- [[nodiscard]] PQXX_PURE oid table() const;
- /// Return row number. The first row is row 0, the second is row 1, etc.
- PQXX_PURE constexpr row_size_type num() const noexcept { return col(); }
- /// What column number in its originating table did this column come from?
- [[nodiscard]] PQXX_PURE row_size_type table_column() const;
- //@}
- /**
- * @name Content access
- */
- //@{
- /// Read as `string_view`, or an empty one if null.
- /** The result only remains usable while the data for the underlying
- * @ref result exists. Once all `result` objects referring to that data have
- * been destroyed, the `string_view` will no longer point to valid memory.
- */
- [[nodiscard]] PQXX_PURE std::string_view view() const &
- {
- return std::string_view(c_str(), size());
- }
- /// Read as plain C string.
- /** Since the field's data is stored internally in the form of a
- * zero-terminated C string, this is the fastest way to read it. Use the
- * to() or as() functions to convert the string to other types such as
- * `int`, or to C++ strings.
- *
- * Do not use this for BYTEA values, or other binary values. To read those,
- * convert the value to your desired type using `to()` or `as()`. For
- * example: `f.as<std::basic_string<std::byte>>()`.
- */
- [[nodiscard]] PQXX_PURE char const *c_str() const &;
- /// Is this field's value null?
- [[nodiscard]] PQXX_PURE bool is_null() const noexcept;
- /// Return number of bytes taken up by the field's value.
- [[nodiscard]] PQXX_PURE size_type size() const noexcept;
- /// Read value into obj; or if null, leave obj untouched and return `false`.
- /** This can be used with optional types (except pointers other than C-style
- * strings).
- */
- template<typename T>
- auto to(T &obj) const -> typename std::enable_if_t<
- (not std::is_pointer<T>::value or std::is_same<T, char const *>::value),
- bool>
- {
- if (is_null())
- {
- return false;
- }
- else
- {
- auto const bytes{c_str()};
- from_string(bytes, obj);
- return true;
- }
- }
- /// Read field as a composite value, write its components into `fields`.
- /** @warning This is still experimental. It may change or be replaced.
- *
- * Returns whether the field was null. If it was, it will not touch the
- * values in `fields`.
- */
- template<typename... T> bool composite_to(T &...fields) const
- {
- if (is_null())
- {
- return false;
- }
- else
- {
- parse_composite(m_home.m_encoding, view(), fields...);
- return true;
- }
- }
- /// Read value into obj; or leave obj untouched and return `false` if null.
- template<typename T> bool operator>>(T &obj) const { return to(obj); }
- /// Read value into obj; or if null, use default value and return `false`.
- /** This can be used with `std::optional`, as well as with standard smart
- * pointer types, but not with raw pointers. If the conversion from a
- * PostgreSQL string representation allocates a pointer (e.g. using `new`),
- * then the object's later deallocation should be baked in as well, right
- * from the point where the object is created. So if you want a pointer, use
- * a smart pointer, not a raw pointer.
- *
- * There is one exception, of course: C-style strings. Those are just
- * pointers to the field's internal text data.
- */
- template<typename T>
- auto to(T &obj, T const &default_value) const -> typename std::enable_if_t<
- (not std::is_pointer<T>::value or std::is_same<T, char const *>::value),
- bool>
- {
- bool const null{is_null()};
- if (null)
- obj = default_value;
- else
- obj = from_string<T>(this->view());
- return not null;
- }
- /// Return value as object of given type, or default value if null.
- /** Note that unless the function is instantiated with an explicit template
- * argument, the Default value's type also determines the result type.
- */
- template<typename T> T as(T const &default_value) const
- {
- if (is_null())
- return default_value;
- else
- return from_string<T>(this->view());
- }
- /// Return value as object of given type, or throw exception if null.
- /** Use as `as<std::optional<int>>()` or `as<my_untemplated_optional_t>()` as
- * an alternative to `get<int>()`; this is disabled for use with raw pointers
- * (other than C-strings) because storage for the value can't safely be
- * allocated here
- */
- template<typename T> T as() const
- {
- if (is_null())
- {
- if constexpr (not nullness<T>::has_null)
- internal::throw_null_conversion(type_name<T>);
- else
- return nullness<T>::null();
- }
- else
- {
- return from_string<T>(this->view());
- }
- }
- /// Return value wrapped in some optional type (empty for nulls).
- /** Use as `get<int>()` as before to obtain previous behavior, or specify
- * container type with `get<int, std::optional>()`
- */
- template<typename T, template<typename> class O = std::optional>
- constexpr O<T> get() const
- {
- return as<O<T>>();
- }
- // TODO: constexpr noexcept, once array_parser constructor gets those.
- /// Parse the field as an SQL array.
- /** Call the parser to retrieve values (and structure) from the array.
- *
- * Make sure the @ref result object stays alive until parsing is finished. If
- * you keep the @ref row of `field` object alive, it will keep the @ref
- * result object alive as well.
- */
- array_parser as_array() const &
- {
- return array_parser{c_str(), m_home.m_encoding};
- }
- //@}
- protected:
- constexpr result const &home() const noexcept { return m_home; }
- constexpr result::size_type idx() const noexcept { return m_row; }
- constexpr row_size_type col() const noexcept { return m_col; }
- // TODO: Create gates.
- friend class pqxx::result;
- friend class pqxx::row;
- field(
- result const &r, result_size_type row_num, row_size_type col_num) noexcept
- :
- m_col{col_num}, m_home{r}, m_row{row_num}
- {}
- /**
- * You'd expect this to be unsigned, but due to the way reverse iterators
- * are related to regular iterators, it must be allowed to underflow to -1.
- */
- row_size_type m_col;
- private:
- result m_home;
- result::size_type m_row;
- };
- template<> inline bool field::to<std::string>(std::string &obj) const
- {
- bool const null{is_null()};
- if (not null)
- obj = std::string{view()};
- return not null;
- }
- template<>
- inline bool field::to<std::string>(
- std::string &obj, std::string const &default_value) const
- {
- bool const null{is_null()};
- if (null)
- obj = default_value;
- else
- obj = std::string{view()};
- return not null;
- }
- /// Specialization: `to(char const *&)`.
- /** The buffer has the same lifetime as the data in this result (i.e. of this
- * result object, or the last remaining one copied from it etc.), so take care
- * not to use it after the last result object referring to this query result is
- * destroyed.
- */
- template<> inline bool field::to<char const *>(char const *&obj) const
- {
- bool const null{is_null()};
- if (not null)
- obj = c_str();
- return not null;
- }
- template<> inline bool field::to<std::string_view>(std::string_view &obj) const
- {
- bool const null{is_null()};
- if (not null)
- obj = view();
- return not null;
- }
- template<>
- inline bool field::to<std::string_view>(
- std::string_view &obj, std::string_view const &default_value) const
- {
- bool const null{is_null()};
- if (null)
- obj = default_value;
- else
- obj = view();
- return not null;
- }
- template<> inline std::string_view field::as<std::string_view>() const
- {
- if (is_null())
- PQXX_UNLIKELY
- internal::throw_null_conversion(type_name<std::string_view>);
- return view();
- }
- template<>
- inline std::string_view
- field::as<std::string_view>(std::string_view const &default_value) const
- {
- return is_null() ? default_value : view();
- }
- template<> inline bool field::to<zview>(zview &obj) const
- {
- bool const null{is_null()};
- if (not null)
- obj = zview{c_str(), size()};
- return not null;
- }
- template<>
- inline bool field::to<zview>(zview &obj, zview const &default_value) const
- {
- bool const null{is_null()};
- if (null)
- obj = default_value;
- else
- obj = zview{c_str(), size()};
- return not null;
- }
- template<> inline zview field::as<zview>() const
- {
- if (is_null())
- PQXX_UNLIKELY
- internal::throw_null_conversion(type_name<zview>);
- return zview{c_str(), size()};
- }
- template<> inline zview field::as<zview>(zview const &default_value) const
- {
- return is_null() ? default_value : zview{c_str(), size()};
- }
- template<typename CHAR = char, typename TRAITS = std::char_traits<CHAR>>
- class field_streambuf : public std::basic_streambuf<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;
- using openmode = std::ios::openmode;
- using seekdir = std::ios::seekdir;
- explicit field_streambuf(field const &f) : m_field{f} { initialize(); }
- protected:
- virtual int sync() override { return traits_type::eof(); }
- virtual pos_type seekoff(off_type, seekdir, openmode) override
- {
- return traits_type::eof();
- }
- virtual pos_type seekpos(pos_type, openmode) override
- {
- return traits_type::eof();
- }
- virtual int_type overflow(int_type) override { return traits_type::eof(); }
- virtual int_type underflow() override { return traits_type::eof(); }
- private:
- field const &m_field;
- int_type initialize()
- {
- auto g{static_cast<char_type *>(const_cast<char *>(m_field.c_str()))};
- this->setg(g, g, g + std::size(m_field));
- return int_type(std::size(m_field));
- }
- };
- /// Input stream that gets its data from a result field
- /** Use this class exactly as you would any other istream to read data from a
- * field. All formatting and streaming operations of `std::istream` are
- * supported. What you'll typically want to use, however, is the fieldstream
- * alias (which defines a @ref basic_fieldstream for `char`). This is similar
- * to how e.g. `std::ifstream` relates to `std::basic_ifstream`.
- *
- * This class has only been tested for the char type (and its default traits).
- */
- template<typename CHAR = char, typename TRAITS = std::char_traits<CHAR>>
- class basic_fieldstream : 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;
- basic_fieldstream(field const &f) : super{nullptr}, m_buf{f}
- {
- super::init(&m_buf);
- }
- private:
- field_streambuf<CHAR, TRAITS> m_buf;
- };
- using fieldstream = basic_fieldstream<char>;
- /// Write a result field to any type of stream
- /** This can be convenient when writing a field to an output stream. More
- * importantly, it lets you write a field to e.g. a `stringstream` which you
- * can then use to read, format and convert the field in ways that to() does
- * not support.
- *
- * Example: parse a field into a variable of the nonstandard
- * "<tt>long long</tt>" type.
- *
- * ```cxx
- * extern result R;
- * long long L;
- * stringstream S;
- *
- * // Write field's string into S
- * S << R[0][0];
- *
- * // Parse contents of S into L
- * S >> L;
- * ```
- */
- template<typename CHAR>
- inline std::basic_ostream<CHAR> &
- operator<<(std::basic_ostream<CHAR> &s, field const &value)
- {
- s.write(value.c_str(), std::streamsize(std::size(value)));
- return s;
- }
- /// Convert a field's value to type `T`.
- /** Unlike the "regular" `from_string`, this knows how to deal with null
- * values.
- */
- template<typename T> inline T from_string(field const &value)
- {
- if (value.is_null())
- {
- if constexpr (nullness<T>::has_null)
- return nullness<T>::null();
- else
- internal::throw_null_conversion(type_name<T>);
- }
- else
- {
- return from_string<T>(value.view());
- }
- }
- /// Convert a field's value to `nullptr_t`.
- /** Yes, you read that right. This conversion does nothing useful. It always
- * returns `nullptr`.
- *
- * Except... what if the field is not null? In that case, this throws
- * @ref conversion_error.
- */
- template<>
- inline std::nullptr_t from_string<std::nullptr_t>(field const &value)
- {
- if (not value.is_null())
- throw conversion_error{
- "Extracting non-null field into nullptr_t variable."};
- return nullptr;
- }
- /// Convert a field to a string.
- template<> PQXX_LIBEXPORT std::string to_string(field const &value);
- } // namespace pqxx
- #endif
|