/* 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-2023, 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.
   */
  //@{

  /// 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 constexpr difference_type all() noexcept
  {
    return (std::numeric_limits<int>::max)() - 1;
  }

  /// 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;
  }

  /// Special value: read backwards from current position back to origin.
  /** @return Minimum value for result::difference_type.
   */
  [[nodiscard]] static constexpr difference_type backward_all() noexcept
  {
    return (std::numeric_limits<int>::min)() + 1;
  }

  //@}

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