/** Internal wrapper for SQL cursors.  Supports higher-level cursor classes.
 *
 * DO NOT INCLUDE THIS FILE DIRECTLY.  Other headers include it for you.
 *
 * Copyright (c) 2000-2026, 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_INTERNAL_SQL_CURSOR_HXX
#define PQXX_INTERNAL_SQL_CURSOR_HXX

namespace pqxx::internal
{
/// Cursor with SQL positioning semantics.
/** Thin wrapper around an SQL cursor, with SQL's ideas of positioning.
 *
 * SQL cursors have pre-increment/pre-decrement semantics, with on either end
 * of the result set a special position that does not repesent a row.  This
 * class models SQL cursors for the purpose of implementing more C++-like
 * semantics on top.
 *
 * Positions of actual rows are numbered starting at 1.  Position 0 exists but
 * does not refer to a row.  There is a similar non-row position at the end of
 * the result set.
 *
 * Don't use this at home.  You deserve better.  Use the stateless_cursor
 * instead.
 */
class PQXX_LIBEXPORT sql_cursor final : public cursor_base
{
public:
  sql_cursor(
    transaction_base &t, std::string_view query, std::string_view cname,
    cursor_base::access_policy ap, cursor_base::update_policy up,
    cursor_base::ownership_policy op, bool hold, sl = sl::current());

  sql_cursor(
    transaction_base &t, std::string_view cname,
    cursor_base::ownership_policy op, sl = sl::current());

  sql_cursor(sql_cursor const &) = delete;
  sql_cursor(sql_cursor &&) = delete;
  ~sql_cursor() noexcept;

  sql_cursor &operator=(sql_cursor const &) = delete;
  sql_cursor &operator=(sql_cursor &&) = delete;

  result fetch(difference_type rows, difference_type &displacement, sl);
  PQXX_INLINE_COV result fetch(difference_type rows, sl loc)
  {
    difference_type d = 0;
    return fetch(rows, d, loc);
  }
  difference_type
  move(difference_type rows, difference_type &displacement, sl);
  PQXX_INLINE_COV difference_type move(difference_type rows, sl loc)
  {
    difference_type d = 0;
    return move(rows, d, loc);
  }

  /// Current position, or -1 for unknown
  /**
   * The starting position, just before the first row, counts as position zero.
   *
   * Position may be unknown if (and only if) this cursor was adopted, and has
   * never hit its starting position (position zero).
   */
  PQXX_INLINE_COV [[nodiscard]] difference_type pos() const noexcept
  {
    return m_pos;
  }

  /// End position, or -1 for unknown
  /**
   * Returns the final position, just after the last row in the result set. The
   * starting position, just before the first row, counts as position zero.
   *
   * End position is unknown until it is encountered during use.
   */
  PQXX_INLINE_COV [[nodiscard]] difference_type endpos() const noexcept
  {
    return m_endpos;
  }

  /// Return zero-row result for this cursor.
  PQXX_INLINE_COV [[nodiscard]] result const &empty_result() const noexcept
  {
    return m_empty_result;
  }

  void close(sl loc);

  PQXX_PURE [[nodiscard]] constexpr sl created_loc() const noexcept
  {
    return m_created_loc;
  }

private:
  difference_type adjust(difference_type hoped, difference_type actual);
  static std::string stridestring(difference_type);
  /// Initialize cached empty result.  Call only at beginning or end!
  void init_empty_result(transaction_base &, sl);

  /// Connection in which this cursor lives.
  connection &m_home;

  /// Zero-row result from this cursor (or plain empty one if cursor is
  /// adopted)
  result m_empty_result;

  result m_cached_current_row;

  /// Will this cursor object destroy its SQL cursor when it dies?
  cursor_base::ownership_policy m_ownership;

  /// At starting position (-1), somewhere in the middle (0), or past end (1)
  int m_at_end;

  /// Position, or -1 for unknown
  difference_type m_pos;

  /// End position, or -1 for unknown
  difference_type m_endpos = -1;

  /// The `std::source_location` for where this cursor was created.
  sl m_created_loc;
};


PQXX_LIBEXPORT result_size_type obtain_stateless_cursor_size(sql_cursor &, sl);
PQXX_LIBEXPORT result stateless_cursor_retrieve(
  sql_cursor &, result::difference_type size,
  result::difference_type begin_pos, result::difference_type end_pos, sl);
} // namespace pqxx::internal
#endif
