/*****************************************************************************

Copyright (c) 1996, 2024, Oracle and/or its affiliates.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License, version 2.0, as published by the
Free Software Foundation.

This program is designed to work with certain software (including
but not limited to OpenSSL) that is licensed under separate terms,
as designated in a particular file or component or in included license
documentation.  The authors of MySQL hereby grant you an additional
permission to link the program and your derivative works with the
separately licensed software that they have either included with
the program or referenced in the documentation.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0,
for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA

*****************************************************************************/

/** @file include/btr0pcur.h
 The index tree persistent cursor

 Created 2/23/1996 Heikki Tuuri
 *******************************************************/

#ifndef btr0pcur_h
#define btr0pcur_h

#include "btr0btr.h"
#include "btr0cur.h"
#include "btr0types.h"
#include "buf0block_hint.h"
#include "data0data.h"
#include "dict0dict.h"
#include "mtr0mtr.h"
#include "page0cur.h"
#include "univ.i"
#ifndef UNIV_HOTBACKUP
#include "gis0rtree.h"
#endif /* UNIV_HOTBACKUP */

/** Relative positions for a stored cursor position */
enum btr_pcur_pos_t {
  BTR_PCUR_UNSET = 0,
  BTR_PCUR_ON = 1,
  BTR_PCUR_BEFORE = 2,
  BTR_PCUR_AFTER = 3,
  /* Note that if the tree is not empty, btr_pcur::store_position does
  not use the following, but only uses the above three alternatives,
  where the position is stored relative to a specific record: this makes
  implementation of a scroll cursor easier */
  BTR_PCUR_BEFORE_FIRST_IN_TREE = 4, /* in an empty tree */
  BTR_PCUR_AFTER_LAST_IN_TREE = 5    /* in an empty tree */
};

/** Position state of persistent B-tree cursor. */
enum pcur_pos_t {

  /** The persistent cursor is not positioned. */
  BTR_PCUR_NOT_POSITIONED = 0,

  /** The persistent cursor was previously positioned.
  TODO: currently, the state can be BTR_PCUR_IS_POSITIONED,
  though it really should be BTR_PCUR_WAS_POSITIONED,
  because we have no obligation to commit the cursor with
  mtr; similarly latch_mode may be out of date. This can
  lead to problems if btr_pcur is not used the right way;
  all current code should be ok. */
  BTR_PCUR_WAS_POSITIONED,

  /** The persistent cursor is positioned by optimistic get to the same
  record as it was positioned at. Not used for rel_pos == BTR_PCUR_ON.
  It may need adjustment depending on previous/current search direction
  and rel_pos. */
  BTR_PCUR_IS_POSITIONED_OPTIMISTIC,

  /** The persistent cursor is positioned by index search.
  Or optimistic get for rel_pos == BTR_PCUR_ON. */
  BTR_PCUR_IS_POSITIONED
};

/* Import tablespace context for persistent B-tree cursor. */
struct import_ctx_t {
  /* true if cursor fails to move to the next page during import. */
  bool is_error{false};
};

/* The persistent B-tree cursor structure. This is used mainly for SQL
selects, updates, and deletes. */

struct btr_pcur_t {
  /** Sets the old_rec_buf field to nullptr.
  @param[in]  read_level  read level where the cursor would be positioned or
  re-positioned. */
  void init(size_t read_level = 0);

  /** @return the index of this persistent cursor */
  dict_index_t *index() { return (m_btr_cur.index); }

  /** Positions a cursor at a randomly chosen position within a B-tree.
  @param[in]        index                   Index to position on.
  @param[in]        latch_mode  BTR_SEARCH_LEAF, ...
  @param[in,out]        mtr                   Mini-transaction.
  @param[in]        location      Location from where called.
  @return true if the index is available and we have put the cursor, false
          if the index is unavailable */
  bool set_random_position(dict_index_t *index, ulint latch_mode, mtr_t *mtr,
                           ut::Location location);

