Skip to content

File show_scan.c

File List > cubrid > src > query > show_scan.c

Go to the documentation of this file

/*
 * Copyright 2008 Search Solution Corporation
 * 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.
 *
 */

/*
 * show_scan.c - scan information for show statements
 */

#ident "$Id$"

/*TODO this header (cinttypes) apparently has to be the first one included
 *in order to compile (there's some problem with the PRIx64 macro)
 */
#include <cinttypes>
#include "config.h"

#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "error_manager.h"
#include "memory_alloc.h"

#include "query_manager.h"
#include "object_primitive.h"
#include "object_representation.h"
#include "scan_manager.h"
#include "show_scan.h"

#include "disk_manager.h"
#include "log_manager.h"
#include "slotted_page.h"
#include "heap_file.h"
#include "btree.h"
#include "connection_support.hpp"
#include "critical_section.h"
#include "tz_support.h"
#include "db_date.h"
#include "network.h"

#if defined(ENABLE_SYSTEMTAP)
#include "probes.h"
#endif /* ENABLE_SYSTEMTAP */

#include "porting.h"
#include "server_support.h"
#include "dbtype.h"
#include "thread_manager.hpp"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

typedef SCAN_CODE (*NEXT_SCAN_FUNC) (THREAD_ENTRY * thread_p, int cursor, DB_VALUE ** out_values, int out_cnt,
                     void *ctx);
typedef int (*START_SCAN_FUNC) (THREAD_ENTRY * thread_p, int show_type, DB_VALUE ** arg_values, int arg_cnt,
                void **ctx);
typedef int (*END_SCAN_FUNC) (THREAD_ENTRY * thread_p, void **ctx);

typedef struct show_request SHOW_REQUEST;
struct show_request
{
  SHOWSTMT_TYPE show_type;  /* show stmt type */
  START_SCAN_FUNC start_func;   /* start scan function */
  NEXT_SCAN_FUNC next_func; /* next scan function */
  END_SCAN_FUNC end_func;   /* end scan function */
};

const size_t THREAD_SCAN_COLUMN_COUNT = 26;

static SCAN_CODE showstmt_array_next_scan (THREAD_ENTRY * thread_p, int cursor, DB_VALUE ** out_values, int out_cnt,
                       void *ptr);
static int showstmt_array_end_scan (THREAD_ENTRY * thread_p, void **ptr);
#if defined (SERVER_MODE)
static void thread_scan_mapfunc (THREAD_ENTRY & thread_ref, bool & stop_mapper, THREAD_ENTRY * caller_thread_p,
                 SHOWSTMT_ARRAY_CONTEXT * ctx, int &error);
#endif // SERVER_MODE


static bool show_scan_Inited = false;

static SHOW_REQUEST show_Requests[SHOWSTMT_END];

/*
 *  showstmt_scan_init () - initialize the scan functions of
 *                          show statments.
 *   return: NULL
 */
