Skip to content

File px_scan_index_leaf_page_dispatcher.cpp

File List > cubrid > src > query > parallel > px_scan > index > px_scan_index_leaf_page_dispatcher.cpp

Go to the documentation of this file

/*
 *
 * Copyright 2016 CUBRID Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

/* px_scan_index_leaf_page_dispatcher.cpp — descent + leaf chain cursor under m_leaf_mutex. */

#include "px_scan_index_leaf_page_dispatcher.hpp"

#include "btree.h"
#include "btree_load.h"
#include "error_code.h"
#include "error_manager.h"
#include "object_representation.h"
#include "page_buffer.h"
#include "slotted_page.h"
#include "storage_common.h"

// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

namespace parallel_index_scan
{
  /* closed-bound via btree_locate_key; open-bound via boundary-leaf path (btree.c:15077). */
  SCAN_CODE
  leaf_page_dispatcher::descend_to_first_leaf (THREAD_ENTRY *thread_p, SCAN_ID *worker_scan_id, int range_idx,
      PAGE_PTR &out_leaf, VPID *out_vpid, INT16 *out_slot_id)
  {
    PAGE_PTR P_page = NULL;
    PAGE_PTR C_page = NULL;
    VPID P_vpid;
    VPID C_vpid;

    out_leaf = nullptr;
    if (out_vpid != nullptr)
      {
    VPID_SET_NULL (out_vpid);
      }
    if (out_slot_id != nullptr)
      {
    *out_slot_id = NULL_SLOTID;
      }

    /* key1 = storage-leftmost lower bound; NULL descent_key → open-bound vertical path. */
    DB_VALUE *descent_key = nullptr;
    bool closed_bound = false;
    key_val_range *all_ranges = m_ranges->get_key_val_ranges ();
    int num_ranges = m_ranges->get_num_key_ranges ();
    if (range_idx >= 0 && range_idx < num_ranges)
      {
    key_val_range *kvr = &all_ranges[range_idx];
    /* open-lower INF_xx: post-swap kvr->key1 holds the +inf marker; descending it lands past the range -> silent-zero. */
    const bool open_lower_range = (kvr->range == INF_LE || kvr->range == INF_LT
                       || kvr->range == INF_INF);
    if (kvr->range != NA_NA && !open_lower_range
        && !DB_IS_NULL (&kvr->key1) && !btree_multicol_key_is_null (&kvr->key1))
      {
        descent_key = &kvr->key1;
        closed_bound = true;
      }
      }

    BTID_INT *btid_int = m_ranges->get_btid_int ();
    BTID *btid = m_ranges->get_btid ();
    bool use_desc_index = m_ranges->is_desc_index ();

    /* btree_locate_key: descent + leaf-slot in one call (slot = found-or-insertion). */
    if (closed_bound)
      {
    PAGE_PTR leaf = NULL;
    INT16 slot_id = NULL_SLOTID;
    bool found = false;
    VPID leaf_vpid;
    VPID_SET_NULL (&leaf_vpid);
    int err = btree_locate_key (thread_p, btid_int, descent_key, &leaf_vpid, &slot_id, &leaf, &found);
    if (err != NO_ERROR || leaf == NULL)
      {
        if (leaf != NULL)
          {
        pgbuf_unfix_and_init (thread_p, leaf);
          }
        if (err == NO_ERROR && er_errid () == NO_ERROR)
          {
        er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_FAILED, 0);
          }
        return S_ERROR;
      }
    (void) pgbuf_check_page_ptype (thread_p, leaf, PAGE_BTREE);
    out_leaf = leaf;
    if (out_vpid != nullptr)
      {
        *out_vpid = leaf_vpid;
      }
    if (out_slot_id != nullptr)
      {
        /* BTREE_KEY_SMALLER returns slot_id=0; clamp to 1 so slot_iterator starts at first key. */
        *out_slot_id = (slot_id <= 0) ? 1 : slot_id;
      }
    return S_SUCCESS;
      }