  /** Opens a persistent cursor at either end of an index.
  @param[in]        from_left   True if open to the low end, false
                              if to the high end.
  @param[in]        index                   Index
  @param[in]        latch_mode  Latch mode
  @param[in]        init_pcur     Whether to initialize pcur.
  @param[in]        level                   Level to search for (0=leaf).
  @param[in,out]        mtr                   Mini-transaction */
  void open_at_side(bool from_left, dict_index_t *index, ulint latch_mode,
                    bool init_pcur, ulint level, mtr_t *mtr);

  /** Opens a persistent cursor at first leaf page (low end). It will not call
  init().
  @param[in]        index                   Index
  @param[in]        latch_mode  Latch mode
  @param[in,out]        mtr                   Mini-transaction */
  void begin_leaf(dict_index_t *index, ulint latch_mode, mtr_t *mtr) {
    open_at_side(true, index, latch_mode, false, 0, mtr);
  }

  /** Opens an persistent cursor to an index tree without initializing
  the cursor.
  @param[in]        index             Index.
  @param[in]        tuple             Tuple on which search done.
  @param[in]        mode              PAGE_CUR_L, ...;
                              NOTE that if the search is made using a unique
                              prefix of a record, mode should be
                              PAGE_CUR_LE, not PAGE_CUR_GE, as the latter
                              may end up on the previous page of the
                              record!
  @param[in]        latch_mode  BTR_SEARCH_LEAF, ...;
                              NOTE that if has_search_latch != 0 then
                              we maybe do not acquire a latch on the cursor
                              page, but assume that the caller uses his
                              btr search latch to protect the record!
  @param[in]        has_search_latch    latch mode the caller
                              currently has on search system: RW_S_LATCH, or 0
  @param[in]        mtr         Mini-transaction
  @param[in]        location      Location where called */
  void open_no_init(dict_index_t *index, const dtuple_t *tuple,
                    page_cur_mode_t mode, ulint latch_mode,
                    ulint has_search_latch, mtr_t *mtr, ut::Location location);

  /** If mode is PAGE_CUR_G or PAGE_CUR_GE, opens a persistent cursor
  on the first user record satisfying the search condition, in the case
  PAGE_CUR_L or PAGE_CUR_LE, on the last user record. If no such user
  record exists, then in the first case sets the cursor after last in
  tree, and in the latter case before first in tree. The latching mode
  must be BTR_SEARCH_LEAF or BTR_MODIFY_LEAF.
  @param[in]        index                   Index
  @param[in]        tuple                 Tuple on which search done.
  @param[in]        mode                    PAGE_CUR_L, ...
  @param[in]        latch_mode  BTR_SEARCH_LEAF or BTR_MODIFY_LEAF
  @param[in]        mtr               Mini-transaction.
  @param[in]        location            Location in file from where called.*/
  void open_on_user_rec(dict_index_t *index, const dtuple_t *tuple,
                        page_cur_mode_t mode, ulint latch_mode, mtr_t *mtr,
                        ut::Location location);

  /** Allows setting the persistent cursor manually.
  @param[in]      cursor      Page cursor where positioned.
  @param[in]        mode                    PAGE_CUR_L, ...
  @param[in]        latch_mode  BTR_SEARCH_LEAF or BTR_MODIFY_LEAF */
  void open_on_user_rec(const page_cur_t &cursor, page_cur_mode_t mode,
                        ulint latch_mode);

  /** Initializes and opens a persistent cursor to an index tree
   It should be closed with btr_pcur::close.
   @param[in]     index                 Index.
   @param[in]     level                 Level in the btree.
   @param[in]     tuple                 Tuple on which search done.
   @param[in]     mode                  PAGE_CUR_L, ...; NOTE that if the search
   is made using a unique prefix of a record, mode should be PAGE_CUR_LE, not
   PAGE_CUR_GE, as the latter may end up on the previous page from the record!
   @param[in]     latch_mode        BTR_SEARCH_LEAF, ...
   @param[in]     mtr                     Mini-transaction.
   @param[in]     location                  Location where called. */
  void open(dict_index_t *index, ulint level, const dtuple_t *tuple,
            page_cur_mode_t mode, ulint latch_mode, mtr_t *mtr,
            ut::Location location);