void
showstmt_scan_init (void)
{
  SHOW_REQUEST *req;

  if (show_scan_Inited)
    {
      return;
    }

  memset (show_Requests, 0, SHOWSTMT_END * sizeof (SHOW_REQUEST));

  req = &show_Requests[SHOWSTMT_VOLUME_HEADER];
  req->show_type = SHOWSTMT_VOLUME_HEADER;
  req->start_func = disk_volume_header_start_scan;
  req->next_func = disk_volume_header_next_scan;
  req->end_func = disk_volume_header_end_scan;

  req = &show_Requests[SHOWSTMT_ACCESS_STATUS];
  req->show_type = SHOWSTMT_ACCESS_STATUS;
  req->start_func = css_user_access_status_start_scan;
  req->next_func = showstmt_array_next_scan;
  req->end_func = showstmt_array_end_scan;

  req = &show_Requests[SHOWSTMT_ACTIVE_LOG_HEADER];
  req->show_type = SHOWSTMT_ACTIVE_LOG_HEADER;
  req->start_func = log_active_log_header_start_scan;
  req->next_func = log_active_log_header_next_scan;
  req->end_func = log_active_log_header_end_scan;

  req = &show_Requests[SHOWSTMT_ARCHIVE_LOG_HEADER];
  req->show_type = SHOWSTMT_ARCHIVE_LOG_HEADER;
  req->start_func = log_archive_log_header_start_scan;
  req->next_func = log_archive_log_header_next_scan;
  req->end_func = log_archive_log_header_end_scan;

  req = &show_Requests[SHOWSTMT_SLOTTED_PAGE_HEADER];
  req->show_type = SHOWSTMT_SLOTTED_PAGE_HEADER;
  req->start_func = spage_header_start_scan;
  req->next_func = spage_header_next_scan;
  req->end_func = spage_header_end_scan;

  req = &show_Requests[SHOWSTMT_SLOTTED_PAGE_SLOTS];
  req->show_type = SHOWSTMT_SLOTTED_PAGE_SLOTS;
  req->start_func = spage_slots_start_scan;
  req->next_func = spage_slots_next_scan;
  req->end_func = spage_slots_end_scan;

  req = &show_Requests[SHOWSTMT_HEAP_HEADER];
  req->show_type = SHOWSTMT_HEAP_HEADER;
  req->start_func = heap_header_capacity_start_scan;
  req->next_func = heap_header_next_scan;
  req->end_func = heap_header_capacity_end_scan;

  req = &show_Requests[SHOWSTMT_ALL_HEAP_HEADER];
  req->show_type = SHOWSTMT_ALL_HEAP_HEADER;
  req->start_func = heap_header_capacity_start_scan;
  req->next_func = heap_header_next_scan;
  req->end_func = heap_header_capacity_end_scan;

  req = &show_Requests[SHOWSTMT_HEAP_CAPACITY];
  req->show_type = SHOWSTMT_HEAP_CAPACITY;
  req->start_func = heap_header_capacity_start_scan;
  req->next_func = heap_capacity_next_scan;
  req->end_func = heap_header_capacity_end_scan;

  req = &show_Requests[SHOWSTMT_ALL_HEAP_CAPACITY];
  req->show_type = SHOWSTMT_ALL_HEAP_CAPACITY;
  req->start_func = heap_header_capacity_start_scan;
  req->next_func = heap_capacity_next_scan;
  req->end_func = heap_header_capacity_end_scan;

  req = &show_Requests[SHOWSTMT_INDEX_HEADER];
  req->show_type = SHOWSTMT_INDEX_HEADER;
  req->start_func = btree_index_start_scan;
  req->next_func = btree_index_next_scan;
  req->end_func = btree_index_end_scan;

  req = &show_Requests[SHOWSTMT_INDEX_CAPACITY];
  req->show_type = SHOWSTMT_INDEX_CAPACITY;
  req->start_func = btree_index_start_scan;
  req->next_func = btree_index_next_scan;
  req->end_func = btree_index_end_scan;

  req = &show_Requests[SHOWSTMT_ALL_INDEXES_HEADER];
  req->show_type = SHOWSTMT_ALL_INDEXES_HEADER;
  req->start_func = btree_index_start_scan;
  req->next_func = btree_index_next_scan;
  req->end_func = btree_index_end_scan;

  req = &show_Requests[SHOWSTMT_ALL_INDEXES_CAPACITY];
  req->show_type = SHOWSTMT_ALL_INDEXES_CAPACITY;
  req->start_func = btree_index_start_scan;
  req->next_func = btree_index_next_scan;
  req->end_func = btree_index_end_scan;

  req = &show_Requests[SHOWSTMT_GLOBAL_CRITICAL_SECTIONS];
  req->show_type = SHOWSTMT_GLOBAL_CRITICAL_SECTIONS;
  req->start_func = csect_start_scan;
  req->next_func = showstmt_array_next_scan;
  req->end_func = showstmt_array_end_scan;

  req = &show_Requests[SHOWSTMT_JOB_QUEUES];
  req->show_type = SHOWSTMT_JOB_QUEUES;
  req->start_func = css_job_queues_start_scan;
  req->next_func = showstmt_array_next_scan;
  req->end_func = showstmt_array_end_scan;

  req = &show_Requests[SHOWSTMT_TIMEZONES];
  req->show_type = SHOWSTMT_TIMEZONES;
  req->start_func = tz_timezones_start_scan;
  req->next_func = showstmt_array_next_scan;
  req->end_func = showstmt_array_end_scan;

  req = &show_Requests[SHOWSTMT_FULL_TIMEZONES];
  req->show_type = SHOWSTMT_FULL_TIMEZONES;
  req->start_func = tz_full_timezones_start_scan;
  req->next_func = showstmt_array_next_scan;
  req->end_func = showstmt_array_end_scan;

  req = &show_Requests[SHOWSTMT_TRAN_TABLES];
  req->show_type = SHOWSTMT_TRAN_TABLES;
  req->start_func = logtb_descriptors_start_scan;
  req->next_func = showstmt_array_next_scan;
  req->end_func = showstmt_array_end_scan;

  req = &show_Requests[SHOWSTMT_THREADS];
  req->show_type = SHOWSTMT_THREADS;
  req->start_func = thread_start_scan;
  req->next_func = showstmt_array_next_scan;
  req->end_func = showstmt_array_end_scan;

  req = &show_Requests[SHOWSTMT_PAGE_BUFFER_STATUS];
  req->show_type = SHOWSTMT_PAGE_BUFFER_STATUS;
  req->start_func = pgbuf_start_scan;
  req->next_func = showstmt_array_next_scan;
  req->end_func = showstmt_array_end_scan;

  /* append to init other show statement scan function here */


  show_scan_Inited = true;
}