    /* Open-bound path: manual latch-coupled descent to leftmost/rightmost leaf. */
    P_vpid.volid = btid->vfid.volid;
    P_vpid.pageid = btid->root_pageid;
    P_page = pgbuf_fix (thread_p, &P_vpid, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH);
    if (P_page == NULL)
      {
    return S_ERROR;
      }

    (void) pgbuf_check_page_ptype (thread_p, P_page, PAGE_BTREE);

    BTREE_ROOT_HEADER *root_header = btree_get_root_header (thread_p, P_page);
    if (root_header == NULL)
      {
    pgbuf_unfix_and_init (thread_p, P_page);
    return S_ERROR;
      }

    /* m_btid_int populated by init_on_main; re-glean would leak main-heap key_type into worker heap. */

    short node_level = root_header->node.node_level;

    /* invariant: P_page latched throughout descent — open-bound only (closed-bound branched above). */
    while (node_level > 1)
      {
    VPID child_vpid;
    VPID_SET_NULL (&child_vpid);

    /* open-bound: asc→slot 1, desc→slot key_cnt; mirrors btree_find_boundary_leaf (btree.c:15077). */
    int key_cnt = btree_node_number_of_keys (thread_p, P_page);
    if (key_cnt <= 0)
      {
        pgbuf_unfix_and_init (thread_p, P_page);
        er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_FAILED, 0);
        return S_ERROR;
      }

    int slot_to_follow = use_desc_index ? key_cnt : 1;
    RECDES rec;
    rec.data = NULL;
    rec.area_size = -1;
    if (spage_get_record (thread_p, P_page, slot_to_follow, &rec, PEEK) != S_SUCCESS)
      {
        pgbuf_unfix_and_init (thread_p, P_page);
        return S_ERROR;
      }

    /* btree_read_fixed_portion_of_non_leaf_record is btree.c-internal; unpack inline. */
    child_vpid.pageid = OR_GET_INT (rec.data);
    child_vpid.volid = OR_GET_SHORT (rec.data + OR_INT_SIZE);