  /** Restores the stored position of a persistent cursor bufferfixing
  the page and obtaining the specified latches. If the cursor position
  was saved when the
  (1) cursor was positioned on a user record: this function restores
  the position to the last record LESS OR EQUAL to the stored record;
  (2) cursor was positioned on a page infimum record: restores the
  position to the last record LESS than the user record which was the
  successor of the page infimum;
  (3) cursor was positioned on the page supremum: restores to the first
  record GREATER than the user record which was the predecessor of the
  supremum.
  (4) cursor was positioned before the first or after the last in an
  empty tree: restores to before first or after the last in the tree.
  @param[in]        latch_mode  BTR_SEARCH_LEAF, ...
  @param[in,out]  mtr                 Mini-transaction
  @param[in]        location            Location where called.
  @return true if the cursor position was stored when it was on a user
          record and it can be restored on a user record whose ordering
          fields are identical to the ones of the original user record */
  bool restore_position(ulint latch_mode, mtr_t *mtr, ut::Location location);

  /** Frees the possible memory heap of a persistent cursor and
  sets the latch mode of the persistent cursor to BTR_NO_LATCHES.
  WARNING: this function does not release the latch on the page where the
  cursor is currently positioned. The latch is acquired by the
  "move to next/previous" family of functions. Since recursive shared
  locks are not allowed, you must take care (if using the cursor in
  S-mode) to manually release the latch by either calling
  btr_leaf_page_release(pcur.get_block(), pcur.latch_mode, mtr)
  or by committing the mini-transaction right after btr_pcur::close().
  A subsequent attempt to crawl the same page in the same mtr would
  cause an assertion failure. */
  void close();

  /** Free old_rec_buf. */
  void free_rec_buf() {
    ut::free(m_old_rec_buf);
    m_old_rec_buf = nullptr;
  }

#ifndef UNIV_HOTBACKUP

  /** Gets the rel_pos field for a cursor whose position has been stored.
  @return BTR_PCUR_ON, ... */
  ulint get_rel_pos() const;

#endif /* !UNIV_HOTBACKUP */

  /** @return the btree cursor (const version). */
  const btr_cur_t *get_btr_cur() const;

  /** @return the btree cursor (non const version). */
  btr_cur_t *get_btr_cur();

  /** @return the btree page cursor (non const version). */
  page_cur_t *get_page_cur();

  /** @return the btree cursor (const version). */
  const page_cur_t *get_page_cur() const;

  /** Returns the page of a persistent pcur (non const version).
  @return pointer to the page */
  page_t *get_page();

  /** Returns the page of a persistent pcur (const version).
  @return pointer to the page */
  const page_t *get_page() const;

  /** Returns the current buffer block (non const version).
  @return pointer to the block */
  buf_block_t *get_block();

  /** Returns the current buffer block (const version).
  @return pointer to the block */
  const buf_block_t *get_block() const;

  /** Returns the current record (non const version).
  @return pointer to the record */
  rec_t *get_rec();

  /** Returns the current record (const version).
  @return pointer to the record */
  const rec_t *get_rec() const;

#ifndef UNIV_HOTBACKUP
  /** Gets the up_match value for a pcur after a search.
  @return number of matched fields at the cursor or to the right if
  search mode was PAGE_CUR_GE, otherwise undefined */
  ulint get_up_match() const;

  /** Gets the low_match value for a pcur after a search.
  @return number of matched fields at the cursor or to the right if
  search mode was PAGE_CUR_LE, otherwise undefined */
  ulint get_low_match() const;

  /** Checks if the persistent cursor is after the last user record
  on a page.
  @return true if after last on page. */
  bool is_after_last_on_page() const;

  /** Checks if the persistent cursor is before the first user record
  on a page.
  @return true if before first on page. */
  bool is_before_first_on_page() const;