/*
 *  showstmt_next_scan () - scan values from different show statment.
 *   return: NO_ERROR, or ER_code
 *   thread_p(in):
 *   s_id(in):
 */
SCAN_CODE
showstmt_next_scan (THREAD_ENTRY * thread_p, SCAN_ID * s_id)
{
  SHOWSTMT_SCAN_ID *stsidp = &s_id->s.stsid;
  SHOWSTMT_TYPE show_type = stsidp->show_type;
  NEXT_SCAN_FUNC next_func = NULL;
  SCAN_CODE code;
  int i;

  assert (show_type == show_Requests[show_type].show_type);

  next_func = show_Requests[show_type].next_func;
  if (next_func == NULL)
    {
      return S_END;
    }

  /* free values which need be cleared */
  for (i = 0; i < stsidp->out_cnt; i++)
    {
      pr_clear_value (stsidp->out_values[i]);
    }

  code = (*next_func) (thread_p, stsidp->cursor++, stsidp->out_values, stsidp->out_cnt, stsidp->ctx);
  return code;
}

/*
 *  showstmt_start_scan () - before scan.
 *   return: NO_ERROR, or ER_code
 *   thread_p(in):
 *   s_id(in):
 */
int
showstmt_start_scan (THREAD_ENTRY * thread_p, SCAN_ID * s_id)
{
  SHOWSTMT_SCAN_ID *stsidp = &s_id->s.stsid;
  SHOWSTMT_TYPE show_type = stsidp->show_type;
  START_SCAN_FUNC start_func = NULL;
  int error;

  assert (show_type == show_Requests[show_type].show_type);

  start_func = show_Requests[show_type].start_func;
  if (start_func == NULL)
    {
      return NO_ERROR;
    }

  error = (*start_func) (thread_p, (int) show_type, stsidp->arg_values, stsidp->arg_cnt, &stsidp->ctx);
  return error;
}

/*
 *  showstmt_end_scan () - after scan.
 *   return: NO_ERROR, or ER_code
 *   thread_p(in):
 *   s_id(in):
 */
int
showstmt_end_scan (THREAD_ENTRY * thread_p, SCAN_ID * s_id)
{
  SHOWSTMT_SCAN_ID *stsidp = &s_id->s.stsid;
  SHOWSTMT_TYPE show_type = stsidp->show_type;
  END_SCAN_FUNC end_func = NULL;
  int error;

  assert (show_type == show_Requests[show_type].show_type);

  end_func = show_Requests[show_type].end_func;
  if (end_func == NULL)
    {
      return NO_ERROR;
    }

  error = (*end_func) (thread_p, &stsidp->ctx);
  return error;
}

/*
 *   showstmt_alloc_array_context () - init context for db_values arrays
 *   return: NO_ERROR, or ER_code
 *   thread_p(in):
 *   num_total(in):
 *   num_col(in):
 */
SHOWSTMT_ARRAY_CONTEXT *
showstmt_alloc_array_context (THREAD_ENTRY * thread_p, int num_total, int num_cols)
{
  SHOWSTMT_ARRAY_CONTEXT *ctx;

  ctx = (SHOWSTMT_ARRAY_CONTEXT *) db_private_alloc (thread_p, sizeof (SHOWSTMT_ARRAY_CONTEXT));
  if (ctx == NULL)
    {
      return NULL;
    }

  ctx->num_used = 0;
  ctx->num_cols = num_cols;
  ctx->num_total = num_total;
  ctx->tuples = (DB_VALUE **) db_private_alloc (thread_p, sizeof (DB_VALUE *) * num_total);
  if (ctx->tuples == NULL)
    {
      goto on_error;
    }

  memset (ctx->tuples, 0, sizeof (DB_VALUE *) * num_total);
  return ctx;

on_error:
  if (ctx != NULL)
    {
      db_private_free (thread_p, ctx);
    }
  return NULL;
}

