You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
483 lines
17 KiB
483 lines
17 KiB
/* Definition of the iterator/container-style cursor classes. |
|
* |
|
* C++-style wrappers for SQL cursors. |
|
* |
|
* DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/cursor 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_CURSOR |
|
#define PQXX_H_CURSOR |
|
|
|
#if !defined(PQXX_HEADER_PRE) |
|
# error "Include libpqxx headers as <pqxx/header>, not <pqxx/header.hxx>." |
|
#endif |
|
|
|
#include <limits> |
|
#include <stdexcept> |
|
|
|
#include "pqxx/result.hxx" |
|
#include "pqxx/transaction_base.hxx" |
|
|
|
|
|
namespace pqxx |
|
{ |
|
/// Common definitions for cursor types |
|
/** In C++ terms, fetches are always done in pre-increment or pre-decrement |
|
* fashion--i.e. the result does not include the row the cursor is on at the |
|
* beginning of the fetch, and the cursor ends up being positioned on the last |
|
* row in the result. |
|
* |
|
* There are singular positions akin to `end()` at both the beginning and the |
|
* end of the cursor's range of movement, although these fit in so naturally |
|
* with the semantics that one rarely notices them. The cursor begins at the |
|
* first of these, but any fetch in the forward direction will move the cursor |
|
* off this position and onto the first row before returning anything. |
|
*/ |
|
class PQXX_LIBEXPORT cursor_base |
|
{ |
|
public: |
|
using size_type = result_size_type; |
|
using difference_type = result_difference_type; |
|
|
|
/// Cursor access-pattern policy |
|
/** Allowing a cursor to move forward only can result in better performance, |
|
* so use this access policy whenever possible. |
|
*/ |
|
enum access_policy |
|
{ |
|
/// Cursor can move forward only |
|
forward_only, |
|
/// Cursor can move back and forth |
|
random_access |
|
}; |
|
|
|
/// Cursor update policy |
|
/** |
|
* @warning Not all PostgreSQL versions support updatable cursors. |
|
*/ |
|
enum update_policy |
|
{ |
|
/// Cursor can be used to read data but not to write |
|
read_only, |
|
/// Cursor can be used to update data as well as read it |
|
update |
|
}; |
|
|
|
/// Cursor destruction policy |
|
/** The normal thing to do is to make a cursor object the owner of the SQL |
|
* cursor it represents. There may be cases, however, where a cursor needs |
|
* to persist beyond the end of the current transaction (and thus also beyond |
|
* the lifetime of the cursor object that created it!), where it can be |
|
* "adopted" into a new cursor object. See the basic_cursor documentation |
|
* for an explanation of cursor adoption. |
|
* |
|
* If a cursor is created with "loose" ownership policy, the object |
|
* representing the underlying SQL cursor will not take the latter with it |
|
* when its own lifetime ends, nor will its originating transaction. |
|
* |
|
* @warning Use this feature with care and moderation. Only one cursor |
|
* object should be responsible for any one underlying SQL cursor at any |
|
* given time. |
|
*/ |
|
enum ownership_policy |
|
{ |
|
/// Destroy SQL cursor when cursor object is closed at end of transaction |
|
owned, |
|
/// Leave SQL cursor in existence after close of object and transaction |
|
loose |
|
}; |
|
|
|
cursor_base() = delete; |
|
cursor_base(cursor_base const &) = delete; |
|
cursor_base &operator=(cursor_base const &) = delete; |
|
|
|
/** |
|
* @name Special movement distances. |
|
*/ |
|
//@{ |
|
|
|
// TODO: Make constexpr inline (but breaks ABI). |
|
/// Special value: read until end. |
|
/** @return Maximum value for result::difference_type, so the cursor will |
|
* attempt to read the largest possible result set. |
|
*/ |
|
[[nodiscard]] static difference_type all() noexcept; |
|
|
|
/// Special value: read one row only. |
|
/** @return Unsurprisingly, 1. |
|
*/ |
|
[[nodiscard]] static constexpr difference_type next() noexcept { return 1; } |
|
|
|
/// Special value: read backwards, one row only. |
|
/** @return Unsurprisingly, -1. |
|
*/ |
|
[[nodiscard]] static constexpr difference_type prior() noexcept |
|
{ |
|
return -1; |
|
} |
|
|
|
// TODO: Make constexpr inline (but breaks ABI). |
|
/// Special value: read backwards from current position back to origin. |
|
/** @return Minimum value for result::difference_type. |
|
*/ |
|
[[nodiscard]] static difference_type backward_all() noexcept; |
|
|
|
//@} |
|
|
|
/// Name of underlying SQL cursor |
|
/** |
|
* @returns Name of SQL cursor, which may differ from original given name. |
|
* @warning Don't use this to access the SQL cursor directly without going |
|
* through the provided wrapper classes! |
|
*/ |
|
[[nodiscard]] constexpr std::string const &name() const noexcept |
|
{ |
|
return m_name; |
|
} |
|
|
|
protected: |
|
cursor_base(connection &, std::string_view Name, bool embellish_name = true); |
|
|
|
std::string const m_name; |
|
}; |
|
} // namespace pqxx |
|
|
|
|
|
#include <pqxx/internal/sql_cursor.hxx> |
|
|
|
|
|
namespace pqxx |
|
{ |
|
/// "Stateless cursor" class: easy API for retrieving parts of result sets |
|
/** This is a front-end for SQL cursors, but with a more C++-like API. |
|
* |
|
* Actually, stateless_cursor feels entirely different from SQL cursors. You |
|
* don't keep track of positions, fetches, and moves; you just say which rows |
|
* you want. See the retrieve() member function. |
|
*/ |
|
template<cursor_base::update_policy up, cursor_base::ownership_policy op> |
|
class stateless_cursor |
|
{ |
|
public: |
|
using size_type = result_size_type; |
|
using difference_type = result_difference_type; |
|
|
|
/// Create cursor. |
|
/** |
|
* @param tx The transaction within which you want to create the cursor. |
|
* @param query The SQL query whose results the cursor should traverse. |
|
* @param cname A hint for the cursor's name. The actual SQL cursor's name |
|
* will be based on this (though not necessarily identical). |
|
* @param hold Create a `WITH HOLD` cursor? Such cursors stay alive after |
|
* the transaction has ended, so you can continue to use it. |
|
*/ |
|
stateless_cursor( |
|
transaction_base &tx, std::string_view query, std::string_view cname, |
|
bool hold) : |
|
m_cur{tx, query, cname, cursor_base::random_access, up, op, hold} |
|
{} |
|
|
|
/// Adopt an existing scrolling SQL cursor. |
|
/** This lets you define a cursor yourself, and then wrap it in a |
|
* libpqxx-managed `stateless_cursor` object. |
|
* |
|
* @param tx The transaction within which you want to manage the cursor. |
|
* @param adopted_cursor Your cursor's SQL name. |
|
*/ |
|
stateless_cursor(transaction_base &tx, std::string_view adopted_cursor) : |
|
m_cur{tx, adopted_cursor, op} |
|
{ |
|
// Put cursor in known position |
|
m_cur.move(cursor_base::backward_all()); |
|
} |
|
|
|
/// Close this cursor. |
|
/** The destructor will do this for you automatically. |
|
* |
|
* Closing a cursor is idempotent. Closing a cursor that's already closed |
|
* does nothing. |
|
*/ |
|
void close() noexcept { m_cur.close(); } |
|
|
|
/// Number of rows in cursor's result set |
|
/** @note This function is not const; it may need to scroll to find the size |
|
* of the result set. |
|
*/ |
|
[[nodiscard]] size_type size() |
|
{ |
|
return internal::obtain_stateless_cursor_size(m_cur); |
|
} |
|
|
|
/// Retrieve rows from begin_pos (inclusive) to end_pos (exclusive) |
|
/** Rows are numbered starting from 0 to size()-1. |
|
* |
|
* @param begin_pos First row to retrieve. May be one row beyond the end of |
|
* the result set, to avoid errors for empty result sets. Otherwise, must be |
|
* a valid row number in the result set. |
|
* @param end_pos Row up to which to fetch. Rows are returned ordered from |
|
* begin_pos to end_pos, i.e. in ascending order if begin_pos < end_pos but |
|
* in descending order if begin_pos > end_pos. The end_pos may be |
|
* arbitrarily inside or outside the result set; only existing rows are |
|
* included in the result. |
|
*/ |
|
result retrieve(difference_type begin_pos, difference_type end_pos) |
|
{ |
|
return internal::stateless_cursor_retrieve( |
|
m_cur, result::difference_type(size()), begin_pos, end_pos); |
|
} |
|
|
|
/// Return this cursor's name. |
|
[[nodiscard]] constexpr std::string const &name() const noexcept |
|
{ |
|
return m_cur.name(); |
|
} |
|
|
|
private: |
|
internal::sql_cursor m_cur; |
|
}; |
|
|
|
|
|
class icursor_iterator; |
|
} // namespace pqxx |
|
|
|
|
|
namespace pqxx::internal::gate |
|
{ |
|
class icursor_iterator_icursorstream; |
|
class icursorstream_icursor_iterator; |
|
} // namespace pqxx::internal::gate |
|
|
|
|
|
namespace pqxx |
|
{ |
|
/// Simple read-only cursor represented as a stream of results |
|
/** SQL cursors can be tricky, especially in C++ since the two languages seem |
|
* to have been designed on different planets. An SQL cursor has two singular |
|
* positions akin to `end()` on either side of the underlying result set. |
|
* |
|
* These cultural differences are hidden from view somewhat by libpqxx, which |
|
* tries to make SQL cursors behave more like familiar C++ entities such as |
|
* iterators, sequences, streams, and containers. |
|
* |
|
* Data is fetched from the cursor as a sequence of result objects. Each of |
|
* these will contain the number of rows defined as the stream's stride, except |
|
* of course the last block of data which may contain fewer rows. |
|
* |
|
* This class can create or adopt cursors that live outside any backend |
|
* transaction, which your backend version may not support. |
|
*/ |
|
class PQXX_LIBEXPORT icursorstream |
|
{ |
|
public: |
|
using size_type = cursor_base::size_type; |
|
using difference_type = cursor_base::difference_type; |
|
|
|
/// Set up a read-only, forward-only cursor. |
|
/** Roughly equivalent to a C++ Standard Library istream, this cursor type |
|
* supports only two operations: reading a block of rows while moving |
|
* forward, and moving forward without reading any data. |
|
* |
|
* @param context Transaction context in which this cursor will be active. |
|
* @param query SQL query whose results this cursor shall iterate. |
|
* @param basename Suggested name for the SQL cursor; the library will append |
|
* a unique code to ensure its uniqueness. |
|
* @param sstride Number of rows to fetch per read operation; must be a |
|
* positive number. |
|
*/ |
|
icursorstream( |
|
transaction_base &context, std::string_view query, |
|
std::string_view basename, difference_type sstride = 1); |
|
|
|
/// Adopt existing SQL cursor. Use with care. |
|
/** Forms a cursor stream around an existing SQL cursor, as returned by e.g. |
|
* a server-side function. The SQL cursor will be cleaned up by the stream's |
|
* destructor as if it had been created by the stream; cleaning it up by hand |
|
* or adopting the same cursor twice is an error. |
|
* |
|
* Passing the name of the cursor as a string is not allowed, both to avoid |
|
* confusion with the other constructor and to discourage unnecessary use of |
|
* adopted cursors. |
|
* |
|
* @warning It is technically possible to adopt a "WITH HOLD" cursor, i.e. a |
|
* cursor that stays alive outside its creating transaction. However, any |
|
* cursor stream (including the underlying SQL cursor, naturally) must be |
|
* destroyed before its transaction context object is destroyed. Therefore |
|
* the only way to use SQL's WITH HOLD feature is to adopt the cursor, but |
|
* defer doing so until after entering the transaction context that will |
|
* eventually destroy it. |
|
* |
|
* @param context Transaction context in which this cursor will be active. |
|
* @param cname Result field containing the name of the SQL cursor to adopt. |
|
* @param sstride Number of rows to fetch per read operation; must be a |
|
* positive number. |
|
* @param op Ownership policy. Determines whether the cursor underlying this |
|
* stream will be destroyed when the stream is closed. |
|
*/ |
|
icursorstream( |
|
transaction_base &context, field const &cname, difference_type sstride = 1, |
|
cursor_base::ownership_policy op = cursor_base::owned); |
|
|
|
/// Return `true` if this stream may still return more data. |
|
constexpr operator bool() const &noexcept { return not m_done; } |
|
|
|
/// Read new value into given result object; same as operator `>>`. |
|
/** The result set may continue any number of rows from zero to the chosen |
|
* stride, inclusive. An empty result will only be returned if there are no |
|
* more rows to retrieve. |
|
* |
|
* @param res Write the retrieved data into this result object. |
|
* @return Reference to this very stream, to facilitate "chained" invocations |
|
* ("C.get(r1).get(r2);") |
|
*/ |
|
icursorstream &get(result &res) |
|
{ |
|
res = fetchblock(); |
|
return *this; |
|
} |
|
/// Read new value into given result object; same as `get(result&)`. |
|
/** The result set may continue any number of rows from zero to the chosen |
|
* stride, inclusive. An empty result will only be returned if there are no |
|
* more rows to retrieve. |
|
* |
|
* @param res Write the retrieved data into this result object. |
|
* @return Reference to this very stream, to facilitate "chained" invocations |
|
* ("C >> r1 >> r2;") |
|
*/ |
|
icursorstream &operator>>(result &res) { return get(res); } |
|
|
|
/// Move given number of rows forward without reading data. |
|
/** Ignores any stride that you may have set. It moves by a given number of |
|
* rows, not a number of strides. |
|
* |
|
* @return Reference to this stream itself, to facilitate "chained" |
|
* invocations. |
|
*/ |
|
icursorstream &ignore(std::streamsize n = 1) &; |
|
|
|
/// Change stride, i.e. the number of rows to fetch per read operation. |
|
/** |
|
* @param stride Must be a positive number. |
|
*/ |
|
void set_stride(difference_type stride) &; |
|
[[nodiscard]] constexpr difference_type stride() const noexcept |
|
{ |
|
return m_stride; |
|
} |
|
|
|
private: |
|
result fetchblock(); |
|
|
|
friend class internal::gate::icursorstream_icursor_iterator; |
|
size_type forward(size_type n = 1); |
|
void insert_iterator(icursor_iterator *) noexcept; |
|
void remove_iterator(icursor_iterator *) const noexcept; |
|
|
|
void service_iterators(difference_type); |
|
|
|
internal::sql_cursor m_cur; |
|
|
|
difference_type m_stride; |
|
difference_type m_realpos, m_reqpos; |
|
|
|
mutable icursor_iterator *m_iterators; |
|
|
|
bool m_done; |
|
}; |
|
|
|
|
|
/// Approximate istream_iterator for icursorstream. |
|
/** Intended as an implementation of an input_iterator (as defined by the C++ |
|
* Standard Library), this class supports only two basic operations: reading |
|
* the current element, and moving forward. In addition to the minimal |
|
* guarantees for istream_iterators, this class supports multiple successive |
|
* reads of the same position (the current result set is cached in the |
|
* iterator) even after copying and even after new data have been read from the |
|
* stream. This appears to be a requirement for input_iterators. Comparisons |
|
* are also supported in the general case. |
|
* |
|
* The iterator does not care about its own position, however. Moving an |
|
* iterator forward moves the underlying stream forward and reads the data from |
|
* the new stream position, regardless of the iterator's old position in the |
|
* stream. |
|
* |
|
* The stream's stride defines the granularity for all iterator movement or |
|
* access operations, i.e. "ici += 1" advances the stream by one stride's worth |
|
* of rows, and "*ici++" reads one stride's worth of rows from the stream. |
|
* |
|
* @warning Do not read from the underlying stream or its cursor, move its read |
|
* position, or change its stride, between the time the first icursor_iterator |
|
* on it is created and the time its last icursor_iterator is destroyed. |
|
* |
|
* @warning Manipulating these iterators within the context of a single cursor |
|
* stream is <em>not thread-safe</em>. Creating a new iterator, copying one, |
|
* or destroying one affects the stream as a whole. |
|
*/ |
|
class PQXX_LIBEXPORT icursor_iterator |
|
{ |
|
public: |
|
using iterator_category = std::input_iterator_tag; |
|
using value_type = result; |
|
using pointer = result const *; |
|
using reference = result const &; |
|
using istream_type = icursorstream; |
|
using size_type = istream_type::size_type; |
|
using difference_type = istream_type::difference_type; |
|
|
|
icursor_iterator() noexcept; |
|
explicit icursor_iterator(istream_type &) noexcept; |
|
icursor_iterator(icursor_iterator const &) noexcept; |
|
~icursor_iterator() noexcept; |
|
|
|
result const &operator*() const |
|
{ |
|
refresh(); |
|
return m_here; |
|
} |
|
result const *operator->() const |
|
{ |
|
refresh(); |
|
return &m_here; |
|
} |
|
icursor_iterator &operator++(); |
|
icursor_iterator operator++(int); |
|
icursor_iterator &operator+=(difference_type); |
|
icursor_iterator &operator=(icursor_iterator const &) noexcept; |
|
|
|
[[nodiscard]] bool operator==(icursor_iterator const &rhs) const; |
|
[[nodiscard]] bool operator!=(icursor_iterator const &rhs) const noexcept |
|
{ |
|
return not operator==(rhs); |
|
} |
|
[[nodiscard]] bool operator<(icursor_iterator const &rhs) const; |
|
[[nodiscard]] bool operator>(icursor_iterator const &rhs) const |
|
{ |
|
return rhs < *this; |
|
} |
|
[[nodiscard]] bool operator<=(icursor_iterator const &rhs) const |
|
{ |
|
return not(*this > rhs); |
|
} |
|
[[nodiscard]] bool operator>=(icursor_iterator const &rhs) const |
|
{ |
|
return not(*this < rhs); |
|
} |
|
|
|
private: |
|
void refresh() const; |
|
|
|
friend class internal::gate::icursor_iterator_icursorstream; |
|
difference_type pos() const noexcept { return m_pos; } |
|
void fill(result const &); |
|
|
|
icursorstream *m_stream{nullptr}; |
|
result m_here; |
|
difference_type m_pos; |
|
icursor_iterator *m_prev{nullptr}, *m_next{nullptr}; |
|
}; |
|
} // namespace pqxx |
|
#endif
|
|
|