  /** Checks if the persistent cursor is on a user record.
  @return true if on user record. */
  bool is_on_user_rec() const;

  /** Checks if the persistent cursor is before the first user record
  in the index tree.
  @param[in,out]        mtr                   Mini-transaction.
  @return true if is before first in tree. */
  bool is_before_first_in_tree(mtr_t *mtr) const;

  /** Checks if the persistent cursor is after the last user record in
  the index tree.
  @param[in,out]        mtr                   Mini-transaction.
  @return is after last in tree. */
  bool is_after_last_in_tree(mtr_t *mtr) const;

  /** Moves the persistent cursor to the next record on the same page. */
  void move_to_next_on_page();

  /** Moves the persistent cursor to the prev record on the same page. */
  void move_to_prev_on_page();

  /** Moves the persistent cursor to the last record on the same page.
  @param[in,out]        mtr                   Mini-transaction. */
  void move_to_last_on_page(mtr_t *mtr);

  /** Moves the persistent cursor to the next user record in the tree.
  If no user records are left, the cursor ends up 'after last in tree'.
  @param[in,out]        mtr                   Mini-transaction.
  @return DB_SUCCESS or DB_END_OF_INDEX. */
  dberr_t move_to_next_user_rec(mtr_t *mtr);

  /** Moves the persistent cursor to the next record in the tree. If no
  records are left, the cursor stays 'after last in tree'.
  Note: Function may release the page latch.
  @param[in,out]        mtr                   Mini-transaction.
  @return true if the cursor was not after last in tree */
  bool move_to_next(mtr_t *mtr);

  /** Moves the persistent cursor to the previous record in the tree.
  If no records are left, the cursor stays 'before first in tree'.
  Note: Function may release the page latch.
  @param[in,out]        mtr                   Mini-transaction.
  @return true if the cursor was not before first in tree */
  bool move_to_prev(mtr_t *mtr);

  /** Moves the persistent cursor to the first record on the next page.
  Releases the latch on the current page, and bufferunfixes it.
  Note that there must not be modifications on the current page, as
  then the x-latch can be released only in mtr_commit.
  @param[in,out] mtr          Mini-transaction. */
  void move_to_next_page(mtr_t *mtr);

  /** Commits the mtr and sets the pcur latch mode to BTR_NO_LATCHES,
  that is, the cursor becomes detached.
  Function btr_pcur::store_position should be used before calling this,
  if restoration of cursor is wanted later.
  @param[in,out]        mtr                   Mini-transaction. */
  void commit_specify_mtr(mtr_t *mtr);

  /** Moves the persistent cursor to the infimum record on the same page. */
  void move_before_first_on_page();
#endif /* !UNIV_HOTBACKUP */

  /** The position of the cursor is stored by taking an initial segment
  of the record the cursor is positioned on, before, or after, and
  copying it to the cursor data structure, or just setting a flag if
  the cursor id before the first in an EMPTY tree, or after the last
  in an EMPTY tree. NOTE that the page where the cursor is positioned
  must not be empty if the index tree is not totally empty!
  @param[in,out]        mtr                   Mini-transaction. */
  void store_position(mtr_t *mtr);

  /** @return true if the cursor is positioned. */
  bool is_positioned() const {
    return (m_old_stored && (m_pos_state == BTR_PCUR_IS_POSITIONED ||
                             m_pos_state == BTR_PCUR_WAS_POSITIONED));
  }

  /** @return true if the cursor is for a clustered index. */
  bool is_clustered() const { return (m_btr_cur.index->is_clustered()); }

  /** Resets a persistent cursor object, freeing "::old_rec_buf" if it is
  allocated and resetting the other members to their initial values. */
  void reset();

  /** Copies the stored position of a pcur to another pcur.
  @param[in,out]        dst                   Which will receive the position
  info.
  @param[in]        src               From which the info is copied */
  static void copy_stored_position(btr_pcur_t *dst, const btr_pcur_t *src);

