ZeroTierOne/ext/libpqxx-7.7.3/include/pqxx/cursor.hxx
Adam Ierymenko b1faebae4a Re-add...
2022-06-23 13:24:33 -04:00

484 lines
17 KiB
C++

/* 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