/*
 *  showstmt_free_array_context () - free context for db_values arrays
 *   return: NO_ERROR, or ER_code
 *   thread_p(in):
 *   ctx(in):
 */
void
showstmt_free_array_context (THREAD_ENTRY * thread_p, SHOWSTMT_ARRAY_CONTEXT * ctx)
{
  int i, j;
  DB_VALUE *vals;

  assert (ctx != NULL);

  for (i = 0; i < ctx->num_used; i++)
    {
      vals = ctx->tuples[i];
      for (j = 0; j < ctx->num_cols; j++)
    {
      pr_clear_value (&vals[j]);
    }

      db_private_free (thread_p, vals);
    }

  db_private_free (thread_p, ctx->tuples);
  db_private_free (thread_p, ctx);
}

/*
 *  showstmt_alloc_tuple_in_context () - alloc and return next tuple from context
 *   return:  tuple pointer
 *   thread_p(in):
 *   ctx(in):
 */
DB_VALUE *
showstmt_alloc_tuple_in_context (THREAD_ENTRY * thread_p, SHOWSTMT_ARRAY_CONTEXT * ctx)
{
  int i, num_new_total;
  DB_VALUE **new_tuples = NULL;
  DB_VALUE *vals = NULL;

  if (ctx->num_used == ctx->num_total)
    {
      num_new_total = (int) (ctx->num_total * 1.5 + 1);
      new_tuples = (DB_VALUE **) db_private_realloc (thread_p, ctx->tuples, sizeof (DB_VALUE *) * num_new_total);
      if (new_tuples == NULL)
    {
      return NULL;
    }

      memset (new_tuples + ctx->num_total, 0, sizeof (DB_VALUE *) * (num_new_total - ctx->num_total));

      ctx->tuples = new_tuples;
      ctx->num_total = num_new_total;
    }

  vals = (DB_VALUE *) db_private_alloc (thread_p, sizeof (DB_VALUE) * ctx->num_cols);
  if (vals == NULL)
    {
      return NULL;
    }
  for (i = 0; i < ctx->num_cols; i++)
    {
      db_make_null (&vals[i]);
    }

  ctx->tuples[ctx->num_used++] = vals;
  return vals;
}

/*
 *  showstmt_array_next_scan () - next scan function for array
 *   return: NO_ERROR, or ER_code
 *   thread_p(in):
 *   cursor(in):
 *   out_values(in/out):
 *   out_cnt(in):
 *   ptr(in):
 */
static SCAN_CODE
showstmt_array_next_scan (THREAD_ENTRY * thread_p, int cursor, DB_VALUE ** out_values, int out_cnt, void *ptr)
{
  SHOWSTMT_ARRAY_CONTEXT *ctx = (SHOWSTMT_ARRAY_CONTEXT *) ptr;
  DB_VALUE *vals = NULL;
  int i;

  if (ctx == NULL || cursor < 0 || cursor >= ctx->num_used)
    {
      return S_END;
    }

  assert (out_cnt == ctx->num_cols);

  vals = ctx->tuples[cursor];

  for (i = 0; i < ctx->num_cols; i++)
    {
      pr_clone_value (&vals[i], out_values[i]);
    }

  return S_SUCCESS;
}

/*
 *  showstmt_array_end_scan () - end scan function for array
 *   return: NO_ERROR, or ER_code
 *   thread_p(in):
 *   ptr(in/out):
 */
static int
showstmt_array_end_scan (THREAD_ENTRY * thread_p, void **ptr)
{
  if (*ptr != NULL)
    {
      showstmt_free_array_context (thread_p, (SHOWSTMT_ARRAY_CONTEXT *) (*ptr));
      *ptr = NULL;
    }
  return NO_ERROR;
}