  /** Allocates memory for a persistent cursor object and initializes
  the cursor.
  @return own: persistent cursor */
  static btr_pcur_t *create_for_mysql() {
    auto pcur = ut::new_withkey<btr_pcur_t>(UT_NEW_THIS_FILE_PSI_KEY);

    pcur->m_btr_cur.index = nullptr;

    pcur->init();

    return (pcur);
  }

  /** Frees the memory for a persistent cursor object and the cursor itself.
  @param[in,out]  pcur      Cursor to free. */
  static void free_for_mysql(btr_pcur_t *&pcur) {
    pcur->free_rec_buf();

    ut::delete_(pcur);

    pcur = nullptr;
  }

  /** Set the cursor access type: Normal or Scan.
  @param[in]  fetch_mode      One of Page_fetch::NORMAL or Page_fetch::SCAN.
  @return the old fetch mode. */
  Page_fetch set_fetch_type(Page_fetch fetch_mode) {
    ut_ad(fetch_mode == Page_fetch::NORMAL || fetch_mode == Page_fetch::SCAN);

    auto old_fetch_mode = m_btr_cur.m_fetch_mode;

    m_btr_cur.m_fetch_mode = fetch_mode;

    return (old_fetch_mode);
  }

 private:
  /** Moves the persistent cursor backward if it is on the first record
  of the page. Commits mtr. Note that to prevent a possible deadlock, the
  operation first stores the position of the cursor, commits mtr, acquires
  the necessary latches and restores the cursor position again before
  returning. The alphabetical position of the cursor is guaranteed to
  be sensible on return, but it may happen that the cursor is not
  positioned on the last record of any page, because the structure
  of the tree may have changed during the time when the cursor had
  no latches.
  @param[in,out]        mtr                   Mini-tranaction. */
  void move_backward_from_page(mtr_t *mtr);

 public:
  /** a B-tree cursor */
  btr_cur_t m_btr_cur;

  /** see TODO note below!
  BTR_SEARCH_LEAF, BTR_MODIFY_LEAF, BTR_MODIFY_TREE or BTR_NO_LATCHES,
  depending on the latching state of the page and tree where the cursor
  is positioned; BTR_NO_LATCHES means that the cursor is not currently
  positioned:
  we say then that the cursor is detached; it can be restored to
  attached if the old position was stored in old_rec */
  ulint m_latch_mode{0};

  /** true if old_rec is stored */
  bool m_old_stored{false};

  /** if cursor position is stored, contains an initial segment of the
  latest record cursor was positioned either on, before or after */
  rec_t *m_old_rec{nullptr};

  /** number of fields in old_rec */
  ulint m_old_n_fields{0};

  /** BTR_PCUR_ON, BTR_PCUR_BEFORE, or BTR_PCUR_AFTER, depending on
  whether cursor was on, before, or after the old_rec record */
  btr_pcur_pos_t m_rel_pos{BTR_PCUR_UNSET};

  /** buffer block when the position was stored */
  buf::Block_hint m_block_when_stored;

  /** the modify clock value of the buffer block when the cursor position
  was stored */
  uint64_t m_modify_clock{0};

  /** position() and restore_position() state. */
  pcur_pos_t m_pos_state{BTR_PCUR_NOT_POSITIONED};

  /** PAGE_CUR_G, ... */
  page_cur_mode_t m_search_mode{PAGE_CUR_UNSUPP};

  /** the transaction, if we know it; otherwise this field is not defined;
  can ONLY BE USED in error prints in fatal assertion failures! */
  trx_t *m_trx_if_known{nullptr};

  /* NOTE that the following fields may possess dynamically allocated
  memory which should be freed if not needed anymore! */

  /** nullptr, or a dynamically allocated buffer for old_rec */
  byte *m_old_rec_buf{nullptr};

  /** old_rec_buf size if old_rec_buf is not nullptr */
  size_t m_buf_size{0};

  /** Read level where the cursor would be positioned or re-positioned. */
  ulint m_read_level{0};