    if (VPID_ISNULL (&child_vpid))
      {
        pgbuf_unfix_and_init (thread_p, P_page);
        er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_FAILED, 0);
        return S_ERROR;
      }

    C_vpid = child_vpid;

    /* fix child before unfixing parent — blocks concurrent split */
    C_page = pgbuf_fix (thread_p, &C_vpid, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH);
    if (C_page == NULL)
      {
        pgbuf_unfix_and_init (thread_p, P_page);
        return S_ERROR;
      }

    (void) pgbuf_check_page_ptype (thread_p, C_page, PAGE_BTREE);

    pgbuf_unfix_and_init (thread_p, P_page);

    BTREE_NODE_HEADER *child_hdr = btree_get_node_header (thread_p, C_page);
    if (child_hdr == NULL)
      {
        pgbuf_unfix_and_init (thread_p, C_page);
        return S_ERROR;
      }

    P_page = C_page;
    C_page = NULL;
    P_vpid = C_vpid;
    node_level = child_hdr->node_level;
      }

    /* hand the leaf latch to the caller; open-bound starts at slot 1 (asc) / key_cnt (desc-scan) */
    out_leaf = P_page;
    if (out_vpid != nullptr)
      {
    *out_vpid = P_vpid;
      }
    if (out_slot_id != nullptr)
      {
    if (use_desc_index)
      {
        int leaf_key_cnt = btree_node_number_of_keys (thread_p, P_page);
        *out_slot_id = (leaf_key_cnt > 0) ? (INT16) leaf_key_cnt : 1;
      }
    else
      {
        *out_slot_id = 1;
      }
      }
    return S_SUCCESS;
  }

  /* out_range_idx: -1 on chain-walk (slot_iterator keeps local idx); target on descent (overwrites). */
  SCAN_CODE
  leaf_page_dispatcher::get_next_page_with_fix (THREAD_ENTRY *thread_p, SCAN_ID *worker_scan_id, PAGE_PTR &out_page,
      INT16 *out_slot_hint, int *out_range_idx)
  {
    out_page = nullptr;
    if (out_slot_hint != nullptr)
      {
    *out_slot_hint = NULL_SLOTID;
      }
    if (out_range_idx != nullptr)
      {
    *out_range_idx = -1;
      }

    std::unique_lock<std::mutex> lock (m_leaf_mutex);

    bool use_desc_index = m_ranges->is_desc_index ();
    key_val_range *all_ranges = m_ranges->get_key_val_ranges ();
    int num_ranges = m_ranges->get_num_key_ranges ();

    /* target = m_current_range_idx + 1, or 0 on first descent. Skip NA_NA (dedup'd duplicate ranges). */
    if (!m_descent_done || m_leaf_ended)
      {
    int target = m_descent_done ? (m_current_range_idx + 1) : 0;
    while (target < num_ranges
           && all_ranges[target].range == NA_NA)
      {
        target++;
      }
    if (target >= num_ranges)
      {
        m_current_range_idx = target;
        m_leaf_ended = true;
        return S_END;
      }

    PAGE_PTR leaf = nullptr;
    VPID leaf_vpid;
    VPID_SET_NULL (&leaf_vpid);
    INT16 leaf_slot = NULL_SLOTID;
    SCAN_CODE sc = descend_to_first_leaf (thread_p, worker_scan_id, target, leaf, &leaf_vpid, &leaf_slot);
    if (sc != S_SUCCESS || leaf == nullptr || VPID_ISNULL (&leaf_vpid))
      {
        if (leaf != nullptr)
          {
        pgbuf_unfix (thread_p, leaf);
          }
        m_leaf_ended = true;
        return (sc == S_SUCCESS) ? S_END : sc;
      }

    BTREE_NODE_HEADER *hdr = btree_get_node_header (thread_p, leaf);
    if (hdr == nullptr)
      {
        pgbuf_unfix (thread_p, leaf);
        m_leaf_ended = true;
        return S_ERROR;
      }

    VPID next = use_desc_index ? hdr->prev_vpid : hdr->next_vpid;
    m_current_leaf_vpid = next;
    m_leaf_ended = VPID_ISNULL (&next);
    m_descent_done = true;
    m_current_range_idx = target;

    out_page = leaf;
    if (out_slot_hint != nullptr)
      {
        *out_slot_hint = leaf_slot;
      }
    if (out_range_idx != nullptr)
      {
        *out_range_idx = target;
      }
    return S_SUCCESS;
      }

    /* Chain-walk branch: fix current leaf vpid, advance pointer, return -1 sentinel for range_idx. */
    VPID ret_vpid = m_current_leaf_vpid;
    PAGE_PTR page = pgbuf_fix (thread_p, &ret_vpid, OLD_PAGE, PGBUF_LATCH_READ, PGBUF_UNCONDITIONAL_LATCH);
    if (page == nullptr)
      {
    m_leaf_ended = true;
    return S_ERROR;
      }

    BTREE_NODE_HEADER *hdr = btree_get_node_header (thread_p, page);
    if (hdr == nullptr)
      {
    pgbuf_unfix (thread_p, page);
    m_leaf_ended = true;
    return S_ERROR;
      }

    VPID next = use_desc_index ? hdr->prev_vpid : hdr->next_vpid;
    if (VPID_ISNULL (&next))
      {
    m_leaf_ended = true;
      }
    else
      {
    m_current_leaf_vpid = next;
      }

    out_page = page;
    /* sync worker range_idx; stale value drops OIDs at new-range leaf boundary. */
    if (out_range_idx != nullptr)
      {
    *out_range_idx = m_current_range_idx;
      }
    return S_SUCCESS;
  }

  /* completed = last_local_idx - 1; monotonic max so next fetch target = idx + 1. */
  /* discard stale past_upper that lags authoritative cursor; otherwise skips in-progress chain. */
  void
  leaf_page_dispatcher::signal_chain_ended (int last_local_idx)
  {
    std::unique_lock<std::mutex> lock (m_leaf_mutex);
    int completed = last_local_idx - 1;
    if (completed < m_current_range_idx)
      {
    return;
      }
    if (completed > m_current_range_idx)
      {
    m_current_range_idx = completed;
      }
    m_leaf_ended = true;
  }
}