/* 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 , not ." #endif #include #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>()`. */ [[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 auto to(T &obj) const -> typename std::enable_if_t< (not std::is_pointer::value or std::is_same::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 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 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 auto to(T &obj, T const &default_value) const -> typename std::enable_if_t< (not std::is_pointer::value or std::is_same::value), bool> { bool const null{is_null()}; if (null) obj = default_value; else obj = from_string(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 T as(T const &default_value) const { if (is_null()) return default_value; else return from_string(this->view()); } /// Return value as object of given type, or throw exception if null. /** Use as `as>()` or `as()` as * an alternative to `get()`; this is disabled for use with raw pointers * (other than C-strings) because storage for the value can't safely be * allocated here */ template T as() const { if (is_null()) { if constexpr (not nullness::has_null) internal::throw_null_conversion(type_name); else return nullness::null(); } else { return from_string(this->view()); } } /// Return value wrapped in some optional type (empty for nulls). /** Use as `get()` as before to obtain previous behavior, or specify * container type with `get()` */ template class O = std::optional> constexpr O get() const { return as>(); } // 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 &obj) const { bool const null{is_null()}; if (not null) obj = std::string{view()}; return not null; } template<> inline bool field::to( 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 *&obj) const { bool const null{is_null()}; if (not null) obj = c_str(); return not null; } template<> inline bool field::to(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 &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() const { if (is_null()) PQXX_UNLIKELY internal::throw_null_conversion(type_name); return view(); } template<> inline std::string_view field::as(std::string_view const &default_value) const { return is_null() ? default_value : view(); } template<> inline bool field::to(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 &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() const { if (is_null()) PQXX_UNLIKELY internal::throw_null_conversion(type_name); return zview{c_str(), size()}; } template<> inline zview field::as(zview const &default_value) const { return is_null() ? default_value : zview{c_str(), size()}; } template> class field_streambuf : public std::basic_streambuf { 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(const_cast(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> class basic_fieldstream : 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; basic_fieldstream(field const &f) : super{nullptr}, m_buf{f} { super::init(&m_buf); } private: field_streambuf m_buf; }; using fieldstream = basic_fieldstream; /// 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 * "long long" 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 inline std::basic_ostream & operator<<(std::basic_ostream &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 inline T from_string(field const &value) { if (value.is_null()) { if constexpr (nullness::has_null) return nullness::null(); else internal::throw_null_conversion(type_name); } else { return from_string(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(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