  /* NOTE that the following field is initialized only during import
  tablespace, otherwise undefined */
  import_ctx_t *import_ctx{nullptr};
};

inline void btr_pcur_t::init(size_t read_level) {
  set_fetch_type(Page_fetch::NORMAL);

  m_old_stored = false;
  m_old_rec_buf = nullptr;
  m_old_rec = nullptr;
  m_btr_cur.rtr_info = nullptr;
  m_read_level = read_level;
  import_ctx = nullptr;
  m_block_when_stored.clear();
}

inline void btr_pcur_t::open(dict_index_t *index, ulint level,
                             const dtuple_t *tuple, page_cur_mode_t mode,
                             ulint latch_mode, mtr_t *mtr,
                             ut::Location location) {
  init();

  m_search_mode = mode;
  m_latch_mode = BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode);

  /* Search with the tree cursor */

  auto cur = get_btr_cur();

  ut_ad(!dict_index_is_spatial(index));

  if (index->table->is_intrinsic()) {
    ut_ad((latch_mode & BTR_MODIFY_LEAF) || (latch_mode & BTR_SEARCH_LEAF) ||
          (latch_mode & BTR_MODIFY_TREE));

    btr_cur_search_to_nth_level_with_no_latch(
        index, level, tuple, mode, cur, location.filename, location.line, mtr,
        (((latch_mode & BTR_MODIFY_LEAF) || (latch_mode & BTR_MODIFY_TREE))
             ? true
             : false));
  } else {
    btr_cur_search_to_nth_level(index, level, tuple, mode, latch_mode, cur, 0,
                                location.filename, location.line, mtr);
  }

  m_pos_state = BTR_PCUR_IS_POSITIONED;

  m_trx_if_known = nullptr;
}

inline void btr_pcur_t::open_at_side(bool from_left, dict_index_t *index,
                                     ulint latch_mode, bool init_pcur,
                                     ulint level, mtr_t *mtr) {
  m_latch_mode = BTR_LATCH_MODE_WITHOUT_FLAGS(latch_mode);

  m_search_mode = from_left ? PAGE_CUR_G : PAGE_CUR_L;

  if (init_pcur) {
    init();
  }

  if (index->table->is_intrinsic()) {
    btr_cur_open_at_index_side_with_no_latch(from_left, index, get_btr_cur(),
                                             level, UT_LOCATION_HERE, mtr);
  } else {
    btr_cur_open_at_index_side(from_left, index, latch_mode, get_btr_cur(),
                               level, UT_LOCATION_HERE, mtr);
  }

  m_pos_state = BTR_PCUR_IS_POSITIONED;

  m_old_stored = false;

  m_trx_if_known = nullptr;
}

inline bool btr_pcur_t::set_random_position(dict_index_t *index,
                                            ulint latch_mode, mtr_t *mtr,
                                            ut::Location location) {
  m_latch_mode = latch_mode;
  m_search_mode = PAGE_CUR_G;

  init();

  auto positioned = btr_cur_open_at_rnd_pos(
      index, latch_mode, get_btr_cur(), location.filename, location.line, mtr);

  m_old_stored = false;

  m_trx_if_known = nullptr;

  m_pos_state = BTR_PCUR_IS_POSITIONED;

  return (positioned);
}

inline void btr_pcur_t::open_no_init(dict_index_t *index, const dtuple_t *tuple,
                                     page_cur_mode_t mode, ulint latch_mode,
                                     ulint has_search_latch, mtr_t *mtr,
                                     ut::Location location) {
  m_latch_mode = BTR_LATCH_MODE_WITHOUT_INTENTION(latch_mode);

  m_search_mode = mode;

  /* Search with the tree cursor */

  auto cur = get_btr_cur();

  if (index->table->is_intrinsic()) {
    ut_ad((latch_mode & BTR_MODIFY_LEAF) || (latch_mode & BTR_SEARCH_LEAF));

    btr_cur_search_to_nth_level_with_no_latch(
        index, m_read_level, tuple, mode, cur, location.filename, location.line,
        mtr, ((latch_mode & BTR_MODIFY_LEAF) ? true : false));
  } else {
    btr_cur_search_to_nth_level(index, m_read_level, tuple, mode, latch_mode,
                                cur, has_search_latch, location.filename,
                                location.line, mtr);
  }

  m_pos_state = BTR_PCUR_IS_POSITIONED;

  m_old_stored = false;

  m_trx_if_known = nullptr;
}

