/* Definitions for the pqxx::result class and support classes. * * pqxx::result represents the set of result rows from a database query. * * DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/result 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_RESULT #define PQXX_H_RESULT #if !defined(PQXX_HEADER_PRE) # error "Include libpqxx headers as , not ." #endif #include #include #include #include #include "pqxx/except.hxx" #include "pqxx/types.hxx" #include "pqxx/util.hxx" #include "pqxx/zview.hxx" #include "pqxx/internal/encodings.hxx" namespace pqxx::internal { // TODO: Make noexcept (but breaks ABI). PQXX_LIBEXPORT void clear_result(pq::PGresult const *); } // namespace pqxx::internal namespace pqxx::internal::gate { class result_connection; class result_creation; class result_pipeline; class result_row; class result_sql_cursor; } // namespace pqxx::internal::gate namespace pqxx { /// Result set containing data returned by a query or command. /** This behaves as a container (as defined by the C++ standard library) and * provides random access const iterators to iterate over its rows. You can * also access a row by indexing a `result R` by the row's zero-based * number: * * * for (result::size_type i=0; i < std::size(R); ++i) Process(R[i]); * * * Result sets in libpqxx are lightweight, reference-counted wrapper objects * which are relatively small and cheap to copy. Think of a result object as * a "smart pointer" to an underlying result set. * * @warning The result set that a result object points to is not thread-safe. * If you copy a result object, it still refers to the same underlying result * set. So never copy, destroy, query, or otherwise access a result while * another thread may be copying, destroying, querying, or otherwise accessing * the same result set--even if it is doing so through a different result * object! */ class PQXX_LIBEXPORT result { public: using size_type = result_size_type; using difference_type = result_difference_type; using reference = row; using const_iterator = const_result_iterator; using pointer = const_iterator; using iterator = const_iterator; using const_reverse_iterator = const_reverse_result_iterator; using reverse_iterator = const_reverse_iterator; result() noexcept : m_data{make_data_pointer()}, m_query{}, m_encoding{internal::encoding_group::MONOBYTE} {} result(result const &rhs) noexcept = default; result(result &&rhs) noexcept = default; /// Assign one result to another. /** Copying results is cheap: it copies only smart pointers, but the actual * data stays in the same place. */ result &operator=(result const &rhs) noexcept = default; /// Assign one result to another, invaliding the old one. result &operator=(result &&rhs) noexcept = default; /** * @name Comparisons * * You can compare results for equality. Beware: this is a very strict, * dumb comparison. The smallest difference between two results (such as a * string "Foo" versus a string "foo") will make them unequal. */ //@{ /// Compare two results for equality. [[nodiscard]] bool operator==(result const &) const noexcept; /// Compare two results for inequality. [[nodiscard]] bool operator!=(result const &rhs) const noexcept { return not operator==(rhs); } //@} /// Iterate rows, reading them directly into a tuple of "TYPE...". /** Converts the fields to values of the given respective types. * * Use this only with a ranged "for" loop. The iteration produces * std::tuple which you can "unpack" to a series of `auto` * variables. */ template auto iter() const; [[nodiscard]] const_reverse_iterator rbegin() const; [[nodiscard]] const_reverse_iterator crbegin() const; [[nodiscard]] const_reverse_iterator rend() const; [[nodiscard]] const_reverse_iterator crend() const; [[nodiscard]] const_iterator begin() const noexcept; [[nodiscard]] const_iterator cbegin() const noexcept; [[nodiscard]] inline const_iterator end() const noexcept; [[nodiscard]] inline const_iterator cend() const noexcept; [[nodiscard]] reference front() const noexcept; [[nodiscard]] reference back() const noexcept; [[nodiscard]] PQXX_PURE size_type size() const noexcept; [[nodiscard]] PQXX_PURE bool empty() const noexcept; [[nodiscard]] size_type capacity() const noexcept { return size(); } /// Exchange two `result` values in an exception-safe manner. /** If the swap fails, the two values will be exactly as they were before. * * The swap is not necessarily thread-safe. */ void swap(result &) noexcept; /// Index a row by number. /** This returns a @ref row object. Generally you should not keep the row * around as a variable, but if you do, make sure that your variable is a * `row`, not a `row&`. */ [[nodiscard]] row operator[](size_type i) const noexcept; #if defined(PQXX_HAVE_MULTIDIMENSIONAL_SUBSCRIPT) // TODO: If C++23 will let us, also accept string for the column. [[nodiscard]] field operator[](size_type row_num, row_size_type col_num) const noexcept; #endif /// Index a row by number, but check that the row number is valid. row at(size_type) const; /// Index a field by row number and column number. field at(size_type, row_size_type) const; /// Let go of the result's data. /** Use this if you need to deallocate the result data earlier than you can * destroy the `result` object itself. * * Multiple `result` objects can refer to the same set of underlying data. * The underlying data will be deallocated once all `result` objects that * refer to it are cleared or destroyed. */ void clear() noexcept { m_data.reset(); m_query = nullptr; } /** * @name Column information */ //@{ /// Number of columns in result. [[nodiscard]] PQXX_PURE row_size_type columns() const noexcept; /// Number of given column (throws exception if it doesn't exist). [[nodiscard]] row_size_type column_number(zview name) const; /// Name of column with this number (throws exception if it doesn't exist) [[nodiscard]] char const *column_name(row_size_type number) const &; /// Return column's type, as an OID from the system catalogue. [[nodiscard]] oid column_type(row_size_type col_num) const; /// Return column's type, as an OID from the system catalogue. [[nodiscard]] oid column_type(zview col_name) const { return column_type(column_number(col_name)); } /// What table did this column come from? [[nodiscard]] oid column_table(row_size_type col_num) const; /// What table did this column come from? [[nodiscard]] oid column_table(zview col_name) const { return column_table(column_number(col_name)); } /// What column in its table did this column come from? [[nodiscard]] row_size_type table_column(row_size_type col_num) const; /// What column in its table did this column come from? [[nodiscard]] row_size_type table_column(zview col_name) const { return table_column(column_number(col_name)); } //@} /// Query that produced this result, if available (empty string otherwise) [[nodiscard]] PQXX_PURE std::string const &query() const &noexcept; /// If command was an `INSERT` of 1 row, return oid of the inserted row. /** @return Identifier of inserted row if exactly one row was inserted, or * @ref oid_none otherwise. */ [[nodiscard]] PQXX_PURE oid inserted_oid() const; /// If command was `INSERT`, `UPDATE`, or `DELETE`: number of affected rows. /** @return Number of affected rows if last command was `INSERT`, `UPDATE`, * or `DELETE`; zero for all other commands. */ [[nodiscard]] PQXX_PURE size_type affected_rows() const; // C++20: Concept like std::invocable, but without specifying param types. /// Run `func` on each row, passing the row's fields as parameters. /** Goes through the rows from first to last. You provide a callable `func`. * * For each row in the `result`, `for_each` will call `func`. It converts * the row's fields to the types of `func`'s parameters, and pass them to * `func`. * * (Therefore `func` must have a _single_ signature. It can't be a generic * lambda, or an object of a class with multiple overloaded function call * operators. Otherwise, `for_each` will have no way to detect a parameter * list without ambiguity.) * * If any of your parameter types is `std::string_view`, it refers to the * underlying storage of this `result`. * * If any of your parameter types is a reference type, its argument will * refer to a temporary value which only lives for the duration of that * single invocation to `func`. If the reference is an lvalue reference, it * must be `const`. * * For example, this queries employee names and salaries from the database * and prints how much each would like to earn instead: * ```cxx * tx.exec("SELECT name, salary FROM employee").for_each( * [](std::string_view name, float salary){ * std::cout << name << " would like " << salary * 2 << ".\n"; * }) * ``` * * If `func` throws an exception, processing stops at that point and * propagates the exception. * * @throws usage_error if `func`'s number of parameters does not match the * number of columns in this result. */ template inline void for_each(CALLABLE &&func) const; private: using data_pointer = std::shared_ptr; /// Underlying libpq result set. data_pointer m_data; /// Factory for data_pointer. static data_pointer make_data_pointer(internal::pq::PGresult const *res = nullptr) noexcept { return {res, internal::clear_result}; } friend class pqxx::internal::gate::result_pipeline; PQXX_PURE std::shared_ptr query_ptr() const noexcept { return m_query; } /// Query string. std::shared_ptr m_query; internal::encoding_group m_encoding; static std::string const s_empty_string; friend class pqxx::field; // TODO: noexcept. Breaks ABI. PQXX_PURE char const *get_value(size_type row, row_size_type col) const; // TODO: noexcept. Breaks ABI. PQXX_PURE bool get_is_null(size_type row, row_size_type col) const; PQXX_PURE field_size_type get_length(size_type, row_size_type) const noexcept; friend class pqxx::internal::gate::result_creation; result( internal::pq::PGresult *rhs, std::shared_ptr query, internal::encoding_group enc); PQXX_PRIVATE void check_status(std::string_view desc = ""sv) const; friend class pqxx::internal::gate::result_connection; friend class pqxx::internal::gate::result_row; bool operator!() const noexcept { return m_data.get() == nullptr; } operator bool() const noexcept { return m_data.get() != nullptr; } [[noreturn]] PQXX_PRIVATE void throw_sql_error(std::string const &Err, std::string const &Query) const; PQXX_PRIVATE PQXX_PURE int errorposition() const; PQXX_PRIVATE std::string status_error() const; friend class pqxx::internal::gate::result_sql_cursor; PQXX_PURE char const *cmd_status() const noexcept; }; } // namespace pqxx #endif