#if defined (SERVER_MODE)
//
// thread_scan_mapfunc () - mapper function to get information from thread entry for scanner
//
// thread_ref (in)      : mapped thread entry
// stop_mapper (out)    : output true to stop mapping
// caller_thread_p (in) : thread entry of show scan thread
// ctx (out)            : show scan array context
// error (out)          : output NO_ERROR or error code
//
static void
thread_scan_mapfunc (THREAD_ENTRY & thread_ref, bool & stop_mapper, THREAD_ENTRY * caller_thread_p,
             SHOWSTMT_ARRAY_CONTEXT * ctx, int &error)
{
  DB_VALUE *vals = NULL;
  THREAD_ENTRY *thrd = &thread_ref;
  THREAD_ENTRY *next_thrd = NULL;
  size_t idx = 0;
  int ival;
  INT64 i64val;
  CSS_CONN_ENTRY *conn_entry = NULL;
  OR_ALIGNED_BUF (1024) a_buffer;
  char *buffer;
  char *area;
  int buf_len;
  HL_HEAPID private_heap_id;
  void *query_entry;
  LK_ENTRY *lockwait;
  time_t stime;
  int msecs;
  DB_DATETIME time_val;

  if (thrd->m_status == cubthread::entry::status::TS_DEAD)
    {
      // thread entry does not belong to a running thread; should not be shown in SHOW THREADS statement
      return;
    }

  vals = showstmt_alloc_tuple_in_context (caller_thread_p, ctx);
  if (vals == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      stop_mapper = true;
      return;
    }

  /* Index */
  db_make_int (&vals[idx], thrd->index);
  idx++;

  /* Jobq_index */// it is obsolete
  db_make_null (&vals[idx]);
  idx++;

  /* Thread_id */
  db_make_bigint (&vals[idx], (DB_BIGINT) thrd->get_posix_id ());
  idx++;

  /* Tran_index */
  ival = thrd->tran_index;
  if (ival >= 0)
    {
      db_make_int (&vals[idx], ival);
    }
  else
    {
      db_make_null (&vals[idx]);
    }
  idx++;

  /* Type */
  db_make_string (&vals[idx], thread_type_to_string (thrd->type));
  idx++;

  /* Status */
  db_make_string (&vals[idx], thread_status_to_string (thrd->m_status));
  idx++;

  /* Resume_status */
  db_make_string (&vals[idx], thread_resume_status_to_string (thrd->resume_status));
  idx++;

  /* Net_request */
  ival = thrd->net_request_index;
  if (ival != -1)
    {
      db_make_string (&vals[idx], get_net_request_name (ival));
    }
  else
    {
      db_make_null (&vals[idx]);
    }
  idx++;

  /* Conn_client_id */
  ival = thrd->client_id;
  if (ival != -1)
    {
      db_make_int (&vals[idx], ival);
    }
  else
    {
      db_make_null (&vals[idx]);
    }
  idx++;

  /* Conn_request_id */
  ival = thrd->rid;
  if (ival != 0)
    {
      db_make_int (&vals[idx], ival);
    }
  else
    {
      db_make_null (&vals[idx]);
    }
  idx++;

  /* Conn_index */
  conn_entry = thrd->conn_entry;
  if (conn_entry != NULL)
    {
      db_make_int (&vals[idx], conn_entry->idx);
    }
  else
    {
      db_make_null (&vals[idx]);
    }
  idx++;

  /* Last_error_code */
  ival = er_errid ();
  db_make_int (&vals[idx], ival);
  idx++;

  /* Last_error_msg */
  buffer = OR_ALIGNED_BUF_START (a_buffer);
  buf_len = 1024;

  if (ival != NO_ERROR)
    {
      char *ermsg;

      area = er_get_area_error (buffer, &buf_len);
      ermsg = er_get_ermsg_from_area_error (area);
      ermsg[255] = '\0';    /* truncate msg */

      error = db_make_string_copy (&vals[idx], ermsg);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      stop_mapper = true;
      return;
    }
    }
  else
    {
      db_make_null (&vals[idx]);
    }
  idx++;

  /* Private_heap_id */
  buffer = OR_ALIGNED_BUF_START (a_buffer);
  buf_len = 1024;

  private_heap_id = thrd->private_heap_id;
  if (private_heap_id != 0)
    {
      snprintf (buffer, buf_len, "0x%08" PRIx64, (UINT64) private_heap_id);
      error = db_make_string_copy (&vals[idx], buffer);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      stop_mapper = true;
      return;
    }
    }
  else
    {
      db_make_null (&vals[idx]);
    }
  idx++;

  /* Query_entry */
  buffer = OR_ALIGNED_BUF_START (a_buffer);
  buf_len = 1024;

  query_entry = thrd->query_entry;
  if (query_entry != NULL)
    {
      snprintf (buffer, buf_len, "0x%08" PRIx64, (UINT64) query_entry);
      error = db_make_string_copy (&vals[idx], buffer);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      stop_mapper = true;
      return;
    }
    }
  else
    {
      db_make_null (&vals[idx]);
    }
  idx++;

  /* Interrupted */
  db_make_int (&vals[idx], thrd->interrupted);
  idx++;

  /* Shutdown */
  db_make_int (&vals[idx], thrd->shutdown);
  idx++;

  /* Check_interrupt */
  db_make_int (&vals[idx], thrd->check_interrupt);
  idx++;

  /* Wait_for_latch_promote */
  db_make_int (&vals[idx], thrd->wait_for_latch_promote);
  idx++;

  buffer = OR_ALIGNED_BUF_START (a_buffer);
  buf_len = 1024;

  lockwait = (LK_ENTRY *) thrd->lockwait;
  if (lockwait != NULL)
    {
      /* lockwait_blocked_mode */
      snprintf (buffer, buf_len, "%*s", LOCK_MODE_STR_MAX_LENGTH,
        lock_to_lockmode_string ((LOCK) lockwait->blocked_mode));
      error = db_make_string_copy (&vals[idx], buffer);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      stop_mapper = true;
      return;
    }
      idx++;

      /* Lockwait_start_time */
      i64val = thrd->lockwait_stime;
      stime = (time_t) (i64val / 1000LL);
      msecs = i64val % 1000;
      db_localdatetime_msec (&stime, msecs, &time_val);
      error = db_make_datetime (&vals[idx], &time_val);
      if (error != NO_ERROR)
    {
      ASSERT_ERROR ();
      stop_mapper = true;
      return;
    }
      idx++;

      /* Lockwait_msecs */
      db_make_int (&vals[idx], thrd->lockwait_msecs);
      idx++;

      /* Lockwait_state */
      db_make_string (&vals[idx], lock_wait_state_to_string (thrd->lockwait_state));
      idx++;
    }
  else
    {
      /* lockwait_blocked_mode */
      db_make_null (&vals[idx]);
      idx++;

      /* Lockwait_start_time */
      db_make_null (&vals[idx]);
      idx++;

      /* Lockwait_msecs */
      db_make_null (&vals[idx]);
      idx++;

      /* Lockwait_state */
      db_make_null (&vals[idx]);
      idx++;
    }

  /* Next_wait_thread_index */
  next_thrd = thrd->next_wait_thrd;
  if (next_thrd != NULL)
    {
      db_make_int (&vals[idx], next_thrd->index);
    }
  else
    {
      db_make_null (&vals[idx]);
    }
  idx++;

  /* Next_tran_wait_thread_index */
  next_thrd = thrd->tran_next_wait;
  if (next_thrd != NULL)
    {
      db_make_int (&vals[idx], next_thrd->index);
    }
  else
    {
      db_make_null (&vals[idx]);
    }
  idx++;

  /* Next_worker_thread_index */
  next_thrd = thrd->worker_thrd_list;
  if (next_thrd != NULL)
    {
      db_make_int (&vals[idx], next_thrd->index);
    }
  else
    {
      db_make_null (&vals[idx]);
    }
  idx++;

  assert (idx == THREAD_SCAN_COLUMN_COUNT);
}
#endif // SERVER_MODE

/*
 * thread_start_scan () -  start scan function for show threads
 *   return: NO_ERROR, or ER_code
 *
 *   thread_p(in):
 *   type (in):
 *   arg_values(in):
 *   arg_cnt(in):
 *   ptr(in/out):
 */
int
thread_start_scan (THREAD_ENTRY * thread_p, int type, DB_VALUE ** arg_values, int arg_cnt, void **ptr)
{
#if defined(SERVER_MODE)
  SHOWSTMT_ARRAY_CONTEXT *ctx = NULL;
  int error = NO_ERROR;

  *ptr = NULL;

  ctx = showstmt_alloc_array_context (thread_p, (int) thread_num_total_threads (), THREAD_SCAN_COLUMN_COUNT);
  if (ctx == NULL)
    {
      ASSERT_ERROR_AND_SET (error);
      return error;
    }

  // scan all threads
  thread_get_manager ()->map_entries (thread_scan_mapfunc, thread_p, ctx, error);

  if (error == NO_ERROR)
    {
      *ptr = ctx;
    }
  else
    {
      ASSERT_ERROR ();
      showstmt_free_array_context (thread_p, ctx);
    }

  return error;
#else // not SERVER_MODE
  return NO_ERROR;
#endif // not SERVER_MODE
}