inline const btr_cur_t *btr_pcur_t::get_btr_cur() const { return (&m_btr_cur); }

inline btr_cur_t *btr_pcur_t::get_btr_cur() {
  return (const_cast<btr_cur_t *>(&m_btr_cur));
}

#ifdef UNIV_DEBUG
inline page_cur_t *btr_pcur_t::get_page_cur() {
  return (btr_cur_get_page_cur(get_btr_cur()));
}

inline const page_cur_t *btr_pcur_t::get_page_cur() const {
  return (btr_cur_get_page_cur(get_btr_cur()));
}

inline page_t *btr_pcur_t::get_page() {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);

  return (btr_cur_get_page(get_btr_cur()));
}

inline const page_t *btr_pcur_t::get_page() const {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);

  return (btr_cur_get_page(const_cast<btr_pcur_t *>(this)->get_btr_cur()));
}

inline buf_block_t *btr_pcur_t::get_block() {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);

  return (btr_cur_get_block(get_btr_cur()));
}

inline const buf_block_t *btr_pcur_t::get_block() const {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);

  return (btr_cur_get_block(get_btr_cur()));
}

inline rec_t *btr_pcur_t::get_rec() {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  return (btr_cur_get_rec(get_btr_cur()));
}

inline const rec_t *btr_pcur_t::get_rec() const {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  return (btr_cur_get_rec(get_btr_cur()));
}
#else

inline page_cur_t *btr_pcur_t::get_page_cur() { return (&m_btr_cur.page_cur); }

inline const page_cur_t *btr_pcur_t::get_page_cur() const {
  return (&m_btr_cur.page_cur);
}

inline page_t *btr_pcur_t::get_page() {
  return (m_btr_cur.page_cur.block->frame);
}

inline const page_t *btr_pcur_t::get_page() const {
  return (m_btr_cur.page_cur.block->frame);
}

inline buf_block_t *btr_pcur_t::get_block() {
  return (m_btr_cur.page_cur.block);
}

inline const buf_block_t *btr_pcur_t::get_block() const {
  return (m_btr_cur.page_cur.block);
}

inline rec_t *btr_pcur_t::get_rec() { return (m_btr_cur.page_cur.rec); }

inline const rec_t *btr_pcur_t::get_rec() const {
  return (m_btr_cur.page_cur.rec);
}

#endif /* UNIV_DEBUG */

#ifndef UNIV_HOTBACKUP

inline ulint btr_pcur_t::get_rel_pos() const {
  ut_ad(m_old_rec != nullptr);
  ut_ad(m_old_stored);
  ut_ad(m_pos_state == BTR_PCUR_WAS_POSITIONED ||
        m_pos_state == BTR_PCUR_IS_POSITIONED);

  return (m_rel_pos);
}

inline void btr_pcur_t::close() {
  free_rec_buf();

  if (m_btr_cur.rtr_info != nullptr) {
    rtr_clean_rtr_info(m_btr_cur.rtr_info, true);
    m_btr_cur.rtr_info = nullptr;
  }

  m_old_rec = nullptr;
  m_btr_cur.page_cur.rec = nullptr;
  m_btr_cur.page_cur.block = nullptr;

  m_old_rec = nullptr;
  m_old_stored = false;

  m_latch_mode = BTR_NO_LATCHES;
  m_pos_state = BTR_PCUR_NOT_POSITIONED;

  m_trx_if_known = nullptr;
}

inline ulint btr_pcur_t::get_up_match() const {
  ut_ad(m_pos_state == BTR_PCUR_WAS_POSITIONED ||
        m_pos_state == BTR_PCUR_IS_POSITIONED);

  const auto cur = get_btr_cur();

  ut_ad(cur->up_match != ULINT_UNDEFINED);

  return (cur->up_match);
}

inline ulint btr_pcur_t::get_low_match() const {
  ut_ad(m_pos_state == BTR_PCUR_WAS_POSITIONED ||
        m_pos_state == BTR_PCUR_IS_POSITIONED);

  const auto cur = get_btr_cur();

  ut_ad(cur->low_match != ULINT_UNDEFINED);

  return (cur->low_match);
}

inline bool btr_pcur_t::is_after_last_on_page() const {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  return (page_cur_is_after_last(get_page_cur()));
}

inline bool btr_pcur_t::is_before_first_on_page() const {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  return (page_cur_is_before_first(get_page_cur()));
}

inline bool btr_pcur_t::is_on_user_rec() const {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  return (!is_before_first_on_page() && !is_after_last_on_page());
}

inline bool btr_pcur_t::is_before_first_in_tree(mtr_t *mtr) const {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  if (btr_page_get_prev(get_page(), mtr) != FIL_NULL) {
    return (false);
  }

  return (page_cur_is_before_first(get_page_cur()));
}

inline bool btr_pcur_t::is_after_last_in_tree(mtr_t *mtr) const {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  if (btr_page_get_next(get_page(), mtr) != FIL_NULL) {
    return (false);
  }

  return (page_cur_is_after_last(get_page_cur()));
}

inline void btr_pcur_t::move_to_next_on_page() {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  page_cur_move_to_next(get_page_cur());

  m_old_stored = false;
}

inline void btr_pcur_t::move_to_prev_on_page() {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  page_cur_move_to_prev(get_page_cur());

  m_old_stored = false;
}

inline void btr_pcur_t::move_to_last_on_page(mtr_t *mtr) {
  UT_NOT_USED(mtr);
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  page_cur_set_after_last(get_block(), get_page_cur());

  m_old_stored = false;
}

inline dberr_t btr_pcur_t::move_to_next_user_rec(mtr_t *mtr) {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  for (;;) {
    m_old_stored = false;

    if (is_after_last_on_page()) {
      if (is_after_last_in_tree(mtr)) {
        return (DB_END_OF_INDEX);
      }

      move_to_next_page(mtr);
    } else {
      move_to_next_on_page();
    }

    if (is_on_user_rec()) {
      return (DB_SUCCESS);
    }
  }
}

inline bool btr_pcur_t::move_to_next(mtr_t *mtr) {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  m_old_stored = false;

  if (is_after_last_on_page()) {
    if (is_after_last_in_tree(mtr)) {
      return (false);
    }

    move_to_next_page(mtr);

    return (true);
  }

  move_to_next_on_page();

  return (true);
}

inline void btr_pcur_t::commit_specify_mtr(mtr_t *mtr) {
  ut_ad(m_pos_state == BTR_PCUR_IS_POSITIONED);

  m_latch_mode = BTR_NO_LATCHES;

  mtr_commit(mtr);

  m_pos_state = BTR_PCUR_WAS_POSITIONED;
}

inline void btr_pcur_t::move_before_first_on_page() {
  ut_ad(m_latch_mode != BTR_NO_LATCHES);

  page_cur_set_before_first(get_block(), get_page_cur());

  m_old_stored = false;
}

inline void btr_pcur_t::reset() {
  free_rec_buf();

  m_old_rec_buf = nullptr;
  m_btr_cur.index = nullptr;
  m_btr_cur.page_cur.rec = nullptr;
  m_old_rec = nullptr;
  m_old_n_fields = 0;
  m_old_stored = false;

  m_latch_mode = BTR_NO_LATCHES;
  m_pos_state = BTR_PCUR_NOT_POSITIONED;
}

#endif /* !UNIV_HOTBACKUP */

#endif /* !btr0pcur_h */
