Skip to content

File query_dump.c

File List > cubrid > src > query > query_dump.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.
 *
 */

/*
 * query_dump.c - Query processor printer
 */

#ident "$Id$"

#include "config.h"
#include <stdio.h>

#include "jansson.h"
#include "query_dump.h"
#include "object_primitive.h"
#include "system_parameter.h"
#include "dbtype.h"
#include "lock_table.h"     // lock_to_lockmode_string
#if defined (SERVER_MODE)
#include "thread_manager.hpp"   // for thread_get_thread_entry_info
#include "px_heap_scan_trace_handler.hpp"
#include "px_query_executor.hpp"
#endif // SERVER_MODE
#include "xasl.h"
#include "xasl_aggregate.hpp"
#include "xasl_predicate.hpp"
#include "subquery_cache.h"
#include "query_hash_join.h"
#include "memoize.hpp"
// XXX: SHOULD BE THE LAST INCLUDE HEADER
#include "memory_wrapper.hpp"

#define foutput stdout

enum
{
  ARITH_EXP = 0,
  AGG_EXP = 1
};

#define HASH_NUMBER 128

#define HAVE_SUBQUERY_PROC(xasl_p) \
  ((xasl_p)->type != MERGELIST_PROC && (xasl_p)->type != HASHJOIN_PROC && (xasl_p)->type != UNION_PROC \
   && (xasl_p)->type != INTERSECTION_PROC && (xasl_p)->type != DIFFERENCE_PROC)

typedef struct qdump_xasl_check_node QDUMP_XASL_CHECK_NODE;
struct qdump_xasl_check_node
{
  QDUMP_XASL_CHECK_NODE *next;
  UINTPTR xasl_addr;
  PROC_TYPE xasl_type;
  int referenced;
  int reachable;
};

static bool qdump_print_xasl_type (XASL_NODE * xasl);
static bool qdump_print_db_value_array (DB_VALUE ** array, int cnt);
static bool qdump_print_column (const char *title_p, int col_count, int *column_p);
static bool qdump_print_list_merge_info (QFILE_LIST_MERGE_INFO * ptr);
static bool qdump_print_merge_list_proc_node (MERGELIST_PROC_NODE * ptr);
static bool qdump_print_hashjoin_proc_node (HASHJOIN_PROC_NODE * ptr);
static bool qdump_print_update_proc_node (UPDATE_PROC_NODE * ptr);
static bool qdump_print_delete_proc_node (DELETE_PROC_NODE * ptr);
static bool qdump_print_insert_proc_node (INSERT_PROC_NODE * ptr);
static const char *qdump_target_type_string (TARGET_TYPE type);
static const char *qdump_access_method_string (ACCESS_METHOD access);
static bool qdump_print_access_spec (ACCESS_SPEC_TYPE * spec_list);
static const char *qdump_key_range_string (RANGE range);
static bool qdump_print_key_info (KEY_INFO * key_info);
static const char *qdump_range_type_string (RANGE_TYPE range_type);
static bool qdump_print_index (INDX_INFO * indexptr);
static bool qdump_print_btid (BTID id);
static bool qdump_print_class (CLS_SPEC_TYPE * ptr);
static bool qdump_print_hfid (HFID id);
static bool qdump_print_vfid (VFID id);
static bool qdump_print_list (LIST_SPEC_TYPE * ptr);
static bool qdump_print_showstmt (SHOWSTMT_SPEC_TYPE * ptr);
static bool qdump_print_outlist (const char *title, OUTPTR_LIST * outlist);
static bool qdump_print_list_id (QFILE_LIST_ID * idptr);
static bool qdump_print_type_list (QFILE_TUPLE_VALUE_TYPE_LIST * typeptr);
static bool qdump_print_domain_list (int cnt, TP_DOMAIN ** ptr);
static bool qdump_print_sort_list (SORT_LIST * sorting_list);
static bool qdump_print_attribute_id (ATTR_DESCR attr);
static bool qdump_print_tuple_value_position (QFILE_TUPLE_VALUE_POSITION pos);
static bool qdump_print_value_list (VAL_LIST * vallist);
static bool qdump_print_regu_variable_list (REGU_VARIABLE_LIST varlist);
static const char *qdump_option_string (int option);
static bool qdump_print_db_value (DB_VALUE * value);
static const char *qdump_regu_type_string (REGU_DATATYPE type);
static bool qdump_print_regu_type (REGU_VARIABLE * value);
static const char *qdump_data_type_string (DB_TYPE type);
static bool qdump_print_value (REGU_VARIABLE * value);
static bool qdump_print_function_value (REGU_VARIABLE * regu);
static bool qdump_print_value_type_addr (REGU_VARIABLE * value);
static bool qdump_print_oid (OID * oidptr);
static bool qdump_print_predicate (PRED_EXPR * predptr);
static const char *qdump_relation_operator_string (int op);
static const char *qdump_arith_operator_string (OPERATOR_TYPE opcode);
static bool qdump_print_arith_expression (ARITH_TYPE * arith_p);
static bool qdump_print_aggregate_expression (AGGREGATE_TYPE * aggptr);
static bool qdump_print_arith (int type, void *ptr);
static bool qdump_print_term (PRED_EXPR * pred_ptr);
static const char *qdump_bool_operator_string (BOOL_OP bool_op);
static bool qdump_print_lhs_predicate (PRED_EXPR * pred_p);
#if defined(CUBRID_DEBUG)
static QDUMP_XASL_CHECK_NODE *qdump_find_check_node_for (XASL_NODE * xasl,
                             QDUMP_XASL_CHECK_NODE * chk_nodes[HASH_NUMBER]);
static void qdump_check_node (XASL_NODE * xasl, QDUMP_XASL_CHECK_NODE * chk_nodes[HASH_NUMBER]);
static int qdump_print_inconsistencies (QDUMP_XASL_CHECK_NODE * chk_nodes[HASH_NUMBER]);
#endif /* CUBRID_DEBUG */
static const char *qdump_hashjoin_type_string (HASH_METHOD hash_method);
static void qdump_print_hashjoin_stats_text (FILE * fp, xasl_node * xasl_p, int indent);
static void qdump_print_hashjoin_stats_json (xasl_node * xasl_p, json_t * parent);
static void qdump_print_px_subquery_stats_json (parallel_query_execute::query_executor * px_executor, json_t * parent);

/*
 * qdump_print_xasl_type () -
 *   return:
 *   xasl(in):
 */
static bool
qdump_print_xasl_type (XASL_NODE * xasl_p)
{
  const char *type_string_p;

  switch (xasl_p->type)
    {
    case BUILDLIST_PROC:
      type_string_p = "buildlist_proc";
      break;
    case BUILDVALUE_PROC:
      type_string_p = "buildvalue_proc";
      break;
    case UNION_PROC:
      type_string_p = "union_proc";
      break;
    case DIFFERENCE_PROC:
      type_string_p = "difference_proc";
      break;
    case INTERSECTION_PROC:
      type_string_p = "intersection_proc";
      break;
    case OBJFETCH_PROC:
      type_string_p = "objfetch_proc";
      break;
    case SCAN_PROC:
      type_string_p = "scan_proc";
      break;
    case MERGELIST_PROC:
      type_string_p = "mergelist_proc";
      break;
    case HASHJOIN_PROC:
      type_string_p = "hashjoin_proc";
      break;
    case UPDATE_PROC:
      type_string_p = "update_proc";
      break;
    case DELETE_PROC:
      type_string_p = "delete_proc";
      break;
    case INSERT_PROC:
      type_string_p = "insert_proc";
      break;
    case CONNECTBY_PROC:
      type_string_p = "connectby_proc";
      break;
    case MERGE_PROC:
      type_string_p = "merge_proc";
      break;
    case CTE_PROC:
      type_string_p = "cte_proc";
      break;
    default:
      return false;
    }

  fprintf (foutput, "[%s:%p]\n", type_string_p, xasl_p);
  return true;
}

/*
 * qdump_print_db_value_array () -
 *   return:
 *   array(in)  :
 *   int cnt(in):
 */
static bool
qdump_print_db_value_array (DB_VALUE ** array_p, int count)
{
  int i;

  if (array_p == NULL)
    {
      return true;
    }

  for (i = 0; i < count; i++, array_p++)
    {
      if (!qdump_print_db_value (*array_p))
    {
      return false;
    }
      fprintf (foutput, "; ");
    }

  return true;
}

static bool
qdump_print_column (const char *title_p, int col_count, int *column_p)
{
  int i;

  fprintf (foutput, "[%s:", title_p);

  for (i = 0; i < col_count; i++)
    {
      fprintf (foutput, "%s%d", (i ? "|" : ""), *(column_p + i));
    }

  fprintf (foutput, "]");
  return true;
}

/*
 * qdump_print_list_merge_info () -
 *   return:
 *   ptr(in):
 */
static bool
qdump_print_list_merge_info (QFILE_LIST_MERGE_INFO * merge_info_p)
{
  if (merge_info_p == NULL)
    {
      return true;
    }

  fprintf (foutput, "[join type:%d]", merge_info_p->join_type);
  fprintf (foutput, "[single fetch:%d]", merge_info_p->single_fetch);
  fprintf (foutput, "\n");

  qdump_print_column ("outer column position", merge_info_p->ls_column_cnt, merge_info_p->ls_outer_column);
  qdump_print_column ("outer column is unique", merge_info_p->ls_column_cnt, merge_info_p->ls_outer_unique);
  qdump_print_column ("inner column position", merge_info_p->ls_column_cnt, merge_info_p->ls_inner_column);
  qdump_print_column ("inner column is unique", merge_info_p->ls_column_cnt, merge_info_p->ls_inner_unique);
  fprintf (foutput, "\n");

  fprintf (foutput, "[output column count:%d]", merge_info_p->ls_pos_cnt);
  qdump_print_column ("output columns", merge_info_p->ls_pos_cnt, merge_info_p->ls_pos_list);
  qdump_print_column ("outer/inner indicators", merge_info_p->ls_pos_cnt, merge_info_p->ls_outer_inner_list);

  return true;
}

/*
 * qdump_print_merge_list_proc_node () -
 *   return:
 *   ptr(in):
 */
static bool
qdump_print_merge_list_proc_node (MERGELIST_PROC_NODE * node_p)
{
  fprintf (foutput, "[outer xasl:%p]\n", node_p->outer_xasl);
  if (node_p->outer_spec_list)
    {
      fprintf (foutput, "-->outer access spec:");
      qdump_print_access_spec (node_p->outer_spec_list);
      fprintf (foutput, "\n");
    }

  if (node_p->outer_val_list)
    {
      fprintf (foutput, "-->outer val_list:");
      qdump_print_value_list (node_p->outer_val_list);
      fprintf (foutput, "\n");
    }

  fprintf (foutput, "[inner xasl:%p]\n", node_p->inner_xasl);

  if (node_p->inner_spec_list)
    {
      fprintf (foutput, "-->inner access spec:");
      qdump_print_access_spec (node_p->inner_spec_list);
      fprintf (foutput, "\n");
    }

  if (node_p->inner_val_list)
    {
      fprintf (foutput, "-->inner val_list:");
      qdump_print_value_list (node_p->inner_val_list);
      fprintf (foutput, "\n");
    }

  qdump_print_list_merge_info (&node_p->ls_merge);
  fprintf (foutput, "\n");

  return true;
}

static bool
qdump_print_hashjoin_proc_node (HASHJOIN_PROC_NODE * node_p)
{
  /* outer */
  fprintf (foutput, "[outer xasl:%p]", node_p->outer.xasl);
  fprintf (foutput, "\n regu_list_pred:");
  qdump_print_regu_variable_list (node_p->outer.regu_list_pred);
  fprintf (foutput, "\n");

  /* inner */
  fprintf (foutput, "[inner xasl:%p]", node_p->inner.xasl);
  fprintf (foutput, "\n regu_list_pred:");
  qdump_print_regu_variable_list (node_p->inner.regu_list_pred);
  fprintf (foutput, "\n");

  qdump_print_list_merge_info (&node_p->merge_info);
  fprintf (foutput, "\n");

  return true;
}

static bool
qdump_print_attribute (const char *action_p, int attr_count, int *attr_ids_p)
{
  int i;

  fprintf (foutput, "[number of attributes to %s:%d]", action_p, attr_count);
  fprintf (foutput, "[ID's of attributes for %s:", action_p);

  for (i = 0; i < attr_count; i++)
    {
      fprintf (foutput, "%d%c", attr_ids_p[i], i == attr_count - 1 ? ']' : ',');
    }

  return true;
}

/*
 * qdump_print_update_proc_node () -
 *   return:
 *   ptr(in):
 */
static bool
qdump_print_update_proc_node (UPDATE_PROC_NODE * node_p)
{
  int i = 0, cnt = 0, idx = 0;
  UPDDEL_CLASS_INFO *cls = NULL;

  cnt = node_p->num_classes;
  for (idx = 0; idx < cnt; idx++)
    {
      cls = node_p->classes;

      fprintf (foutput, "[number of HFID's to use:%d]", cls->num_subclasses);

      for (i = 0; i < cls->num_subclasses; ++i)
    {
      qdump_print_oid (&cls->class_oid[i]);
    }

      for (i = 0; i < cls->num_subclasses; ++i)
    {
      qdump_print_hfid (cls->class_hfid[i]);
    }

      qdump_print_attribute ("update", cls->num_attrs, cls->att_id);
    }
  fprintf (foutput, "[numer of ORDER BY keys:%d]", node_p->num_orderby_keys);

  return true;
}

/*
 * qdump_print_delete_proc_node () -
 *   return:
 *   ptr(in):
 */
static bool
qdump_print_delete_proc_node (DELETE_PROC_NODE * node_p)
{
  int i, j;
  int hfid_count = 0;
  /* actual number of HFID's is no_classes + number of subclasses for each class */


  for (i = 0; i < node_p->num_classes; ++i)
    {
      hfid_count += node_p->classes[i].num_subclasses;
    }

  fprintf (foutput, "[number of HFID's to use:%d]", hfid_count);

  for (i = 0; i < node_p->num_classes; ++i)
    {
      for (j = 0; j < node_p->classes[i].num_subclasses; ++j)
    {
      qdump_print_oid (&node_p->classes[i].class_oid[j]);
    }

      for (j = 0; j < node_p->classes[i].num_subclasses; ++j)
    {
      qdump_print_hfid (node_p->classes[i].class_hfid[j]);
    }
    }

  return true;
}

/*
 * qdump_print_insert_proc_node () -
 *   return:
 *   ptr(in):
 */
static bool
qdump_print_insert_proc_node (INSERT_PROC_NODE * node_p)
{
  fprintf (foutput, "class oid[%d %d %d]", node_p->class_oid.pageid, node_p->class_oid.slotid, node_p->class_oid.volid);

  qdump_print_hfid (node_p->class_hfid);
  qdump_print_attribute ("insert", node_p->num_vals, node_p->att_id);

  return true;
}

/*
 * qdump_target_type_string () -
 *   return:
 *   type(in):
 */
static const char *
qdump_target_type_string (TARGET_TYPE type)
{
  switch (type)
    {
    case TARGET_CLASS:
      return "class";
    case TARGET_CLASS_ATTR:
      return "class_attr";
    case TARGET_LIST:
      return "list";
    case TARGET_SHOWSTMT:
      return "show";
    case TARGET_SET:
      return "set";
    case TARGET_METHOD:
      return "method";
    case TARGET_DBLINK:
      return "dblink";
    default:
      return "undefined";
    }
}

/*
 * qdump_access_method_string () -
 *   return:
 *   access(in):
 */
static const char *
qdump_access_method_string (ACCESS_METHOD access)
{
  switch (access)
    {
    case ACCESS_METHOD_SEQUENTIAL:
      return "sequential";
    case ACCESS_METHOD_INDEX:
      return "index";
    case ACCESS_METHOD_SEQUENTIAL_RECORD_INFO:
      return "sequential record info";
    case ACCESS_METHOD_SEQUENTIAL_SAMPLING_SCAN:
      return "sequential sampling scan";
    case ACCESS_METHOD_SEQUENTIAL_PAGE_SCAN:
      return "sequential page scan";
    default:
      return "undefined";
    }
}

/*
 * qdump_print_access_spec () -
 *   return:
 *   spec_list(in):
 */
static bool
qdump_print_access_spec (ACCESS_SPEC_TYPE * spec_list_p)
{
  TARGET_TYPE type;

  if (spec_list_p == NULL)
    {
      return true;
    }

  type = spec_list_p->type;
  fprintf (foutput, " %s", qdump_target_type_string (type));

  fprintf (foutput, ",%s", qdump_access_method_string (spec_list_p->access));

  if (spec_list_p->flags & ACCESS_SPEC_FLAG_NO_PARALLEL_HEAP_SCAN)
    {
      fprintf (foutput, ",no_parallel_heap_scan");
    }

  if (IS_ANY_INDEX_ACCESS (spec_list_p->access))
    {
      if (qdump_print_index (spec_list_p->indexptr) == false)
    {
      return false;
    }
    }

  fprintf (foutput, "\n ");

  if (type == TARGET_CLASS)
    {
      qdump_print_class (&ACCESS_SPEC_CLS_SPEC (spec_list_p));
    }
  else if (type == TARGET_SET)
    {
      qdump_print_value (ACCESS_SPEC_SET_PTR (spec_list_p));
    }
  else if (type == TARGET_LIST)
    {
      qdump_print_list (&ACCESS_SPEC_LIST_SPEC (spec_list_p));
    }
  else if (type == TARGET_SHOWSTMT)
    {
      qdump_print_showstmt (&ACCESS_SPEC_SHOWSTMT_SPEC (spec_list_p));
    }


  if (spec_list_p->where_key)
    {
      fprintf (foutput, "\n      key filter:");
      qdump_print_predicate (spec_list_p->where_key);
    }

  if (spec_list_p->where_pred)
    {
      fprintf (foutput, "\n      access pred:");
      qdump_print_predicate (spec_list_p->where_pred);
    }

  if (spec_list_p->where_range)
    {
      fprintf (foutput, "\n      access range:");
      qdump_print_predicate (spec_list_p->where_range);
    }

#if defined (SERVER_MODE) || defined (SA_MODE)
  fprintf (foutput, "\n  grouped scan=%d", spec_list_p->grouped_scan);
  fprintf (foutput, ",fixed scan=%d", spec_list_p->fixed_scan);
#endif /* defined (SERVER_MODE) || defined (SA_MODE) */
  fprintf (foutput, ",single fetch=%d", spec_list_p->single_fetch);

  if (spec_list_p->s_dbval)
    {
      fprintf (foutput, "\n      s_dbval:");
      qdump_print_db_value (spec_list_p->s_dbval);
    }

  fprintf (foutput, "\n-->next access spec:");
  qdump_print_access_spec (spec_list_p->next);
  fprintf (foutput, "\n");

  return true;
}

static const char *
qdump_key_range_string (RANGE range)
{
  switch (range)
    {
    case NA_NA:
      return "N/A";
    case GE_LE:
      return "GE_LE";
    case GE_LT:
      return "GE_LT";
    case GT_LE:
      return "GT_LE";
    case GT_LT:
      return "GT_LT";
    case GE_INF:
      return "GE_INF";
    case GT_INF:
      return "GT_INF";
    case INF_LT:
      return "INF_LT";
    case INF_LE:
      return "INF_LE";
    case INF_INF:
      return "INF_INF";
    case EQ_NA:
      return "EQ";
    default:
      return "undefined";
    }
}

/*
 * qdump_print_key_info () -
 *   return:
 *   key_info(in):
 */
static bool
qdump_print_key_info (KEY_INFO * key_info_p)
{
  int i;

  fprintf (foutput, "<key cnt:%d>", key_info_p->key_cnt);
  fprintf (foutput, "key ranges:");
  for (i = 0; i < key_info_p->key_cnt; i++)
    {
      fprintf (foutput, "<%s>", qdump_key_range_string (key_info_p->key_ranges[i].range));

      fprintf (foutput, "[");
      if (!qdump_print_value (key_info_p->key_ranges[i].key1))
    {
      return false;
    }

      fprintf (foutput, "][");

      if (!qdump_print_value (key_info_p->key_ranges[i].key2))
    {
      return false;
    }
      fprintf (foutput, "]");
    }
  fprintf (foutput, "<is constant:%d>", key_info_p->is_constant);
  fprintf (foutput, "<is user given keylimit:%d>", key_info_p->is_user_given_keylimit);
  fprintf (foutput, "<reset:%d>", key_info_p->key_limit_reset);

  fprintf (foutput, " key limit: [");
  qdump_print_value (key_info_p->key_limit_l);
  fprintf (foutput, "][");
  qdump_print_value (key_info_p->key_limit_u);
  fprintf (foutput, "]");

  return true;
}

static const char *
qdump_range_type_string (RANGE_TYPE range_type)
{
  switch (range_type)
    {
    case R_KEY:
      return "R_KEY";
    case R_RANGE:
      return "R_RANGE";
    case R_KEYLIST:
      return "R_KEYLIST";
    case R_RANGELIST:
      return "R_RANGELIST";
    default:
      return "undefined";
    }
}

/*
 * qdump_print_index () -
 *   return:
 *   index_ptr(in):
 */
static bool
qdump_print_index (INDX_INFO * index_p)
{
  if (index_p == NULL)
    {
      return true;
    }

  fprintf (foutput, "<index id:");
  if (!qdump_print_btid (index_p->btid))
    {
      return false;
    }
  fprintf (foutput, ">");

  fprintf (foutput, "<%s>", qdump_range_type_string (index_p->range_type));

  fprintf (foutput, "key info:");
  if (!qdump_print_key_info (&index_p->key_info))
    {
      return false;
    }
  fprintf (foutput, ">");

  return true;
}

/*
 * qdump_print_btid () -
 *   return:
 *   id(in):
 */
static bool
qdump_print_btid (BTID id)
{
  fprintf (foutput, "<Btree:(%d;%d;%d)>", id.vfid.fileid, id.vfid.volid, id.root_pageid);
  return true;
}

/*
 * qdump_print_class () -
 *   return:
 *   ptr(in):
 */
static bool
qdump_print_class (CLS_SPEC_TYPE * class_p)
{
  qdump_print_hfid (class_p->hfid);
  fprintf (foutput, "oid[%d %d %d]", class_p->cls_oid.pageid, class_p->cls_oid.slotid, class_p->cls_oid.volid);
  fprintf (foutput, "\n regu_list_key:");
  qdump_print_regu_variable_list (class_p->cls_regu_list_key);
  fprintf (foutput, "\n regu_list_pred:");
  qdump_print_regu_variable_list (class_p->cls_regu_list_pred);
  fprintf (foutput, "\n regu_list_rest:");
  qdump_print_regu_variable_list (class_p->cls_regu_list_rest);
  return true;
}

/*
 * qdump_print_hfid () -
 *   return:
 *   id(in):
 */
static bool
qdump_print_hfid (HFID id)
{
  fprintf (foutput, "hfid:");
  qdump_print_vfid (id.vfid);
  fprintf (foutput, ":%d", id.hpgid);
  return true;
}

/*
 * qdump_print_vfid () -
 *   return:
 *   id(in):
 */
static bool
qdump_print_vfid (VFID id)
{
  fprintf (foutput, "vfid(%d;%d)", id.fileid, id.volid);
  return true;
}

/*
 * qdump_print_list () -
 *   return:
 *   ptr(in):
 */
static bool
qdump_print_list (LIST_SPEC_TYPE * list_p)
{
  fprintf (foutput, "list=");
  fprintf (foutput, "xasl:%p", list_p->xasl_node);
  fprintf (foutput, "\n regu_list_pred:");
  qdump_print_regu_variable_list (list_p->list_regu_list_pred);
  fprintf (foutput, "\n regu_list_rest:");
  qdump_print_regu_variable_list (list_p->list_regu_list_rest);
  fprintf (foutput, "\n regu_list_build:");
  qdump_print_regu_variable_list (list_p->list_regu_list_build);
  fprintf (foutput, "\n regu_list_probe:");
  qdump_print_regu_variable_list (list_p->list_regu_list_probe);
  return true;
}

/*
 * qdump_print_showstmt () -
 *   return:
 *   ptr(in):
 */
static bool
qdump_print_showstmt (SHOWSTMT_SPEC_TYPE * showstmt_p)
{
  fprintf (foutput, "show_type: %d", showstmt_p->show_type);
  fprintf (foutput, "\n show_args: ");
  qdump_print_regu_variable_list (showstmt_p->arg_list);
  return true;
}

/*
 * qdump_print_outlist () -
 *   return:
 *   title(in):
 *   outlist(in):
 */
static bool
qdump_print_outlist (const char *title_p, OUTPTR_LIST * outlist_p)
{
  REGU_VARIABLE_LIST nextptr;

  if (outlist_p == NULL)
    {
      return true;
    }

  nextptr = outlist_p->valptrp;
  fprintf (foutput, "-->%s:", title_p);

  while (nextptr)
    {
      fprintf (foutput, "[addr:%p]", &nextptr->value);
      if (!qdump_print_value (&nextptr->value))
    {
      return false;
    }

      fprintf (foutput, "; ");
      nextptr = nextptr->next;
    }

  fprintf (foutput, "\n");
  return true;
}

/*
 * qdump_print_list_id () -
 *   return:
 *   idptr(in):
 */
static bool
qdump_print_list_id (QFILE_LIST_ID * list_id_p)
{
  if (list_id_p == NULL)
    {
      return true;
    }

  fprintf (foutput, "(address:%p)", list_id_p);
  fprintf (foutput, "(type_list:");

  if (!qdump_print_type_list (&list_id_p->type_list))
    {
      return false;
    }

  fprintf (foutput, ")(tuple_cnt:%lld)", (long long) list_id_p->tuple_cnt);
  return true;
}

/*
 * qdump_print_type_list () -
 *   return:
 *   typeptr(in):
 */
static bool
qdump_print_type_list (QFILE_TUPLE_VALUE_TYPE_LIST * type_list_p)
{
  fprintf (foutput, "<type_cnt:%d>", type_list_p->type_cnt);
  if (!qdump_print_domain_list (type_list_p->type_cnt, type_list_p->domp))
    {
      return false;
    }
  return true;
}

/*
 * qdump_print_domain_list () -
 *   return:
 *   cnt(in):
 *   ptr(in):
 */
static bool
qdump_print_domain_list (int cnt, TP_DOMAIN ** domains_p)
{
  int i;

  if (domains_p == NULL)
    {
      return true;
    }

  for (i = 0; i < cnt; i++)
    {
      fprintf (foutput, "%s; ", qdump_data_type_string (TP_DOMAIN_TYPE (domains_p[i])));
    }

  return true;
}

/*
 * qdump_print_sort_list () -
 *   return:
 *   sorting_list(in):
 */
static bool
qdump_print_sort_list (SORT_LIST * sort_list_p)
{
  if (sort_list_p == NULL)
    {
      return true;
    }

  fprintf (foutput, "<sorting field(POS):");
  if (!qdump_print_tuple_value_position (sort_list_p->pos_descr))
    {
      return false;
    }

  fprintf (foutput, ">");
  fprintf (foutput, "<sorting order:");
  if (sort_list_p->s_order == S_ASC)
    {
      fprintf (foutput, "ascending>");
    }
  else
    {
      fprintf (foutput, "descending>");
    }

  if (!qdump_print_sort_list (sort_list_p->next))
    {
      return false;
    }
  return true;
}

/*
 * qdump_print_attribute_id () -
 *   return:
 *   attr(in):
 */
static bool
qdump_print_attribute_id (ATTR_DESCR attr)
{
  fprintf (foutput, "attr_id:%d|db_type:", (int) attr.id);
  fprintf (foutput, "%s", qdump_data_type_string (attr.type));

  return true;
}

/*
 * qdump_print_tuple_value_position () -
 *   return:
 *   pos(in):
 */
static bool
qdump_print_tuple_value_position (QFILE_TUPLE_VALUE_POSITION pos)
{
  fprintf (foutput, "(position %d) (db_type ", pos.pos_no);
  fprintf (foutput, "%s", qdump_data_type_string (TP_DOMAIN_TYPE (pos.dom)));
  fprintf (foutput, ")");

  return true;
}

/*
 * qdump_print_value_list () -
 *   return:
 *   vallist(in):
 */
static bool
qdump_print_value_list (VAL_LIST * value_list_p)
{
  QPROC_DB_VALUE_LIST dbval_list;

  if (value_list_p == NULL)
    {
      return true;
    }

  dbval_list = value_list_p->valp;
  fprintf (foutput, "(values %d <", value_list_p->val_cnt);

  while (dbval_list != NULL)
    {
      fprintf (foutput, "addr:%p|", dbval_list->val);
      fprintf (foutput, "type:%s", qdump_data_type_string (DB_VALUE_DOMAIN_TYPE (dbval_list->val)));
      fprintf (foutput, "|value:");

      if (!qdump_print_db_value (dbval_list->val))
    {
      return false;
    }

      fprintf (foutput, "; ");
      dbval_list = dbval_list->next;
    }

  fprintf (foutput, ">)");
  return true;
}

/*
 * qdump_print_regu_variable_list () -
 *   return:
 *   varlist(in):
 */
static bool
qdump_print_regu_variable_list (REGU_VARIABLE_LIST var_list)
{
  if (var_list == NULL)
    {
      return true;
    }

  while (var_list != NULL)
    {
      if (!qdump_print_value (&var_list->value))
    {
      return false;
    }

      fprintf (foutput, "; ");
      var_list = var_list->next;
    }

  return true;
}

/*
 * qdump_option_string () -
 *   return:
 *   option(in):
 */
static const char *
qdump_option_string (int option)
{
  switch (option)
    {
    case Q_DISTINCT:
      return "DISTINCT";
    case Q_ALL:
      return "ALL";
    default:
      return "undefined";
    }
}

/*
 * qdump_print_db_value () -
 *   return:
 *   value(in):
 */
static bool
qdump_print_db_value (DB_VALUE * value_p)
{
  db_value_print (value_p);
  return true;
}

const char *
qdump_operator_type_string (OPERATOR_TYPE optype)
{
  switch (optype)
    {
    case T_TO_CHAR:
      return "TO_CHAR";
      /* TODO - fill */
    default:
      return NULL;
    }
}

static const char *
qdump_regu_type_string (REGU_DATATYPE type)
{
  switch (type)
    {
    case TYPE_DBVAL:
      return "TYPE_DBVAL";
    case TYPE_CONSTANT:
      return "TYPE_CONSTANT";
    case TYPE_ORDERBY_NUM:
      return "TYPE_ORDERBY_NUM";
    case TYPE_INARITH:
      return "TYPE_INARITH";
    case TYPE_OUTARITH:
      return "TYPE_OUTARITH";
    case TYPE_ATTR_ID:
      return "TYPE_ATTR_ID";
    case TYPE_CLASS_ATTR_ID:
      return "TYPE_CLASS_ATTR_ID";
    case TYPE_SHARED_ATTR_ID:
      return "TYPE_SHARED_ATTR_ID";
    case TYPE_POSITION:
      return "TYPE_POSITION";
    case TYPE_LIST_ID:
      return "TYPE_LIST_ID";
    case TYPE_POS_VALUE:
      return "TYPE_POS_VALUE";
    case TYPE_OID:
      return "TYPE_OID";
    case TYPE_CLASSOID:
      return "TYPE_CLASSOID";
    case TYPE_FUNC:
      return "TYPE_FUNC";
    case TYPE_REGUVAL_LIST:
      return "TYPE_REGUVAL_LIST";
    case TYPE_REGU_VAR_LIST:
      return "TYPE_REGU_VAR_LIST";
    case TYPE_SP:
      return "TYPE_SP";
    default:
      return "undefined";
    }
}

/*
 * qdump_print_regu_type () -
 *   return:
 *   value(in):
 */
static bool
qdump_print_regu_type (REGU_VARIABLE * value_p)
{
  DB_TYPE type;

  if (value_p->type == TYPE_DBVAL)
    {
      type = DB_VALUE_DOMAIN_TYPE (&(value_p->value.dbval));
      fprintf (foutput, "%s", qdump_data_type_string (type));
    }
  else
    {
      fprintf (foutput, "%s", qdump_regu_type_string (value_p->type));
    }

  return true;
}

/*
 * qdump_data_type_string () -
 *   return:
 *   type(in):
 */

static const char *
qdump_data_type_string (DB_TYPE type)
{
  switch (type)
    {
    case DB_TYPE_NULL:
      return "NULL";
    case DB_TYPE_INTEGER:
      return "INTEGER";
    case DB_TYPE_BIGINT:
      return "BIGINT";
    case DB_TYPE_FLOAT:
      return "FLOAT";
    case DB_TYPE_DOUBLE:
      return "DOUBLE";
    case DB_TYPE_VARCHAR:
      return "VARCHAR";
    case DB_TYPE_OBJECT:
      return "OBJECT";
    case DB_TYPE_SET:
      return "SET";
    case DB_TYPE_MULTISET:
      return "MULTISET";
    case DB_TYPE_SEQUENCE:
      return "SEQUENCE";
    case DB_TYPE_BLOB:
      return "BLOB";
    case DB_TYPE_CLOB:
      return "CLOB";
    case DB_TYPE_TIME:
      return "TIME";
    case DB_TYPE_TIMESTAMP:
      return "TIMESTAMP";
    case DB_TYPE_TIMESTAMPTZ:
      return "TIMESTAMPTZ";
    case DB_TYPE_TIMESTAMPLTZ:
      return "TIMESTAMPLTZ";
    case DB_TYPE_DATETIME:
      return "DATETIME";
    case DB_TYPE_DATETIMETZ:
      return "DATETIMETZ";
    case DB_TYPE_DATETIMELTZ:
      return "DATETIMELTZ";
    case DB_TYPE_DATE:
      return "DATE";
    case DB_TYPE_MONETARY:
      return "MONETARY";
    case DB_TYPE_VARIABLE:
      return "VARIABLE";
    case DB_TYPE_SUB:
      return "SUB";
    case DB_TYPE_POINTER:
      return "POINTER";
    case DB_TYPE_ERROR:
      return "ERROR";
    case DB_TYPE_SMALLINT:
      return "SMALLINT";
    case DB_TYPE_VOBJ:
      return "VOBJ";
    case DB_TYPE_OID:
      return "OID";
    case DB_TYPE_NUMERIC:
      return "NUMERIC";
    case DB_TYPE_BIT:
      return "BIT";
    case DB_TYPE_VARBIT:
      return "VARBIT";
    case DB_TYPE_CHAR:
      return "CHAR";
    case DB_TYPE_DB_VALUE:
      return "DB_VALUE";
    case DB_TYPE_RESULTSET:
      return "DB_RESULTSET";
    case DB_TYPE_MIDXKEY:
      return "DB_MIDXKEY";
    case DB_TYPE_TABLE:
      return "DB_TABLE";
    case DB_TYPE_ENUMERATION:
      return "ENUM";
    case DB_TYPE_JSON:
      return "JSON";
    default:
      return "[***UNKNOWN***]";
    }
}

/*
 * qdump_print_value () -
 *   return:
 *   value(in):
 */
static bool
qdump_print_value (REGU_VARIABLE * value_p)
{
  if (value_p == NULL)
    {
      fprintf (foutput, "NIL");
      return true;
    }

  if (REGU_VARIABLE_IS_FLAGED (value_p, REGU_VARIABLE_HIDDEN_COLUMN))
    {
      fprintf (foutput, "[HIDDEN_COLUMN]");
    }
  if (value_p->xasl)
    {
      fprintf (foutput, "[xasl:%p]", value_p->xasl);
    }

  fprintf (foutput, "[");
  qdump_print_value_type_addr (value_p);
  fprintf (foutput, "]");

  switch (value_p->type)
    {
    case TYPE_DBVAL:
      fprintf (foutput, "[type:%s]", qdump_data_type_string (value_p->domain->type->id));
      qdump_print_db_value (&value_p->value.dbval);
      return true;

    case TYPE_CONSTANT:
    case TYPE_ORDERBY_NUM:
      fprintf (foutput, "[type:%s]", qdump_data_type_string (value_p->domain->type->id));
      qdump_print_db_value (value_p->value.dbvalptr);
      return true;

    case TYPE_INARITH:
    case TYPE_OUTARITH:
      if (!qdump_print_arith (ARITH_EXP, (void *) value_p->value.arithptr))
    {
      return false;
    }
      return true;
    case TYPE_ATTR_ID:
      if (!qdump_print_attribute_id (value_p->value.attr_descr))
    {
      return false;
    }
      return true;

    case TYPE_LIST_ID:
      if (value_p->value.srlist_id->sorted)
    {
      fprintf (foutput, "[SORTED]");
    }
      else
    {
      fprintf (foutput, "[NOT SORTED]");
    }

      if (!qdump_print_list_id (value_p->value.srlist_id->list_id))
    {
      return false;
    }

      return true;

    case TYPE_POSITION:
      if (!qdump_print_tuple_value_position (value_p->value.pos_descr))
    {
      return false;
    }
#if !defined (NDEBUG)
      qdump_print_db_value (value_p->vfetch_to);
#endif
      return true;

    case TYPE_POS_VALUE:
    case TYPE_OID:
      fprintf (foutput, "[type:%s]", qdump_data_type_string (value_p->domain->type->id));
      return true;

    case TYPE_FUNC:
      qdump_print_function_value (value_p);
      return true;

    case TYPE_SP:
      fprintf (foutput, "[TYPE_SP]");
      fprintf (foutput, "[%s]", value_p->value.sp_ptr->sig->name);
      fprintf (foutput, "args-->");
      qdump_print_regu_variable_list (value_p->value.sp_ptr->args);
      return true;

    default:
      return true;
    }
}

/*
 * qdump_print_function_value () -
 *   return:
 *   regu(in):
 */
static bool
qdump_print_function_value (REGU_VARIABLE * regu_var_p)
{
  if (regu_var_p == NULL)
    {
      fprintf (foutput, "NIL");
      return true;
    }

  if (REGU_VARIABLE_IS_FLAGED (regu_var_p, REGU_VARIABLE_HIDDEN_COLUMN))
    {
      fprintf (foutput, "[HIDDEN_COLUMN]");
    }

  fprintf (foutput, "[TYPE_FUNC]");
  fprintf (foutput, "[%s]", fcode_get_uppercase_name (regu_var_p->value.funcp->ftype));
  fprintf (foutput, "operand-->");
  qdump_print_regu_variable_list (regu_var_p->value.funcp->operand);

  return true;
}

/*
 * qdump_print_value_type_addr () -
 *   return:
 *   value(in):
 */
static bool
qdump_print_value_type_addr (REGU_VARIABLE * regu_var_p)
{
  void *addr;

  qdump_print_regu_type (regu_var_p);

  switch (regu_var_p->type)
    {
    case TYPE_DBVAL:
      addr = (void *) &regu_var_p->value.dbval;
      break;

    case TYPE_CONSTANT:
    case TYPE_ORDERBY_NUM:
      addr = (void *) regu_var_p->value.dbvalptr;
      break;

    case TYPE_INARITH:
    case TYPE_OUTARITH:
      addr = (void *) regu_var_p->value.arithptr;
      break;

    case TYPE_LIST_ID:
      addr = (void *) regu_var_p->value.srlist_id->list_id;
      break;

    case TYPE_ATTR_ID:
    case TYPE_SHARED_ATTR_ID:
    case TYPE_CLASS_ATTR_ID:
      addr = (void *) &regu_var_p->value.attr_descr;
      break;

    case TYPE_POSITION:
      addr = (void *) &regu_var_p->value.pos_descr;
      break;

    case TYPE_POS_VALUE:
      addr = (void *) &regu_var_p->value.val_pos;
      break;

    case TYPE_OID:
    case TYPE_CLASSOID:
    case TYPE_FUNC:
      return true;

    default:
      return false;
    }

  fprintf (foutput, ":%p", addr);

  return true;
}


/*
 * qdump_print_oid () -
 *   return:
 *   id(in):
 */
static bool
qdump_print_oid (OID * oid_p)
{
  if (oid_p)
    {
      fprintf (foutput, "[OID:%d,%d,%d]", oid_p->pageid, oid_p->slotid, oid_p->volid);
    }

  return true;
}

static bool
qdump_print_comp_eval_term (EVAL_TERM * term_p)
{
  COMP_EVAL_TERM *et_comp_p = &term_p->et.et_comp;

  fprintf (foutput, "[TYPE:%s]", qdump_data_type_string (et_comp_p->type));

  qdump_print_value (et_comp_p->lhs);
  fprintf (foutput, " %s ", qdump_relation_operator_string (et_comp_p->rel_op));

  if (et_comp_p->rhs != NULL)
    {
      qdump_print_value (et_comp_p->rhs);
    }

  return true;
}

static bool
qdump_print_alsm_eval_term (EVAL_TERM * term_p)
{
  ALSM_EVAL_TERM *et_alsm_p = &term_p->et.et_alsm;

  fprintf (foutput, "[ITEM TYPE:%s]", qdump_data_type_string (et_alsm_p->item_type));

  qdump_print_value (et_alsm_p->elem);
  fprintf (foutput, " %s ", qdump_relation_operator_string (et_alsm_p->rel_op));

  switch (et_alsm_p->eq_flag)
    {
    case F_SOME:
      fprintf (foutput, "some ");
      break;
    case F_ALL:
      fprintf (foutput, "all ");
      break;
    default:
      return false;
    }

  qdump_print_value (et_alsm_p->elemset);

  return true;
}

static bool
qdump_print_like_eval_term (EVAL_TERM * term_p)
{
  LIKE_EVAL_TERM *et_like_p = &term_p->et.et_like;

  fprintf (foutput, "SOURCE");
  qdump_print_value (et_like_p->src);
  fprintf (foutput, "PATTERN:");

  if (!qdump_print_value (et_like_p->pattern))
    {
      return false;
    }

  if (et_like_p->esc_char)
    {
      if (!qdump_print_value (et_like_p->esc_char))
    {
      return false;
    }
    }

  return true;
}

static bool
qdump_print_rlike_eval_term (EVAL_TERM * term_p)
{
  RLIKE_EVAL_TERM *et_rlike_p = &term_p->et.et_rlike;

  fprintf (foutput, "SOURCE");
  qdump_print_value (et_rlike_p->src);
  fprintf (foutput, (et_rlike_p->case_sensitive->value.dbval.data.i
             ? "PATTERN (CASE SENSITIVE):" : "PATTERN (CASE INSENSITIVE):"));

  if (!qdump_print_value (et_rlike_p->pattern))
    {
      return false;
    }

  return true;
}

static bool
qdump_print_eval_term (PRED_EXPR * pred_p)
{
  EVAL_TERM *term = &pred_p->pe.m_eval_term;

  switch (term->et_type)
    {
    case T_COMP_EVAL_TERM:
      return qdump_print_comp_eval_term (term);

    case T_ALSM_EVAL_TERM:
      return qdump_print_alsm_eval_term (term);

    case T_LIKE_EVAL_TERM:
      return qdump_print_like_eval_term (term);

    case T_RLIKE_EVAL_TERM:
      return qdump_print_rlike_eval_term (term);

    default:
      return false;
    }
}

/*
 * qdump_print_term () -
 *   return:
 *   term(in):
 */
static bool
qdump_print_term (PRED_EXPR * pred_p)
{
  if (pred_p == NULL)
    {
      return true;
    }

  switch (pred_p->type)
    {
    case T_EVAL_TERM:
      return qdump_print_eval_term (pred_p);

    case T_NOT_TERM:
      fprintf (foutput, "(NOT ");

      if (!qdump_print_predicate (pred_p->pe.m_not_term))
    {
      return false;
    }
      fprintf (foutput, ")");

      return true;

    default:
      return false;
    }
}

static const char *
qdump_bool_operator_string (BOOL_OP bool_op)
{
  if (bool_op == B_AND)
    {
      return "AND";
    }
  else if (bool_op == B_OR)
    {
      return "OR";
    }
  else if (bool_op == B_XOR)
    {
      return "XOR";
    }
  else if (bool_op == B_IS)
    {
      return "IS";
    }
  else if (bool_op == B_IS_NOT)
    {
      return "IS NOT";
    }
  else
    {
      return "undefined";
    }
}

static bool
qdump_print_lhs_predicate (PRED_EXPR * pred_p)
{
  fprintf (foutput, "(");

  if (!qdump_print_predicate (pred_p->pe.m_pred.lhs))
    {
      return false;
    }

  fprintf (foutput, " %s ", qdump_bool_operator_string (pred_p->pe.m_pred.bool_op));

  return true;
}

/*
 * qdump_print_predicate () -
 *   return:
 *   predptr(in):
 */
static bool
qdump_print_predicate (PRED_EXPR * pred_p)
{
  int parn_cnt;

  if (pred_p == NULL)
    {
      return true;
    }

  switch (pred_p->type)
    {
    case T_PRED:
      if (qdump_print_lhs_predicate (pred_p) == false)
    {
      return false;
    }

      parn_cnt = 1;

      /* Traverse right-linear chains of AND/OR terms */
      for (pred_p = pred_p->pe.m_pred.rhs; pred_p->type == T_PRED; pred_p = pred_p->pe.m_pred.rhs)
    {
      if (qdump_print_lhs_predicate (pred_p) == false)
        {
          return false;
        }

      parn_cnt++;
    }

      /* rhs */
      switch (pred_p->type)
    {
    case T_EVAL_TERM:
    case T_NOT_TERM:
      if (!qdump_print_term (pred_p))
        {
          return false;
        }
      break;
    default:
      return false;
    }

      while (parn_cnt > 0)
    {
      fprintf (foutput, ")");
      parn_cnt--;
    }

      return true;

    case T_EVAL_TERM:
    case T_NOT_TERM:
      return qdump_print_term (pred_p);

    default:
      return false;
    }
}

/*
 * qdump_relation_operator_string () -
 *   return:
 *   op(in):
 */
static const char *
qdump_relation_operator_string (int op)
{
  switch (op)
    {
    case R_EQ:
      return "=";
    case R_NE:
      return "<>";
    case R_GT:
      return ">";
    case R_GE:
      return ">=";
    case R_LT:
      return "<";
    case R_LE:
      return "<=";
    case R_NULL:
      return "IS NULL";
    case R_EXISTS:
      return "EXISTS";
    case R_NULLSAFE_EQ:
      return "<=>";
    default:
      return "undefined";
    }
}

static const char *
qdump_arith_operator_string (OPERATOR_TYPE opcode)
{
  switch (opcode)
    {
    case T_ADD:
      return "+";
    case T_SUB:
      return "-";
    case T_MUL:
      return "*";
    case T_DIV:
      return "/";
    case T_STRCAT:
      return "||";
    case T_BIT_NOT:
      return "~";
    case T_BIT_AND:
      return "&";
    case T_BIT_OR:
      return "|";
    case T_BIT_XOR:
      return "^";
    case T_BITSHIFT_LEFT:
      return "<<";
    case T_BITSHIFT_RIGHT:
      return ">>";
    case T_INTDIV:
      return "div";
    case T_INTMOD:
      return "mod";
    default:
      return "undefined";
    }
}

static bool
qdump_print_arith_expression (ARITH_TYPE * arith_p)
{
  fprintf (foutput, "[%s]", qdump_data_type_string (DB_VALUE_DOMAIN_TYPE (arith_p->value)));

  if (arith_p->opcode == T_UNMINUS
#if defined(ENABLE_UNUSED_FUNCTION)
      || arith_p->opcode == T_UNPLUS
#endif /* ENABLE_UNUSED_FUNCTION */
    )
    {
      fprintf (foutput, "(");
      if (arith_p->opcode == T_UNMINUS)
    {
      fprintf (foutput, "-");
    }
#if defined(ENABLE_UNUSED_FUNCTION)
      else
    {
      fprintf (foutput, "+");
    }
#endif /* ENABLE_UNUSED_FUNCTION */

      if (!qdump_print_value (arith_p->rightptr))
    {
      return false;
    }
      fprintf (foutput, ")");
    }
  else
    {
      /* binary op */

      fprintf (foutput, "(");
      if (!qdump_print_value (arith_p->leftptr))
    {
      return false;
    }

      fprintf (foutput, "%s", qdump_arith_operator_string (arith_p->opcode));

      if (!qdump_print_value (arith_p->rightptr))
    {
      return false;
    }
      fprintf (foutput, ")");
    }

  return true;
}

static bool
qdump_print_aggregate_expression (AGGREGATE_TYPE * aggptr)
{
  fprintf (foutput, "[%s]", qdump_data_type_string (DB_VALUE_DOMAIN_TYPE (aggptr->accumulator.value)));

  fprintf (foutput, "%s(", fcode_get_uppercase_name (aggptr->function));

  fprintf (foutput, "%s ", qdump_option_string (aggptr->option));

  REGU_VARIABLE_LIST operand = NULL;
  for (operand = aggptr->operands; operand != NULL; operand = operand->next)
    {
      if (!qdump_print_value (&operand->value))
    {
      return false;
    }
    }

  if (!qdump_print_list_id (aggptr->list_id))
    {
      return false;
    }

  fprintf (foutput, "(optimize:%d)", aggptr->flag.agg_optimized);

  if (!qdump_print_btid (aggptr->btid))
    {
      return false;
    }

  fprintf (foutput, ")");

  if (aggptr->next != NULL)
    {
      fprintf (foutput, "; ");
      if (!qdump_print_arith (AGG_EXP, aggptr->next))
    {
      return false;
    }
    }

  return true;
}

/*
 * qdump_print_arith () -
 *   return:
 *   type(in):
 *   ptr(in):
 */
static bool
qdump_print_arith (int type, void *ptr)
{
  if (ptr == NULL)
    {
      return true;
    }

  if (type == ARITH_EXP)
    {
      return qdump_print_arith_expression ((ARITH_TYPE *) ptr);
    }
  else if (type == AGG_EXP)
    {
      return qdump_print_aggregate_expression ((AGGREGATE_TYPE *) ptr);
    }

  return true;
}

#if defined(CUBRID_DEBUG)
/*
 * qdump_check_xasl_tree () -
 *   return:
 *   xasl(in):
 */
bool
qdump_check_xasl_tree (xasl_node * xasl_p)
{
  QDUMP_XASL_CHECK_NODE *chk_nodes[HASH_NUMBER] = { NULL };

  if (xasl_p == NULL)
    {
      return true;
    }

  /* recursively check the tree */
  qdump_check_node (xasl_p, chk_nodes);

  /* print any inconsistencies in the tree */
  return qdump_print_inconsistencies (chk_nodes);
}

/*
 * qdump_find_check_node_for () -
 *   return:
 *   xasl(in):
 *   chk_nodes(in):
 */
static QDUMP_XASL_CHECK_NODE *
qdump_find_check_node_for (XASL_NODE * xasl_p, QDUMP_XASL_CHECK_NODE * chk_nodes[HASH_NUMBER])
{
  UINTPTR access_node_hash;
  QDUMP_XASL_CHECK_NODE *check_node_p;

  access_node_hash = (UINTPTR) xasl_p % HASH_NUMBER;

  for (check_node_p = chk_nodes[access_node_hash]; check_node_p; check_node_p = check_node_p->next)
    {
      if (check_node_p->xasl_addr == (UINTPTR) xasl_p)
    {
      break;
    }
    }

  if (!check_node_p)
    {
      /* forward reference */
      check_node_p = (QDUMP_XASL_CHECK_NODE *) malloc (sizeof (QDUMP_XASL_CHECK_NODE));
      if (check_node_p == NULL)
    {
      return NULL;
    }
      check_node_p->next = chk_nodes[access_node_hash];
      chk_nodes[access_node_hash] = check_node_p;
      check_node_p->xasl_addr = (UINTPTR) xasl_p;
      check_node_p->xasl_type = xasl_p->type;
      check_node_p->referenced = 0;
      check_node_p->reachable = 0;
    }

  return check_node_p;
}

/*
 * qdump_check_node () -
 *   return:
 *   xasl(in):
 *   chk_nodes(in):
 */
static void
qdump_check_node (XASL_NODE * xasl_p, QDUMP_XASL_CHECK_NODE * chk_nodes[HASH_NUMBER])
{
  UINTPTR addr_hash;
  QDUMP_XASL_CHECK_NODE *check_node_p, *check_node1_p;
  ACCESS_SPEC_TYPE *spec_p;

  if (!xasl_p)
    {
      return;
    }

  /* get hash number */
  addr_hash = (UINTPTR) xasl_p % HASH_NUMBER;

  check_node_p = qdump_find_check_node_for (xasl_p, chk_nodes);
  if (check_node_p == NULL)
    {
      return;
    }

  if (check_node_p->reachable)
    {
      return;
    }

  check_node_p->reachable = 1;

  /*
   * Mark the node its access spec references.  You may need to create
   * it if it is a forward reference.
   */
  for (spec_p = xasl_p->spec_list; spec_p; spec_p = spec_p->next)
    {
      if (spec_p->type == TARGET_LIST)
    {
      check_node1_p = qdump_find_check_node_for (ACCESS_SPEC_XASL_NODE (spec_p), chk_nodes);
      /* mark as referenced */
      if (check_node1_p)
        {
          check_node1_p->referenced = 1;
        }
    }
    }

  /* recursively check the children of this node */
  switch (xasl_p->type)
    {
    case UNION_PROC:
    case DIFFERENCE_PROC:
    case INTERSECTION_PROC:
      check_node1_p = qdump_find_check_node_for (xasl_p->proc.union_.left, chk_nodes);
      if (check_node1_p)
    {
      check_node1_p->referenced = 1;
    }

      check_node1_p = qdump_find_check_node_for (xasl_p->proc.union_.right, chk_nodes);
      if (check_node1_p)
    {
      check_node1_p->referenced = 1;
    }
      break;

    case MERGELIST_PROC:
      check_node1_p = qdump_find_check_node_for (xasl_p->proc.mergelist.outer_xasl, chk_nodes);
      if (check_node1_p)
    {
      check_node1_p->referenced = 1;
    }

      check_node1_p = qdump_find_check_node_for (xasl_p->proc.mergelist.inner_xasl, chk_nodes);
      if (check_node1_p)
    {
      check_node1_p->referenced = 1;
    }
      break;

    case HASHJOIN_PROC:
      check_node1_p = qdump_find_check_node_for (xasl_p->proc.hashjoin.outer.xasl, chk_nodes);
      if (check_node1_p)
    {
      check_node1_p->referenced = 1;
    }

      check_node1_p = qdump_find_check_node_for (xasl_p->proc.hashjoin.inner.xasl, chk_nodes);
      if (check_node1_p)
    {
      check_node1_p->referenced = 1;
    }
      break;

    case BUILDLIST_PROC:
      qdump_check_node (xasl_p->proc.buildlist.eptr_list, chk_nodes);

    default:
      break;
    }

  qdump_check_node (xasl_p->aptr_list, chk_nodes);
  qdump_check_node (xasl_p->bptr_list, chk_nodes);
  qdump_check_node (xasl_p->scan_ptr, chk_nodes);
  qdump_check_node (xasl_p->dptr_list, chk_nodes);
  qdump_check_node (xasl_p->fptr_list, chk_nodes);
  qdump_check_node (xasl_p->connect_by_ptr, chk_nodes);
  qdump_check_node (xasl_p->next, chk_nodes);
}

/*
 * qdump_print_inconsistencies () -
 *   return:
 *   chk_nodes(in):
 */
static int
qdump_print_inconsistencies (QDUMP_XASL_CHECK_NODE * chk_nodes[HASH_NUMBER])
{
  int i, error = 0;
  QDUMP_XASL_CHECK_NODE *check_node_p, *tmp_node_p;

  for (i = 0; i < HASH_NUMBER; i++)
    {
      for (check_node_p = chk_nodes[i]; check_node_p; check_node_p = check_node_p->next)
    {
      /* any buildlist procs that are referenced must be reachable */
      if (check_node_p->referenced && !check_node_p->reachable)
        {
          if (!error)
        {
          fprintf (stdout, "\nSYSTEM ERROR--INCONSISTENT XASL TREE\n\n");
        }

          fprintf (stdout, "Referenced node [%lld] is not reachable in the tree\n",
               (long long) check_node_p->xasl_addr);
          error = 1;
        }
    }
    }

  /* clean up our mallocs */
  for (i = 0; i < HASH_NUMBER; i++)
    {
      for (check_node_p = chk_nodes[i]; check_node_p; /* do nothing */ )
    {
      tmp_node_p = check_node_p;
      check_node_p = check_node_p->next;
      free_and_init (tmp_node_p);
    }
    }

  if (error)
    {
      fprintf (stdout, "\n");
    }

  return !error;
}
#endif /* CUBRID_DEBUG */

static bool
qdump_print_fetch_node (XASL_NODE * xasl_p)
{
  FETCH_PROC_NODE *node_p = &xasl_p->proc.fetch;

  fprintf (foutput, "-->fetch  <addr:%p><type:", node_p->arg);
  fprintf (foutput, "%s", qdump_data_type_string (DB_VALUE_DOMAIN_TYPE (node_p->arg)));
  fprintf (foutput, ">\n fetch_res (%d)\n", (int) node_p->fetch_res);

  if (node_p->set_pred)
    {
      fprintf (foutput, "-->set predicate:");
      qdump_print_predicate (node_p->set_pred);
      fprintf (foutput, "\n");
    }

  if (node_p->ql_flag)
    {
      fprintf (foutput, "-->ql_flag on (no null paths)\n");
    }

  return true;
}

static bool
qdump_print_build_list_node (XASL_NODE * xasl_p)
{
  BUILDLIST_PROC_NODE *node_p = &xasl_p->proc.buildlist;

  if (xasl_p->outptr_list != NULL)
    {
      fprintf (foutput, "-->output columns:");
      qdump_print_db_value_array (node_p->output_columns, xasl_p->outptr_list->valptr_cnt);
      fprintf (foutput, "\n");
    }

  if (node_p->g_outptr_list)
    {
      qdump_print_outlist ("group by output ptrlist", node_p->g_outptr_list);
      fprintf (foutput, "\n");
    }

  if (node_p->groupby_list)
    {
      fprintf (foutput, "-->group by list:");
      qdump_print_sort_list (node_p->groupby_list);
      fprintf (foutput, "\n");
    }

  if (node_p->g_regu_list)
    {
      fprintf (foutput, "-->group by regu list:");
      qdump_print_regu_variable_list (node_p->g_regu_list);
      fprintf (foutput, "\n");
    }

  if (node_p->g_val_list)
    {
      fprintf (foutput, "-->group by val_list:");
      qdump_print_value_list (node_p->g_val_list);
      fprintf (foutput, "\n");
    }

  if (node_p->g_having_pred)
    {
      fprintf (foutput, "-->having predicate:");
      qdump_print_predicate (node_p->g_having_pred);
      fprintf (foutput, "\n");
    }

  if (node_p->g_grbynum_val)
    {
      fprintf (foutput, "-->grbynum val:");
      fprintf (foutput, "<addr:%p|", node_p->g_grbynum_val);
      fprintf (foutput, "type:%s", qdump_data_type_string (DB_VALUE_DOMAIN_TYPE (node_p->g_grbynum_val)));
      fprintf (foutput, "|value:");
      qdump_print_db_value (node_p->g_grbynum_val);
      fprintf (foutput, ">\n");
    }

  if (node_p->g_grbynum_pred)
    {
      fprintf (foutput, "-->grbynum predicate:");
      qdump_print_predicate (node_p->g_grbynum_pred);
      fprintf (foutput, "\n");

      if (node_p->g_grbynum_flag == XASL_G_GRBYNUM_FLAG_SCAN_CONTINUE)
    {
      fprintf (foutput, "-->grbynum CONTINUE\n");
    }
    }

  if (node_p->g_agg_list)
    {
      fprintf (foutput, "-->having agg list:");
      qdump_print_arith (AGG_EXP, (void *) node_p->g_agg_list);
      fprintf (foutput, "\n");
    }

  if (node_p->eptr_list)
    {
      fprintf (foutput, "-->EPTR LIST:%p\n", node_p->eptr_list);
    }

  if (node_p->g_with_rollup)
    {
      fprintf (foutput, "-->WITH ROLLUP\n");
    }

  return true;
}

static bool
qdump_print_build_value_node (XASL_NODE * xasl_p)
{
  BUILDVALUE_PROC_NODE *node_p = &xasl_p->proc.buildvalue;
  if (xasl_p->proc.buildvalue.having_pred)
    {
      fprintf (foutput, "-->having predicate:");
      qdump_print_predicate (node_p->having_pred);
      fprintf (foutput, "\n");
    }

  if (node_p->grbynum_val)
    {
      fprintf (foutput, "-->grbynum val:");
      fprintf (foutput, "<addr:%p|", node_p->grbynum_val);
      fprintf (foutput, "type:%s", qdump_data_type_string (DB_VALUE_DOMAIN_TYPE (node_p->grbynum_val)));
      fprintf (foutput, "|value:");
      qdump_print_db_value (node_p->grbynum_val);
      fprintf (foutput, ">\n");
    }

  if (node_p->agg_list)
    {
      fprintf (foutput, "-->agg list:");
      qdump_print_arith (AGG_EXP, (void *) node_p->agg_list);
      fprintf (foutput, "\n");
    }

  if (node_p->outarith_list)
    {
      fprintf (foutput, "-->outarith list:");
      qdump_print_arith (ARITH_EXP, (void *) node_p->outarith_list);
    }

  if (node_p->is_always_false)
    {
      fprintf (foutput, "-->always-false\n");
    }

  return true;
}

static bool
qdump_print_connect_by_proc_node (XASL_NODE * xasl_p)
{
  CONNECTBY_PROC_NODE *node_p = &xasl_p->proc.connect_by;

  if (node_p->start_with_pred)
    {
      fprintf (foutput, "-->start with predicate:");
      qdump_print_predicate (node_p->start_with_pred);
      fprintf (foutput, "\n");
    }

  if (node_p->after_connect_by_pred)
    {
      fprintf (foutput, "-->after connect by predicate:");
      qdump_print_predicate (node_p->after_connect_by_pred);
      fprintf (foutput, "\n");
    }

  if (node_p->input_list_id)
    {
      fprintf (foutput, "-->input list id:");
      qdump_print_list_id (node_p->input_list_id);
      fprintf (foutput, "\n");
    }

  if (node_p->start_with_list_id)
    {
      fprintf (foutput, "-->start with list id:");
      qdump_print_list_id (node_p->start_with_list_id);
      fprintf (foutput, "\n");
    }

  if (node_p->regu_list_pred)
    {
      fprintf (foutput, "-->connect by regu list pred:");
      qdump_print_regu_variable_list (node_p->regu_list_pred);
      fprintf (foutput, "\n");
    }

  if (node_p->regu_list_rest)
    {
      fprintf (foutput, "-->connect by regu list rest:");
      qdump_print_regu_variable_list (node_p->regu_list_rest);
      fprintf (foutput, "\n");
    }

  if (node_p->prior_val_list)
    {
      fprintf (foutput, "-->prior val list:");
      qdump_print_value_list (node_p->prior_val_list);
      fprintf (foutput, "\n");
    }

  if (node_p->prior_outptr_list)
    {
      qdump_print_outlist ("prior output ptrlist", node_p->prior_outptr_list);
      fprintf (foutput, "\n");
    }

  if (node_p->prior_regu_list_pred)
    {
      fprintf (foutput, "-->prior regu list pred:");
      qdump_print_regu_variable_list (node_p->prior_regu_list_pred);
      fprintf (foutput, "\n");
    }

  if (node_p->prior_regu_list_rest)
    {
      fprintf (foutput, "-->prior regu list rest:");
      qdump_print_regu_variable_list (node_p->prior_regu_list_rest);
      fprintf (foutput, "\n");
    }

  if (node_p->after_cb_regu_list_pred)
    {
      fprintf (foutput, "-->after connect by regu list pred:");
      qdump_print_regu_variable_list (node_p->after_cb_regu_list_pred);
      fprintf (foutput, "\n");
    }

  if (node_p->after_cb_regu_list_rest)
    {
      fprintf (foutput, "-->after connect by regu list rest:");
      qdump_print_regu_variable_list (node_p->after_cb_regu_list_rest);
      fprintf (foutput, "\n");
    }

  return true;
}

/*
 * qdump_print_xasl () -
 *   return:
 *   xasl(in):
 */
bool
qdump_print_xasl (xasl_node * xasl_p)
{
  VAL_LIST *single_tuple_p;
  QPROC_DB_VALUE_LIST value_list;
  int i;

  if (xasl_p == NULL)
    {
      return true;
    }

  fprintf (foutput, "\n<start of xasl structure %p>\n", xasl_p);
  qdump_print_xasl_type (xasl_p);

  if (xasl_p->flag)
    {
      int save_flag, nflag;

      save_flag = xasl_p->flag;
      nflag = 0;

      fprintf (foutput, "-->[flag=");

      if (XASL_IS_FLAGED (xasl_p, XASL_LINK_TO_REGU_VARIABLE))
    {
      XASL_CLEAR_FLAG (xasl_p, XASL_LINK_TO_REGU_VARIABLE);
      fprintf (foutput, "%sXASL_LINK_TO_REGU_VARIABLE", (nflag ? "|" : ""));
      nflag++;
    }

      if (XASL_IS_FLAGED (xasl_p, XASL_SKIP_ORDERBY_LIST))
    {
      XASL_CLEAR_FLAG (xasl_p, XASL_SKIP_ORDERBY_LIST);
      fprintf (foutput, "%sXASL_SKIP_ORDERBY_LIST", (nflag ? "|" : ""));
      nflag++;
    }

      if (XASL_IS_FLAGED (xasl_p, XASL_ZERO_CORR_LEVEL))
    {
      XASL_CLEAR_FLAG (xasl_p, XASL_ZERO_CORR_LEVEL);
      fprintf (foutput, "%sXASL_ZERO_CORR_LEVEL", (nflag ? "|" : ""));
      nflag++;
    }

      if (IS_DBLINK_CURSOR_REWIND_XASL (xasl_p))
    {
      XASL_CLEAR_FLAG (xasl_p, XASL_DBLINK_CURSOR_REWIND);
      fprintf (foutput, "%sXASL_DBLINK_CURSOR_REWIND", (nflag ? "|" : ""));
      nflag++;
    }

      if (XASL_IS_FLAGED (xasl_p, XASL_TOP_MOST_XASL))
    {
      XASL_CLEAR_FLAG (xasl_p, XASL_TOP_MOST_XASL);
      fprintf (foutput, "%sXASL_TOP_MOST_XASL", (nflag ? "|" : ""));
      nflag++;
    }

      if (XASL_IS_FLAGED (xasl_p, XASL_SAMPLING_SCAN))
    {
      XASL_CLEAR_FLAG (xasl_p, XASL_SAMPLING_SCAN);
      fprintf (foutput, "%sXASL_SAMPLING_SCAN", (nflag ? "|" : ""));
      nflag++;
    }

      if (xasl_p->flag)
    {
      fprintf (foutput, "%d%s", xasl_p->flag, (nflag ? "|" : ""));
      nflag++;
    }

      fprintf (foutput, "]\n");

      xasl_p->flag = save_flag;
    }

  if (xasl_p->next)
    {
      fprintf (foutput, "-->next:%p\n", xasl_p->next);
    }

  if (xasl_p->list_id)
    {
      fprintf (foutput, "-->list id:");
      qdump_print_list_id (xasl_p->list_id);
      fprintf (foutput, "\n");
    }

  if (xasl_p->orderby_list)
    {
      fprintf (foutput, "-->order by list:");
      qdump_print_sort_list (xasl_p->orderby_list);
      fprintf (foutput, "\n");
    }

  if (xasl_p->ordbynum_val)
    {
      fprintf (foutput, "-->ordbynum val:");
      fprintf (foutput, "<addr:%p|", xasl_p->ordbynum_val);
      fprintf (foutput, "type:%s", qdump_data_type_string (DB_VALUE_DOMAIN_TYPE (xasl_p->ordbynum_val)));
      fprintf (foutput, "|value:");
      qdump_print_db_value (xasl_p->ordbynum_val);
      fprintf (foutput, ">\n");
    }

  if (xasl_p->ordbynum_pred)
    {
      fprintf (foutput, "-->ordbynum predicate:");
      qdump_print_predicate (xasl_p->ordbynum_pred);
      fprintf (foutput, "\n");
      if (xasl_p->ordbynum_flag == XASL_ORDBYNUM_FLAG_SCAN_CONTINUE)
    fprintf (foutput, "-->ordbynum CONTINUE\n");
    }

  if (xasl_p->orderby_limit)
    {
      fprintf (foutput, "-->orderby limit:");
      qdump_print_value (xasl_p->orderby_limit);
      fprintf (foutput, " (optimization %s)",
           prm_get_bool_value (PRM_ID_USE_ORDERBY_SORT_LIMIT) ? "enabled" : "disabled");
      fprintf (foutput, "\n");
    }

  if (xasl_p->is_single_tuple)
    {
      fprintf (foutput, "-->single tuple:");
      single_tuple_p = xasl_p->single_tuple;
      if (single_tuple_p)
    {
      fprintf (foutput, "[value list]:");
      for (value_list = single_tuple_p->valp, i = 0; i < single_tuple_p->val_cnt;
           value_list = value_list->next, i++)
        {
          qdump_print_db_value (value_list->val);
          fprintf (foutput, "\t");
        }
    }
      fprintf (foutput, "\n");
    }

  if (xasl_p->option == Q_DISTINCT)
    {
      fprintf (foutput, "-->query DISTINCT\n");
    }

  if (xasl_p->outptr_list)
    {
      qdump_print_outlist ("outptr list", xasl_p->outptr_list);
      fprintf (foutput, "\n");
    }

  if (xasl_p->spec_list)
    {
      fprintf (foutput, "-->access spec:");
      qdump_print_access_spec (xasl_p->spec_list);
      fprintf (foutput, "\n");
    }

  if (xasl_p->merge_spec)
    {
      fprintf (foutput, "-->merge spec:");
      qdump_print_access_spec (xasl_p->merge_spec);
      fprintf (foutput, "\n");
    }

  if (xasl_p->val_list)
    {
      fprintf (foutput, "-->val_list:");
      qdump_print_value_list (xasl_p->val_list);
      fprintf (foutput, "\n");
    }

  if (xasl_p->aptr_list)
    {
      fprintf (foutput, "-->aptr list:%p\n", xasl_p->aptr_list);
    }

  if (xasl_p->bptr_list)
    {
      fprintf (foutput, "-->bptr list:%p\n", xasl_p->bptr_list);
    }

  if (xasl_p->scan_ptr)
    {
      fprintf (foutput, "-->scan ptr:%p\n", xasl_p->scan_ptr);
    }

  if (xasl_p->dptr_list)
    {
      fprintf (foutput, "-->dptr list:%p\n", xasl_p->dptr_list);
    }

  if (xasl_p->fptr_list)
    {
      fprintf (foutput, "-->fptr list:%p\n", xasl_p->fptr_list);
    }

  if (xasl_p->connect_by_ptr)
    {
      fprintf (foutput, "-->connect_by ptr:%p\n", xasl_p->connect_by_ptr);
    }

  if (xasl_p->during_join_pred)
    {
      fprintf (foutput, "-->during_join predicate:");
      qdump_print_predicate (xasl_p->during_join_pred);
      fprintf (foutput, "\n");
    }

  if (xasl_p->after_join_pred)
    {
      fprintf (foutput, "-->after_join predicate:");
      qdump_print_predicate (xasl_p->after_join_pred);
      fprintf (foutput, "\n");
    }

  if (xasl_p->if_pred)
    {
      fprintf (foutput, "-->if predicate:");
      qdump_print_predicate (xasl_p->if_pred);
      fprintf (foutput, "\n");
    }

  if (xasl_p->instnum_val)
    {
      fprintf (foutput, "-->instnum val:");
      fprintf (foutput, "<addr:%p|", xasl_p->instnum_val);
      fprintf (foutput, "type:%s", qdump_data_type_string (DB_VALUE_DOMAIN_TYPE (xasl_p->instnum_val)));
      fprintf (foutput, "|value:");
      qdump_print_db_value (xasl_p->instnum_val);
      fprintf (foutput, ">\n");
    }

  if (xasl_p->save_instnum_val)
    {
      fprintf (foutput, "-->old instnum val:");
      fprintf (foutput, "<addr:%p|", xasl_p->save_instnum_val);
      fprintf (foutput, "type:%s", qdump_data_type_string (DB_VALUE_DOMAIN_TYPE (xasl_p->save_instnum_val)));
      fprintf (foutput, "|value:");
      qdump_print_db_value (xasl_p->save_instnum_val);
      fprintf (foutput, ">\n");
    }

  if (xasl_p->instnum_pred)
    {
      fprintf (foutput, "-->instnum predicate:");
      qdump_print_predicate (xasl_p->instnum_pred);
      fprintf (foutput, "\n");
      if (xasl_p->instnum_flag == XASL_INSTNUM_FLAG_SCAN_CONTINUE)
    {
      fprintf (foutput, "-->instnum CONTINUE\n");
    }
    }

  if (xasl_p->level_val)
    {
      fprintf (foutput, "-->level val:");
      fprintf (foutput, "<addr:%p|", xasl_p->level_val);
      fprintf (foutput, "type:%s", qdump_data_type_string (DB_VALUE_DOMAIN_TYPE (xasl_p->level_val)));
      fprintf (foutput, "|value:");
      qdump_print_db_value (xasl_p->level_val);
      fprintf (foutput, ">\n");
    }

  if (xasl_p->level_regu)
    {
      fprintf (foutput, "-->level regu:");
      qdump_print_value (xasl_p->level_regu);
      fprintf (foutput, "\n");
    }

  if (xasl_p->isleaf_val)
    {
      fprintf (foutput, "-->isleaf val:");
      fprintf (foutput, "<addr:%p|", xasl_p->isleaf_val);
      fprintf (foutput, "type:%s", qdump_data_type_string (DB_VALUE_DOMAIN_TYPE (xasl_p->isleaf_val)));
      fprintf (foutput, "|value:");
      qdump_print_db_value (xasl_p->isleaf_val);
      fprintf (foutput, ">\n");
    }

  if (xasl_p->isleaf_regu)
    {
      fprintf (foutput, "-->isleaf regu:");
      qdump_print_value (xasl_p->isleaf_regu);
      fprintf (foutput, "\n");
    }

  if (xasl_p->iscycle_val)
    {
      fprintf (foutput, "-->iscycle val:");
      fprintf (foutput, "<addr:%p|", xasl_p->iscycle_val);
      fprintf (foutput, "type:%s", qdump_data_type_string (DB_VALUE_DOMAIN_TYPE (xasl_p->iscycle_val)));
      fprintf (foutput, "|value:");
      qdump_print_db_value (xasl_p->iscycle_val);
      fprintf (foutput, ">\n");
    }

  if (xasl_p->iscycle_regu)
    {
      fprintf (foutput, "-->iscycle regu:");
      qdump_print_value (xasl_p->iscycle_regu);
      fprintf (foutput, "\n");
    }

  fprintf (foutput, "-->current spec:");
  qdump_print_access_spec (xasl_p->curr_spec);
  fprintf (foutput, "\n");

#if defined (SERVER_MODE) || defined (SA_MODE)
  fprintf (foutput, "-->[next scan on=%d]", xasl_p->next_scan_on);
  fprintf (foutput, "[next scan block on=%d]", xasl_p->next_scan_block_on);
  fprintf (foutput, "\n");
#endif /* SERVER_MODE || SA_MODE */

  switch (xasl_p->type)
    {
    case OBJFETCH_PROC:
      qdump_print_fetch_node (xasl_p);
      break;

    case BUILDLIST_PROC:
      qdump_print_build_list_node (xasl_p);
      break;

    case BUILDVALUE_PROC:
      qdump_print_build_value_node (xasl_p);
      break;

    case UNION_PROC:
    case DIFFERENCE_PROC:
    case INTERSECTION_PROC:
      fprintf (foutput, "left xasl:%p\n", xasl_p->proc.union_.left);
      fprintf (foutput, "right xasl:%p\n", xasl_p->proc.union_.right);
      break;

    case MERGELIST_PROC:
      fprintf (foutput, "outer xasl:%p\n", xasl_p->proc.mergelist.outer_xasl);
      fprintf (foutput, "inner xasl:%p\n", xasl_p->proc.mergelist.inner_xasl);
      qdump_print_merge_list_proc_node (&xasl_p->proc.mergelist);
      break;

    case HASHJOIN_PROC:
      fprintf (foutput, "outer xasl:%p\n", xasl_p->proc.hashjoin.outer.xasl);
      fprintf (foutput, "inner xasl:%p\n", xasl_p->proc.hashjoin.inner.xasl);
      qdump_print_hashjoin_proc_node (&xasl_p->proc.hashjoin);
      break;

    case CONNECTBY_PROC:
      qdump_print_connect_by_proc_node (xasl_p);
      break;

    case SCAN_PROC:
      break;

    case UPDATE_PROC:
      fprintf (foutput, "-->update info:");
      qdump_print_update_proc_node (&xasl_p->proc.update);
      fprintf (foutput, "\n");
      break;

    case DELETE_PROC:
      fprintf (foutput, "-->delete info:");
      qdump_print_delete_proc_node (&xasl_p->proc.delete_);
      fprintf (foutput, "\n");
      break;

    case INSERT_PROC:
      fprintf (foutput, "-->insert info:");
      qdump_print_insert_proc_node (&xasl_p->proc.insert);
      fprintf (foutput, "\n");
      break;

    case MERGE_PROC:
      fprintf (foutput, "-->merge info:");
      if (xasl_p->proc.merge.update_xasl)
    {
      fprintf (foutput, "\n---->update:");
      qdump_print_update_proc_node (&xasl_p->proc.merge.update_xasl->proc.update);
      fprintf (foutput, "\n");
    }
      if (xasl_p->proc.merge.insert_xasl)
    {
      fprintf (foutput, "\n---->insert:");
      qdump_print_insert_proc_node (&xasl_p->proc.merge.insert_xasl->proc.insert);
      fprintf (foutput, "\n");
    }
      break;

    case CTE_PROC:
      fprintf (foutput, "non_recursive_part xasl:%p\n", xasl_p->proc.cte.non_recursive_part);
      fprintf (foutput, "recursive_part xasl:%p\n", xasl_p->proc.cte.recursive_part);
      /* TODO - dump anchor and recursive part of CTE when we need */
      break;

    default:
      return false;
    }

  fprintf (foutput, "end of internals of ");
  qdump_print_xasl_type (xasl_p);
  qdump_print_xasl (xasl_p->aptr_list);
  qdump_print_xasl (xasl_p->bptr_list);
  qdump_print_xasl (xasl_p->scan_ptr);
  qdump_print_xasl (xasl_p->dptr_list);
  qdump_print_xasl (xasl_p->fptr_list);
  qdump_print_xasl (xasl_p->connect_by_ptr);

  if (xasl_p->type == BUILDLIST_PROC)
    {
      qdump_print_xasl (xasl_p->proc.buildlist.eptr_list);
    }

  qdump_print_xasl (xasl_p->next);
  fprintf (foutput, "creator OID:");
  qdump_print_oid (&xasl_p->creator_oid);

  fprintf (foutput, "\nclass/serial OID, #pages list:%d ", xasl_p->n_oid_list);
  for (i = 0; i < xasl_p->n_oid_list; ++i)
    {
      qdump_print_oid (&xasl_p->class_oid_list[i]);
      fprintf (foutput, "/%s ", lock_to_lockmode_string ((LOCK) xasl_p->class_locks[i]));
      fprintf (foutput, "/%d ", xasl_p->tcard_list[i]);
    }

  fprintf (foutput, "\ndbval_cnt:%d", xasl_p->dbval_cnt);
  fprintf (foutput, "\nquery_alias:%s\n", xasl_p->query_alias ? xasl_p->query_alias : "(NULL)");
  fprintf (foutput, "<end of xasl structure %p>\n", xasl_p);
  fflush (foutput);

  return true;
}

/*
 * query trace dump for profile
 */

#if defined (SERVER_MODE)

/*
 * qdump_xasl_type_string () -
 *   return:
 *   xasl_p(in):
 */
static const char *
qdump_xasl_type_string (XASL_NODE * xasl_p)
{
  switch (xasl_p->type)
    {
    case BUILDLIST_PROC:
    case BUILDVALUE_PROC:
      return "SELECT";
    case SCAN_PROC:
      return "SCAN";
    case INSERT_PROC:
      return "INSERT";
    case UPDATE_PROC:
      return "UPDATE";
    case DELETE_PROC:
      return "DELETE";
    case MERGE_PROC:
      return "MERGE";
    case UNION_PROC:
      return "UNION";
    case DIFFERENCE_PROC:
      return "DIFFERENCE";
    case INTERSECTION_PROC:
      return "INTERSECTION";
    case MERGELIST_PROC:
      return "MERGELIST";
    case HASHJOIN_PROC:
      return "HASHJOIN";
    case CONNECTBY_PROC:
      return "CONNECTBY";
    case OBJFETCH_PROC:
      return "OBJFETCH";
    case BUILD_SCHEMA_PROC:
      return "SCHEMA";
    case DO_PROC:
      return "DO";
    case CTE_PROC:
      return "CTE";
    default:
      assert (false);
      return "";
    }
}

/*
 * qdump_print_access_spec_stats () -
 *   return:
 *   spec_list_p(in):
 *   proc(in):
 */
static json_t *
qdump_print_access_spec_stats_json (ACCESS_SPEC_TYPE * spec_list_p)
{
  TARGET_TYPE type;
  char *class_name = NULL, *index_name = NULL;
  CLS_SPEC_TYPE *cls_node;
  ACCESS_SPEC_TYPE *spec;
  json_t *scan = NULL, *scan_array = NULL;
  json_t *part_scan = NULL, *part_scan_array = NULL;
  int num_spec = 0;
  char spec_name[1024];
  THREAD_ENTRY *thread_p;

  thread_p = thread_get_thread_entry_info ();

  for (spec = spec_list_p; spec != NULL; spec = spec->next)
    {
      num_spec++;
    }

  if (num_spec > 1)
    {
      scan_array = json_array ();
    }

  for (spec = spec_list_p; spec != NULL; spec = spec->next)
    {
      scan = json_object ();
      type = spec->type;

      if (type == TARGET_CLASS)
    {
      cls_node = &ACCESS_SPEC_CLS_SPEC (spec);
      if (heap_get_class_name (thread_p, &(cls_node->cls_oid), &class_name) != NO_ERROR)
        {
          if (er_errid () != ER_INTERRUPTED)
        {
          /* ignore */
          er_clear ();
        }
        }

      spec_name[0] = '\0';

      if (spec->access == ACCESS_METHOD_SEQUENTIAL)
        {
          if (class_name != NULL)
        {
          sprintf (spec_name, "table (%s)", class_name);
        }
          else
        {
          sprintf (spec_name, "table (unknown)");
        }
        }
      else if (spec->access == ACCESS_METHOD_INDEX)
        {
          if (heap_get_indexinfo_of_btid (thread_p, &cls_node->cls_oid, &spec->indexptr->btid, NULL, NULL, NULL,
                          NULL, &index_name, NULL) == NO_ERROR)
        {
          if (class_name != NULL && index_name != NULL)
            {
              sprintf (spec_name, "index (%s.%s)", class_name, index_name);
            }
          else
            {
              sprintf (spec_name, "index (unknown)");
            }
        }
        }

      json_object_set_new (scan, "access", json_string (spec_name));

      if (class_name != NULL)
        {
          free_and_init (class_name);
        }

      if (index_name != NULL)
        {
          free_and_init (index_name);
        }

      if ((spec->parts != NULL) && (spec->s_id.scan_stats.agl == NULL))
        {
          PARTITION_SPEC_TYPE *curr_part = NULL, *prev_part = NULL;
          SCAN_STATS *scan_stats, *curr_stats, *prev_stats;
          SCAN_STATS save_stats;

          scan_stats = &spec->s_id.scan_stats;

          /* save */
          memcpy (&save_stats, &spec->s_id.scan_stats, sizeof (SCAN_STATS));

          part_scan_array = json_array ();

          for (curr_part = spec->parts; curr_part != NULL; prev_part = curr_part, curr_part = curr_part->next)
        {
          curr_stats = &curr_part->scan_stats;

          if (curr_stats->num_fetches == 0)
            {
              /* skip */
              continue;
            }

          part_scan = json_object ();

          if (heap_get_class_name (thread_p, &curr_part->oid, &class_name) != NO_ERROR)
            {
              /* ignore */
              er_clear ();
            }

          spec_name[0] = '\0';

          switch (spec->access)
            {
            case ACCESS_METHOD_SEQUENTIAL:
              {
            if (class_name != NULL)
              {
                sprintf (spec_name, "table (%s)", class_name);
              }
            else
              {
                sprintf (spec_name, "table (unknown)");
              }

            break;
              }

            case ACCESS_METHOD_INDEX:
              {
            if (heap_get_indexinfo_of_btid
                (thread_p, &curr_part->oid, &curr_part->btid, NULL, NULL, NULL, NULL, &index_name,
                 NULL) == NO_ERROR)
              {
                if (class_name != NULL && index_name != NULL)
                  {
                sprintf (spec_name, "index (%s.%s)", class_name, index_name);
                  }

                else
                  {
                sprintf (spec_name, "index (unknown)");
                  }
              }
            break;
              }

            default:
              break;
            }

          json_object_set_new (part_scan, "access", json_string (spec_name));

          memcpy (scan_stats, &curr_part->scan_stats, sizeof (SCAN_STATS));

          /* SCAN_STATS for DB_PARTITION_CLASS does not support AGL (Aggregate Lookup Optimization). */
          assert (scan_stats->agl == NULL);

          scan_print_stats_json (&spec->s_id, part_scan);

          json_array_append_new (part_scan_array, part_scan);

          if (class_name != NULL)
            {
              free_and_init (class_name);
            }

          if (index_name != NULL)
            {
              free_and_init (index_name);
            }
        }

          /* restore */
          memcpy (&spec->s_id.scan_stats, &save_stats, sizeof (SCAN_STATS));
        }
    }
      else if (type == TARGET_LIST)
    {
      json_object_set_new (scan, "access", json_string ("temp"));
    }
      else if (type == TARGET_SHOWSTMT)
    {
      json_object_set_new (scan, "access", json_string ("show"));
    }
      else if (type == TARGET_SET)
    {
      json_object_set_new (scan, "access", json_string ("set"));
    }
      else if (type == TARGET_METHOD)
    {
      json_object_set_new (scan, "access", json_string ("method"));
    }
      else if (type == TARGET_CLASS_ATTR)
    {
      json_object_set_new (scan, "access", json_string ("class_attr"));
    }
      else if (type == TARGET_DBLINK)
    {
      json_object_set_new (scan, "access", json_string ("dblink"));
    }

      scan_print_stats_json (&spec->s_id, scan);

      if (part_scan_array != NULL)
    {
      json_object_set_new (scan, "PARTITION", part_scan_array);
    }

#if !WINDOWS
      if (spec->s_id.type == S_PARALLEL_HEAP_SCAN)
    {
      if (spec->s_id.s.phsid.trace_storage != NULL)
        {
          if (!spec->s_id.scan_stats.noscan)
        {
          spec->s_id.s.phsid.trace_storage->dump_stats_json (scan, class_name);
        }
          spec->s_id.s.phsid.trace_storage->~accumulative_trace_storage ();
          free (spec->s_id.s.phsid.trace_storage);
          spec->s_id.s.phsid.trace_storage = NULL;
        }
    }
#endif

      if (scan_array != NULL)
    {
      json_array_append_new (scan_array, scan);
    }
    }

  if (scan_array != NULL)
    {
      return scan_array;
    }
  else
    {
      return scan;
    }
}

void
qdump_print_px_subquery_stats_json (parallel_query_execute::query_executor * px_executor, json_t * parent)
{
  json_t *input;
  input = json_object ();
  json_object_set_new (input, "parallel_workers", json_integer (px_executor->get_parallelism () + 1));
  json_object_set_new (input, "time", json_integer (TO_MSEC (px_executor->get_stats ().elapsed_time)));
  json_object_set_new (input, "fetch", json_integer (px_executor->get_stats ().fetches));
  json_object_set_new (input, "fetch_time", json_integer (px_executor->get_stats ().fetch_time));
  json_object_set_new (input, "ioread", json_integer (px_executor->get_stats ().ioreads));
  json_object_set_new (parent, "parallel subquery execution", input);
}

/*
 * qdump_print_stats_json () -
 *   return:
 *   xasl_p(in):
 */
void
qdump_print_stats_json (xasl_node * xasl_p, json_t * parent)
{
  ORDERBY_STATS *ostats;
  GROUPBY_STATS *gstats;
  ANALYTIC_STATS *astats;
  json_t *proc, *scan = NULL;
  json_t *subquery, *groupby, *orderby, *analytic, *parallel;
  json_t *outer, *inner;
  json_t *cte_non_recursive_part, *cte_recursive_part;
  json_t *temp;
  json_t *func;
  xasl_node *xptr;
  json_t *sq_cache;
  json_t *memoize;

  if (xasl_p == NULL || parent == NULL)
    {
      return;
    }

  if (xasl_p->type == SCAN_PROC)
    {
      proc = parent;
    }
  else
    {
      proc = json_object ();
      json_object_set_new (parent, qdump_xasl_type_string (xasl_p), proc);
    }

  switch (xasl_p->type)
    {
    case BUILDLIST_PROC:
    case BUILDVALUE_PROC:
    case UPDATE_PROC:
    case DELETE_PROC:
    case INSERT_PROC:
    case CONNECTBY_PROC:
    case BUILD_SCHEMA_PROC:
      json_object_set_new (proc, "time", json_integer (TO_MSEC (xasl_p->xasl_stats.elapsed_time)));
      json_object_set_new (proc, "fetch", json_integer (xasl_p->xasl_stats.fetches));
      json_object_set_new (proc, "fetch_time", json_integer (xasl_p->xasl_stats.fetch_time));
      json_object_set_new (proc, "ioread", json_integer (xasl_p->xasl_stats.ioreads));
      if (xasl_p->func_stats.calls > 0)
    {
      func = json_object ();
      json_object_set_new (func, "time", json_integer (xasl_p->func_stats.time));
      json_object_set_new (func, "fetch", json_integer (xasl_p->func_stats.fetches));
      json_object_set_new (func, "ioread", json_integer (xasl_p->func_stats.ioreads));
      json_object_set_new (func, "calls", json_integer (xasl_p->func_stats.calls));

      json_object_set_new (proc, "func", func);
    }
      break;

    case UNION_PROC:
    case DIFFERENCE_PROC:
    case INTERSECTION_PROC:
      json_object_set_new (proc, "time", json_integer (TO_MSEC (xasl_p->xasl_stats.elapsed_time)));
      json_object_set_new (proc, "fetch", json_integer (xasl_p->xasl_stats.fetches));
      json_object_set_new (proc, "fetch_time", json_integer (xasl_p->xasl_stats.fetch_time));
      json_object_set_new (proc, "ioread", json_integer (xasl_p->xasl_stats.ioreads));
      subquery = json_array ();
      if (xasl_p->px_executor)
    {
      qdump_print_px_subquery_stats_json (xasl_p->px_executor, proc);
    }
      for (xptr = xasl_p->aptr_list; xptr; xptr = xptr->next)
    {
      temp = json_object ();
      qdump_print_stats_json (xptr, temp);
      json_array_append_new (subquery, temp);
    }
      json_object_set_new (proc, "SUBQUERY (uncorrelated)", subquery);
      break;

    case MERGELIST_PROC:
      outer = json_object ();
      inner = json_object ();

      qdump_print_stats_json (xasl_p->proc.mergelist.outer_xasl, outer);
      qdump_print_stats_json (xasl_p->proc.mergelist.inner_xasl, inner);

      if (xasl_p->px_executor)
    {
      qdump_print_px_subquery_stats_json (xasl_p->px_executor, proc);
    }

      json_object_set_new (proc, "outer", outer);
      json_object_set_new (proc, "inner", inner);
      break;

    case HASHJOIN_PROC:
      json_object_set_new (proc, "time", json_integer (TO_MSEC (xasl_p->xasl_stats.elapsed_time)));
      json_object_set_new (proc, "fetch", json_integer (xasl_p->xasl_stats.fetches));
      json_object_set_new (proc, "fetch_time", json_integer (xasl_p->xasl_stats.fetch_time));
      json_object_set_new (proc, "ioread", json_integer (xasl_p->xasl_stats.ioreads));
      if (xasl_p->executed_parallelism > 1)
    {
      json_object_set_new (proc, "parallel workers", json_integer (xasl_p->executed_parallelism));
    }
      qdump_print_hashjoin_stats_json (xasl_p, proc);
      break;

    case MERGE_PROC:
      inner = json_object ();
      outer = json_object ();

      qdump_print_stats_json (xasl_p->proc.merge.update_xasl, inner);
      qdump_print_stats_json (xasl_p->proc.merge.insert_xasl, outer);

      json_object_set_new (proc, "update", inner);
      json_object_set_new (proc, "insert", outer);
      break;

    case CTE_PROC:
      cte_non_recursive_part = json_object ();
      qdump_print_stats_json (xasl_p->proc.cte.non_recursive_part, cte_non_recursive_part);
      json_object_set_new (proc, "non_recursive_part", cte_non_recursive_part);

      if (xasl_p->proc.cte.recursive_part != NULL)
    {
      cte_recursive_part = json_object ();
      qdump_print_stats_json (xasl_p->proc.cte.recursive_part, cte_recursive_part);
      json_object_set_new (proc, "recursive_part", cte_recursive_part);
    }
      break;

    case SCAN_PROC:
    case OBJFETCH_PROC:
    default:
      break;
    }

  if (xasl_p->spec_list != NULL)
    {
      scan = qdump_print_access_spec_stats_json (xasl_p->spec_list);
    }
  else if (xasl_p->merge_spec != NULL)
    {
      scan = qdump_print_access_spec_stats_json (xasl_p->merge_spec);
    }

  if (xasl_p->memoize_storage && xasl_p->memoize_storage->hit > 0)
    {
      memoize = json_object ();
      json_object_set_new (memoize, "time", json_integer (TO_MSEC (xasl_p->memoize_storage->m_elapsed_time)));
      json_object_set_new (memoize, "hit", json_integer (xasl_p->memoize_storage->hit));
      json_object_set_new (memoize, "miss", json_integer (xasl_p->memoize_storage->miss));
      json_object_set_new (memoize, "size", json_integer (xasl_p->memoize_storage->get_current_size () / 1024));
      json_object_set_new (memoize, "enabled", json_boolean (xasl_p->memoize_storage->is_disabled ()? false : true));
      json_object_set_new (proc, "MEMOIZE", memoize);
    }

  if (scan != NULL)
    {
      json_object_set_new (proc, "SCAN", scan);
      qdump_print_stats_json (xasl_p->scan_ptr, scan);
    }
  else
    {
      qdump_print_stats_json (xasl_p->scan_ptr, proc);
    }

  qdump_print_stats_json (xasl_p->connect_by_ptr, proc);

  if (xasl_p->sq_cache && XASL_IS_FLAGED (xasl_p, XASL_USES_SQ_CACHE) && SQ_CACHE_HIT (xasl_p) > 0)
    {
      sq_cache = json_object ();
      json_object_set_new (sq_cache, "hit", json_integer (SQ_CACHE_HIT (xasl_p)));
      json_object_set_new (sq_cache, "miss", json_integer (SQ_CACHE_MISS (xasl_p)));
      json_object_set_new (sq_cache, "size", json_integer (SQ_CACHE_SIZE (xasl_p)));
      if (SQ_CACHE_ENABLED (xasl_p))
    {
      json_object_set_new (sq_cache, "status", json_string ("enabled"));
    }
      else
    {
      json_object_set_new (sq_cache, "status", json_string ("disabled"));
    }
      json_object_set_new (proc, "SUBQUERY_CACHE", sq_cache);
    }

  gstats = &xasl_p->groupby_stats;
  if (gstats->run_groupby)
    {
      groupby = json_object ();

      json_object_set_new (groupby, "time", json_integer (TO_MSEC (gstats->groupby_time)));

      if (gstats->groupby_hash == HS_ACCEPT_ALL)
    {
      json_object_set_new (groupby, "hash", json_true ());
    }
      else if (gstats->groupby_hash == HS_REJECT_ALL)
    {
      json_object_set_new (groupby, "hash", json_string ("partial"));
    }
      else
    {
      json_object_set_new (groupby, "hash", json_false ());
    }

      if (gstats->groupby_sort)
    {
      json_object_set_new (groupby, "sort", json_true ());
      json_object_set_new (groupby, "page", json_integer (gstats->groupby_pages));
      json_object_set_new (groupby, "ioread", json_integer (gstats->groupby_ioreads));
    }
      else
    {
      json_object_set_new (groupby, "sort", json_false ());
    }

      json_object_set_new (groupby, "rows", json_integer (gstats->rows));
      json_object_set_new (proc, "GROUPBY", groupby);
    }

  ostats = &xasl_p->orderby_stats;
  if (ostats->orderby_filesort || ostats->orderby_topnsort || XASL_IS_FLAGED (xasl_p, XASL_SKIP_ORDERBY_LIST))
    {
      orderby = json_object ();

      json_object_set_new (orderby, "time", json_integer (TO_MSEC (ostats->orderby_time)));

      if (ostats->orderby_filesort)
    {
      json_object_set_new (orderby, "sort", json_true ());
      json_object_set_new (orderby, "page", json_integer (ostats->orderby_pages));
      json_object_set_new (orderby, "ioread", json_integer (ostats->orderby_ioreads));
    }
      else if (ostats->orderby_topnsort)
    {
      json_object_set_new (orderby, "topnsort", json_true ());
    }
      else
    {
      json_object_set_new (orderby, "skipsort", json_true ());
    }

      json_object_set_new (proc, "ORDERBY", orderby);
      if (ostats->parallel_num > 0)
    {
      parallel = json_object ();
      json_object_set_new (parallel, "parallel workers", json_integer (ostats->parallel_num));
      json_object_set_new (parallel, "min time", json_integer (ostats->px_min_orderby_time));
      json_object_set_new (parallel, "max time", json_integer (ostats->px_max_orderby_time));
      json_object_set_new (parallel, "min pages", json_integer (ostats->px_min_orderby_pages));
      json_object_set_new (parallel, "max pages", json_integer (ostats->px_max_orderby_pages));
      json_object_set_new (parallel, "min ioreads", json_integer (ostats->px_min_orderby_ioreads));
      json_object_set_new (parallel, "max ioreads", json_integer (ostats->px_max_orderby_ioreads));
      json_object_set_new (proc, "PARALLEL ORDERBY", parallel);
    }

    }

  astats = xasl_p->analytic_stats;
  if (astats != NULL)
    {
      json_t *analytic_array = json_array ();

      for (ANALYTIC_STATS * curr = astats; curr != NULL; curr = curr->next)
    {
      analytic = json_object ();
      json_object_set_new (analytic, "time", json_integer (TO_MSEC (curr->analytic_time)));

      if (curr->analytic_sort)
        {
          json_object_set_new (analytic, "sort", json_true ());
        }
      else
        {
          json_object_set_new (analytic, "sort", json_false ());
        }

      if (curr->analytic_stopkey)
        {
          json_object_set_new (analytic, "stopkey", json_true ());
        }
      else
        {
          json_object_set_new (analytic, "stopkey", json_false ());
        }

      json_object_set_new (analytic, "page", json_integer (curr->analytic_pages));
      json_object_set_new (analytic, "ioread", json_integer (curr->analytic_ioreads));
      json_object_set_new (analytic, "rows", json_integer (curr->rows));
      json_array_append_new (analytic_array, analytic);
    }

      json_object_set_new (proc, "ANALYTIC", analytic_array);
    }

  if (HAVE_SUBQUERY_PROC (xasl_p) && xasl_p->aptr_list != NULL)
    {
      subquery = json_array ();
      if (xasl_p->px_executor)
    {
      temp = json_object ();
      qdump_print_px_subquery_stats_json (xasl_p->px_executor, temp);
      json_array_append_new (subquery, temp);
    }
      for (xptr = xasl_p->aptr_list; xptr; xptr = xptr->next)
    {
      temp = json_object ();
      qdump_print_stats_json (xptr, temp);
      json_array_append_new (subquery, temp);
    }
      json_object_set_new (proc, "SUBQUERY (uncorrelated)", subquery);
    }

  if (xasl_p->dptr_list != NULL)
    {
      subquery = json_array ();
      for (xptr = xasl_p->dptr_list; xptr; xptr = xptr->next)
    {
      temp = json_object ();
      qdump_print_stats_json (xptr, temp);
      json_array_append_new (subquery, temp);
    }
      json_object_set_new (proc, "SUBQUERY (correlated)", subquery);
    }
}

/*
 * qdump_print_access_spec_stats_text () -
 *   return:
 *   fp(in):
 *   spec_list_p(in):
 */
static void
qdump_print_access_spec_stats_text (FILE * fp, ACCESS_SPEC_TYPE * spec_list_p, int indent)
{
  TARGET_TYPE type;
  char *class_name = NULL, *index_name = NULL;
  CLS_SPEC_TYPE *cls_node;
  ACCESS_SPEC_TYPE *spec;
  int i, multi_spec_indent;
  THREAD_ENTRY *thread_p;

  if (spec_list_p == NULL)
    {
      return;
    }

  thread_p = thread_get_thread_entry_info ();

  multi_spec_indent = fprintf (fp, "%*cSCAN ", indent, ' ');

  for (spec = spec_list_p, i = 0; spec != NULL; spec = spec->next, i++)
    {
      if (i > 0)
    {
      fprintf (fp, "%*c", multi_spec_indent, ' ');
    }

      type = spec->type;
      if (type == TARGET_CLASS)
    {
      cls_node = &ACCESS_SPEC_CLS_SPEC (spec);
      if (heap_get_class_name (thread_p, &(cls_node->cls_oid), &class_name) != NO_ERROR)
        {
          if (er_errid () != ER_INTERRUPTED)
        {
          /* ignore */
          er_clear ();
        }
        }

      if (spec->access == ACCESS_METHOD_SEQUENTIAL)
        {
          if (class_name != NULL)
        {
          fprintf (fp, "(table: %s), ", class_name);
        }
          else
        {
          fprintf (fp, "(table: unknown), ");
        }
        }
      else if (spec->access == ACCESS_METHOD_INDEX)
        {
          if (heap_get_indexinfo_of_btid (thread_p, &cls_node->cls_oid, &spec->indexptr->btid, NULL, NULL, NULL,
                          NULL, &index_name, NULL) == NO_ERROR)
        {
          if (class_name != NULL && index_name != NULL)
            {
              fprintf (fp, "(index: %s.%s), ", class_name, index_name);
            }
          else
            {
              fprintf (fp, "(index: unknown), ");
            }
        }
        }
      scan_print_stats_text (fp, &spec->s_id);
#if !WINDOWS
      if (spec->s_id.type == S_PARALLEL_HEAP_SCAN || spec->s_id.type == S_HEAP_SCAN)
        {
          if (spec->s_id.s.phsid.trace_storage)
        {
          if (!spec->s_id.scan_stats.noscan)
            {
              spec->s_id.s.phsid.trace_storage->dump_stats_text (fp, multi_spec_indent, class_name);
            }
          spec->s_id.s.phsid.trace_storage->~accumulative_trace_storage ();
          free (spec->s_id.s.phsid.trace_storage);
          spec->s_id.s.phsid.trace_storage = NULL;
        }
        }
#endif
      if (class_name != NULL)
        {
          free_and_init (class_name);
        }

      if (index_name != NULL)
        {
          free_and_init (index_name);
        }

      if ((spec->parts != NULL) && (spec->s_id.scan_stats.agl == NULL))
        {
          PARTITION_SPEC_TYPE *curr_part = NULL, *prev_part = NULL;
          SCAN_STATS *scan_stats, *curr_stats, *prev_stats;
          SCAN_STATS save_stats;

          scan_stats = &spec->s_id.scan_stats;

          /* save */
          memcpy (&save_stats, scan_stats, sizeof (SCAN_STATS));

          for (curr_part = spec->parts; curr_part != NULL; prev_part = curr_part, curr_part = curr_part->next)
        {
          curr_stats = &curr_part->scan_stats;

          if (curr_stats->num_fetches == 0)
            {
              /* skip */
              continue;
            }

          fprintf (fp, "\n");
          fprintf (fp, "%*cPARTITION ", multi_spec_indent + 2, ' ');

          if (heap_get_class_name (thread_p, &curr_part->oid, &class_name) != NO_ERROR)
            {
              /* ignore */
              er_clear ();
            }

          switch (spec->access)
            {
            case ACCESS_METHOD_SEQUENTIAL:
              {
            if (class_name != NULL)
              {
                fprintf (fp, "(table: %s), ", class_name);
              }
            else
              {
                fprintf (fp, "(table: unknown), ");
              }

            break;
              }

            case ACCESS_METHOD_INDEX:
              {
            if (heap_get_indexinfo_of_btid
                (thread_p, &curr_part->oid, &curr_part->btid, NULL, NULL, NULL, NULL, &index_name,
                 NULL) == NO_ERROR)
              {
                if (class_name != NULL && index_name != NULL)
                  {
                fprintf (fp, "(index: %s.%s), ", class_name, index_name);
                  }
                else
                  {
                fprintf (fp, "(index: unknown), ");
                  }
              }
            break;
              }

            default:
              break;
            }

          memcpy (scan_stats, &curr_part->scan_stats, sizeof (SCAN_STATS));

          /* SCAN_STATS for DB_PARTITION_CLASS does not support AGL (Aggregate Lookup Optimization). */
          assert (scan_stats->agl == NULL);

          scan_print_stats_text (fp, &spec->s_id);

          if (class_name != NULL)
            {
              free_and_init (class_name);
            }

          if (index_name != NULL)
            {
              free_and_init (index_name);
            }
        }

          /* restore */
          memcpy (&spec->s_id.scan_stats, &save_stats, sizeof (SCAN_STATS));
        }
    }
      else
    {
      scan_print_stats_text (fp, &spec->s_id);
    }

      fprintf (fp, "\n");
    }

  return;
}

/*
 * qdump_print_stats_text () -
 *   return:
 *   fp(in):
 *   xasl_p(in):
 *   indent(in):
 */
void
qdump_print_stats_text (FILE * fp, xasl_node * xasl_p, int indent)
{
  ORDERBY_STATS *ostats;
  GROUPBY_STATS *gstats;
  ANALYTIC_STATS *astats;
  xasl_node *xptr;

  if (xasl_p == NULL)
    {
      return;
    }

  if (xasl_p->type != CONNECTBY_PROC)
    {
      indent += 2;
    }

  if (xasl_p->type != SCAN_PROC)
    {
      fprintf (fp, "%*c", indent, ' ');
    }

  switch (xasl_p->type)
    {
    case BUILDLIST_PROC:
    case BUILDVALUE_PROC:
    case INSERT_PROC:
    case UPDATE_PROC:
    case DELETE_PROC:
    case CONNECTBY_PROC:
    case BUILD_SCHEMA_PROC:
      fprintf (fp, "%s (time: %d, fetch: %lld, fetch_time: %lld, ioread: %lld)\n",
           qdump_xasl_type_string (xasl_p), TO_MSEC (xasl_p->xasl_stats.elapsed_time),
           (long long int) xasl_p->xasl_stats.fetches, (long long int) xasl_p->xasl_stats.fetch_time,
           (long long int) xasl_p->xasl_stats.ioreads);

      indent += 2;
      if (xasl_p->func_stats.calls > 0)
    {
      fprintf (fp, "%*cFUNC (time: %lld, fetch: %lld, ioread: %lld, calls: %lld)\n", indent, ' ',
           (long long int) xasl_p->func_stats.time, (long long int) xasl_p->func_stats.fetches,
           (long long int) xasl_p->func_stats.ioreads, (long long int) xasl_p->func_stats.calls);
    }
      break;

    case UNION_PROC:
    case DIFFERENCE_PROC:
    case INTERSECTION_PROC:
      fprintf (fp, "%s (time: %d, fetch: %lld, fetch_time: %lld, ioread: %lld)\n",
           qdump_xasl_type_string (xasl_p), TO_MSEC (xasl_p->xasl_stats.elapsed_time),
           (long long int) xasl_p->xasl_stats.fetches, (long long int) xasl_p->xasl_stats.fetch_time,
           (long long int) xasl_p->xasl_stats.ioreads);
      if (xasl_p->px_executor)
    {
      int len = strlen (qdump_xasl_type_string (xasl_p));
      fprintf (fp, "%*c (parallel workers: %d, time: %d, fetch: %lld, fetch_time: %lld, ioread: %lld)\n",
           indent + len, ' ', xasl_p->px_executor->get_parallelism () + 1,
           TO_MSEC (xasl_p->px_executor->get_stats ().elapsed_time),
           (long long int) xasl_p->px_executor->get_stats ().fetches,
           (long long int) xasl_p->px_executor->get_stats ().fetch_time,
           (long long int) xasl_p->px_executor->get_stats ().ioreads);
    }

      for (xptr = xasl_p->aptr_list; xptr; xptr = xptr->next)
    {
      qdump_print_stats_text (fp, xptr, indent);
    }
      break;

    case MERGELIST_PROC:
      if (xasl_p->px_executor)
    {
      fprintf (fp,
           "MERGELIST (parallel workers: %d, time: %d, fetch: %lld, fetch_time: %lld, ioread: %lld)\n",
           xasl_p->px_executor->get_parallelism () + 1,
           TO_MSEC (xasl_p->px_executor->get_stats ().elapsed_time),
           (long long int) xasl_p->px_executor->get_stats ().fetches,
           (long long int) xasl_p->px_executor->get_stats ().fetch_time,
           (long long int) xasl_p->px_executor->get_stats ().ioreads);
    }
      else
    {
      fprintf (fp, "MERGELIST\n");
    }
      qdump_print_stats_text (fp, xasl_p->proc.mergelist.outer_xasl, indent);
      qdump_print_stats_text (fp, xasl_p->proc.mergelist.inner_xasl, indent);
      break;

    case HASHJOIN_PROC:
      fprintf (fp, "%s (time: %d, fetch: %ld, fetch_time: %ld, ioread: %ld", qdump_xasl_type_string (xasl_p),
           TO_MSEC (xasl_p->xasl_stats.elapsed_time), xasl_p->xasl_stats.fetches, xasl_p->xasl_stats.fetch_time,
           xasl_p->xasl_stats.ioreads);
      if (xasl_p->executed_parallelism > 1)
    {
      fprintf (fp, ", parallel workers: %d)\n", xasl_p->executed_parallelism);
    }
      else
    {
      fprintf (fp, ")\n");
    }
      qdump_print_hashjoin_stats_text (fp, xasl_p, indent);
      break;

    case MERGE_PROC:
      fprintf (fp, "MERGE\n");
      qdump_print_stats_text (fp, xasl_p->proc.merge.update_xasl, indent);
      qdump_print_stats_text (fp, xasl_p->proc.merge.insert_xasl, indent);
      break;

    case CTE_PROC:
      fprintf (fp, "CTE (non_recursive_part)\n");
      qdump_print_stats_text (fp, xasl_p->proc.cte.non_recursive_part, indent);
      if (xasl_p->proc.cte.recursive_part != NULL)
    {
      fprintf (fp, "%*c", indent, ' ');
      fprintf (fp, "CTE (recursive_part)\n");
      qdump_print_stats_text (fp, xasl_p->proc.cte.recursive_part, indent);
    }
      break;

    case SCAN_PROC:
    case OBJFETCH_PROC:
    default:
      break;
    }

  if (xasl_p->sub_xasl_id && xasl_p->sub_cache_ref_count > 0)
    {
      fprintf (fp, "%*cRESULT CACHE (reference count: %d)\n", indent, ' ', xasl_p->sub_cache_ref_count);
    }
  else
    {
      qdump_print_access_spec_stats_text (fp, xasl_p->spec_list, indent);
      qdump_print_access_spec_stats_text (fp, xasl_p->merge_spec, indent);
    }

  if (xasl_p->memoize_storage && xasl_p->memoize_storage->hit > 0)
    {
      fprintf (fp, "%*c", indent, ' ');
      fprintf (fp, "MEMOIZE (time: %d, hit: %lu, miss: %lu, size: %luKB, enabled: %s)\n",
           TO_MSEC (xasl_p->memoize_storage->m_elapsed_time), xasl_p->memoize_storage->hit,
           xasl_p->memoize_storage->miss, xasl_p->memoize_storage->get_current_size () / 1024,
           xasl_p->memoize_storage->is_disabled ()? "false" : "true");
    }

  qdump_print_stats_text (fp, xasl_p->scan_ptr, indent);
  qdump_print_stats_text (fp, xasl_p->connect_by_ptr, indent);

  if (xasl_p->sq_cache && XASL_IS_FLAGED (xasl_p, XASL_USES_SQ_CACHE) && SQ_CACHE_HIT (xasl_p) > 0)
    {
      fprintf (fp, "%*c", indent, ' ');
      if (SQ_CACHE_ENABLED (xasl_p))
    {
      fprintf (fp, "SUBQUERY_CACHE (hit: %d, miss: %d, size: %lu, status: enabled)\n",
           SQ_CACHE_HIT (xasl_p), SQ_CACHE_MISS (xasl_p), SQ_CACHE_SIZE (xasl_p));
    }
      else
    {
      fprintf (fp, "SUBQUERY_CACHE (hit: %d, miss: %d, size: %lu, status: disabled)\n",
           SQ_CACHE_HIT (xasl_p), SQ_CACHE_MISS (xasl_p), SQ_CACHE_SIZE (xasl_p));
    }
    }

  astats = xasl_p->analytic_stats;
  if (astats != NULL)
    {
      int analytic_num = 0;
      for (ANALYTIC_STATS * curr = astats; curr != NULL; curr = curr->next)
    {
      fprintf (fp, "%*c", indent, ' ');
      fprintf (fp, "ANALYTIC #%d (time: %d", ++analytic_num, TO_MSEC (curr->analytic_time));

      if (curr->analytic_sort)
        {
          fprintf (fp, ", sort: true");
        }
      else
        {
          fprintf (fp, ", sort: false");
        }

      fprintf (fp, ", page: %lld, ioread: %lld", (long long int) curr->analytic_pages,
           (long long int) curr->analytic_ioreads);
      fprintf (fp, ", rows: %d)", curr->rows);
      if (curr->analytic_stopkey)
        {
          fprintf (fp, " (stopkey)");
        }
      fprintf (fp, "\n");
    }
    }

  gstats = &xasl_p->groupby_stats;
  if (gstats->run_groupby)
    {
      fprintf (fp, "%*c", indent, ' ');
      fprintf (fp, "GROUPBY (time: %d", TO_MSEC (gstats->groupby_time));

      if (gstats->groupby_hash == HS_ACCEPT_ALL)
    {
      fprintf (fp, ", hash: true");
    }
      else if (gstats->groupby_hash == HS_REJECT_ALL)
    {
      fprintf (fp, ", hash: partial");
    }
      else
    {
      fprintf (fp, ", hash: false");
    }

      if (gstats->groupby_sort)
    {
      fprintf (fp, ", sort: true, page: %lld, ioread: %lld", (long long int) gstats->groupby_pages,
           (long long int) gstats->groupby_ioreads);
    }
      else
    {
      fprintf (fp, ", sort: false");
    }

      fprintf (fp, ", rows: %d)\n", gstats->rows);
    }

  ostats = &xasl_p->orderby_stats;
  if (ostats->orderby_filesort || ostats->orderby_topnsort || XASL_IS_FLAGED (xasl_p, XASL_SKIP_ORDERBY_LIST))
    {
      fprintf (fp, "%*c", indent, ' ');
      fprintf (fp, "ORDERBY (time: %d", TO_MSEC (ostats->orderby_time));

      if (ostats->orderby_filesort)
    {
      fprintf (fp, ", sort: true");
      fprintf (fp, ", page: %lld, ioread: %lld", (long long int) ostats->orderby_pages,
           (long long int) ostats->orderby_ioreads);
      if (ostats->parallel_num > 0)
        {
          fprintf (fp, ")\n");
          fprintf (fp, "%*c", indent + 8, ' ');
          fprintf (fp, "(parallel workers: %d", ostats->parallel_num);
          fprintf (fp, ", time: %lu..%lu", ostats->px_min_orderby_time, ostats->px_max_orderby_time);
          fprintf (fp, ", page: %lu..%lu", ostats->px_min_orderby_pages, ostats->px_max_orderby_pages);
          fprintf (fp, ", ioread: %lu..%lu", ostats->px_min_orderby_ioreads, ostats->px_max_orderby_ioreads);
        }
    }
      else if (ostats->orderby_topnsort)
    {
      fprintf (fp, ", topnsort: true");
    }
      else
    {
      fprintf (fp, ", skipsort: true");
    }
      fprintf (fp, ")\n");
    }

  if (HAVE_SUBQUERY_PROC (xasl_p) && xasl_p->aptr_list != NULL)
    {
      if (HAVE_SUBQUERY_PROC (xasl_p->aptr_list))
    {
      fprintf (fp, "%*cSUBQUERY (uncorrelated)\n", indent, ' ');
      if (xasl_p->px_executor)
        {
          fprintf (fp,
               "%*c         (parallel workers: %d, time: %d, fetch: %lld, fetch_time: %lld, ioread: %lld)\n",
               indent, ' ', xasl_p->px_executor->get_parallelism () + 1,
               TO_MSEC (xasl_p->px_executor->get_stats ().elapsed_time),
               (long long int) xasl_p->px_executor->get_stats ().fetches,
               (long long int) xasl_p->px_executor->get_stats ().fetch_time,
               (long long int) xasl_p->px_executor->get_stats ().ioreads);
        }
    }

      for (xptr = xasl_p->aptr_list; xptr; xptr = xptr->next)
    {
      qdump_print_stats_text (fp, xptr, indent);
    }
    }

  if (xasl_p->dptr_list != NULL)
    {
      fprintf (fp, "%*cSUBQUERY (correlated)\n", indent, ' ');
      for (xptr = xasl_p->dptr_list; xptr; xptr = xptr->next)
    {
      qdump_print_stats_text (fp, xptr, indent);
    }
    }
}

static const char *
qdump_hashjoin_type_string (HASH_METHOD hash_method)
{
  switch (hash_method)
    {
    case HASH_METH_IN_MEM:
      return "memory";

    case HASH_METH_HYBRID:
      return "hybrid";

    case HASH_METH_HASH_FILE:
      return "file";

    case HASH_METH_NOT_USE:
    default:
      return "skip";
    }
}

static void
qdump_print_hashjoin_stats_text (FILE * fp, xasl_node * xasl_p, int indent)
{
  XASL_NODE *outer_xasl, *inner_xasl;

  HASHJOIN_PROC_NODE *proc;
  HASHJOIN_STATS_GROUP *stats_group;
  HASHJOIN_STATS *stats, *part_stats, *current_stats;
  UINT32 part_cnt, part_index;

  char hash_method_str[32];
  int len;
  bool need_separator;

  assert (fp != NULL);
  assert (xasl_p != NULL);

  proc = &xasl_p->proc.hashjoin;
  stats_group = &proc->stats_group;
  stats = &stats_group->stats;
  part_stats = stats_group->context_stats;
  part_cnt = stats_group->context_cnt;
  assert (part_stats == NULL || part_cnt > 1);

  outer_xasl = proc->outer.xasl;
  inner_xasl = proc->inner.xasl;
  assert (outer_xasl != NULL);
  assert (inner_xasl != NULL);

  if (part_cnt > 1)
    {
      len = 0;
      need_separator = false;

      if (stats->use_hash_memory)
    {
      len += sprintf (hash_method_str + len, "%s", qdump_hashjoin_type_string (HASH_METH_IN_MEM));
      need_separator = true;
    }

      if (stats->use_hash_hybrid)
    {
      len +=
        sprintf (hash_method_str + len, "%s%s", (need_separator) ? "+" : "",
             qdump_hashjoin_type_string (HASH_METH_HYBRID));
      need_separator = true;
    }

      if (stats->use_hash_file)
    {
      len +=
        sprintf (hash_method_str + len, "%s%s", (need_separator) ? "+" : "",
             qdump_hashjoin_type_string (HASH_METH_HASH_FILE));
      need_separator = true;
    }

      if (stats->use_hash_skip)
    {
      len +=
        sprintf (hash_method_str + len, "%s%s", (need_separator) ? "+" : "",
             qdump_hashjoin_type_string (HASH_METH_NOT_USE));
      need_separator = true;
    }

      hash_method_str[len] = '\0';
    }

  indent += 2;

  if (part_cnt <= 1)
    {
      XASL_NODE *build_xasl, *probe_xasl;

      if (stats->swap_join_inputs)
    {
      build_xasl = outer_xasl;
      probe_xasl = inner_xasl;
    }
      else
    {
      build_xasl = inner_xasl;
      probe_xasl = outer_xasl;
    }

      /* no parallel subquery */
      if (xasl_p->px_executor == NULL)
    {
      TSC_ADD_TIMEVAL (stats->build.elapsed_time, build_xasl->xasl_stats.elapsed_time);
      stats->build.fetches += build_xasl->xasl_stats.fetches;
      stats->build.ioreads += build_xasl->xasl_stats.ioreads;

      TSC_ADD_TIMEVAL (stats->probe.elapsed_time, probe_xasl->xasl_stats.elapsed_time);
      stats->probe.fetches += probe_xasl->xasl_stats.fetches;
      stats->probe.ioreads += probe_xasl->xasl_stats.ioreads;
    }

      fprintf (fp, "%*cBUILD (time: %d, fetch: %ld, ioread: %ld, rows: %ld, method: %s", indent, ' ',
           TO_MSEC (stats->build.elapsed_time), stats->build.fetches, stats->build.ioreads,
           stats->build.qualified_rows, qdump_hashjoin_type_string (stats->hash_method));

#if HASHJOIN_COLLISION_RATE
      if (stats->use_hash_file)
    {
      fprintf (fp, ")");
    }
      else
    {
      fprintf (fp, ", collision_rate: %.0f%%)", stats->collision_rate * 100);
    }
#else
      fprintf (fp, ")");
#endif /* HASHJOIN_COLLISION_RATE */

#if HASHJOIN_PROFILE_TIME
      fprintf (fp, ", (F: %d, H: %d, I: %d)", TO_MSEC (stats->profile.build.fetch), TO_MSEC (stats->profile.build.hash),
           TO_MSEC (stats->profile.build.insert));
#endif /* HASHJOIN_PROFILE_TIME */

      fprintf (fp, "\n");

      /* no parallel subquery */
      if (xasl_p->px_executor == NULL)
    {
      qdump_print_stats_text (fp, build_xasl, indent);
    }

      fprintf (fp,
           "%*cPROBE (time: %d, fetch: %ld, ioread: %ld, readrows: %ld, readkeys: %ld, rows: %ld)",
           indent, ' ', TO_MSEC (stats->probe.elapsed_time), stats->probe.fetches, stats->probe.ioreads,
           stats->probe.read_rows, stats->probe.read_keys, stats->probe.qualified_rows);


#if HASHJOIN_PROFILE_TIME
      fprintf (fp, ", (F: %d, H: %d, S: %d, M: %d, A: %d)", TO_MSEC (stats->profile.probe.fetch),
           TO_MSEC (stats->profile.probe.hash), TO_MSEC (stats->profile.probe.search),
           TO_MSEC (stats->profile.probe.match), TO_MSEC (stats->profile.probe.add));
#endif /* HASHJOIN_PROFILE_TIME */

      fprintf (fp, "\n");

      /* no parallel subquery */
      if (xasl_p->px_executor == NULL)
    {
      qdump_print_stats_text (fp, probe_xasl, indent);
    }
    }
  else
    {
      fprintf (fp, "%*cSPLIT (time: %d, fetch: %ld, ioread: %ld, partitions: %d)\n", indent, ' ',
           TO_MSEC (stats->split.elapsed_time), stats->split.fetches, stats->split.ioreads, part_cnt);

      if (stats->num_parallel_threads > 1)
    {
      fprintf (fp, "%*cPARALLEL (time: %d, fetch: %ld, ioread: %ld)\n", indent, ' ',
           TO_MSEC (stats->parallel.elapsed_time), stats->parallel.fetches, stats->parallel.ioreads);

      indent += 2;

      fprintf (fp, "%*cBUILD (time: %d..%d, fetch: %ld, ioread: %ld, rows: %ld, method: %s",
           indent, ' ', TO_MSEC (stats->build.range_time.min), TO_MSEC (stats->build.range_time.max),
           stats->build.fetches, stats->build.ioreads, stats->build.qualified_rows, hash_method_str);
    }
      else
    {
      fprintf (fp, "%*cBUILD (time: %d, fetch: %ld, ioread: %ld, rows: %ld, method: %s",
           indent, ' ', TO_MSEC (stats->build.elapsed_time), stats->build.fetches, stats->build.ioreads,
           stats->build.qualified_rows, hash_method_str);
    }

#if HASHJOIN_COLLISION_RATE
      if (stats->use_hash_file)
    {
      fprintf (fp, ")\n");
    }
      else
    {
      fprintf (fp, ", collision_rate: %.0f%%)\n", stats->collision_rate * 100);
    }
#else
      fprintf (fp, ")\n");
#endif /* HASHJOIN_COLLISION_RATE */

#if HASHJOIN_DUMP_PARTITION
      indent += 2;

      for (part_index = 0; part_index < part_cnt; part_index++)
    {
      current_stats = &part_stats[part_index];
      assert (current_stats != NULL);

      fprintf (fp, "%*c#%d (time: %d, fetch: %ld, ioread: %ld, rows: %ld, method: %s)", indent,
           ' ', part_index + 1, TO_MSEC (current_stats->build.elapsed_time), current_stats->build.fetches,
           current_stats->build.ioreads, current_stats->build.qualified_rows,
           qdump_hashjoin_type_string (current_stats->hash_method));

#if HASHJOIN_PROFILE_TIME
      fprintf (fp, ", (F: %d, H: %d, I: %d)",
           TO_MSEC (current_stats->profile.build.fetch), TO_MSEC (current_stats->profile.build.hash),
           TO_MSEC (current_stats->profile.build.insert));
#endif /* HASHJOIN_PROFILE_TIME */

      fprintf (fp, "\n");
    }

      indent -= 2;
#endif /* HASHJOIN_DUMP_PARTITION */

      if (stats->num_parallel_threads > 1)
    {
      fprintf (fp,
           "%*cPROBE (time: %d..%d, fetch: %ld, ioread: %ld, readrows: %ld, readkeys: %ld, rows: %ld)\n",
           indent, ' ', TO_MSEC (stats->probe.range_time.min), TO_MSEC (stats->probe.range_time.max),
           stats->probe.fetches, stats->probe.ioreads, stats->probe.read_rows, stats->probe.read_keys,
           stats->probe.qualified_rows);
    }
      else
    {
      fprintf (fp,
           "%*cPROBE (time: %d, fetch: %ld, ioread: %ld, readrows: %ld, readkeys: %ld, rows: %ld)\n",
           indent, ' ', TO_MSEC (stats->probe.elapsed_time),
           stats->probe.fetches, stats->probe.ioreads, stats->probe.read_rows, stats->probe.read_keys,
           stats->probe.qualified_rows);
    }

#if HASHJOIN_DUMP_PARTITION
      indent += 2;

      for (part_index = 0; part_index < part_cnt; part_index++)
    {
      current_stats = &part_stats[part_index];
      assert (current_stats != NULL);

      fprintf (fp,
           "%*c#%d (time: %d, fetch: %ld, ioread: %ld, readrows: %ld, readkeys: %ld, rows: %ld)",
           indent, ' ', part_index + 1, TO_MSEC (current_stats->probe.elapsed_time),
           current_stats->probe.fetches, current_stats->probe.ioreads, current_stats->probe.read_rows,
           current_stats->probe.read_keys, current_stats->probe.qualified_rows);

#if HASHJOIN_PROFILE_TIME
      fprintf (fp, ", (F: %d, H: %d, S: %d, M: %d, A: %d)",
           TO_MSEC (current_stats->profile.probe.fetch), TO_MSEC (current_stats->profile.probe.hash),
           TO_MSEC (current_stats->profile.probe.search), TO_MSEC (current_stats->profile.probe.match),
           TO_MSEC (current_stats->profile.probe.add));
#endif /* HASHJOIN_PROFILE_TIME */

      fprintf (fp, "\n");
    }

      indent -= 2;
#endif /* HASHJOIN_DUMP_PARTITION */

      if (stats->num_parallel_threads > 1)
    {
      indent -= 2;
    }

#if HASHJOIN_PROFILE_TIME
      fprintf (fp, "%*cMERGE (time: %d, fetch: %ld, ioread: %ld, rows: %ld)\n", indent, ' ',
           TO_MSEC (stats->profile.merge.elapsed_time), stats->profile.merge.fetches, stats->profile.merge.ioreads,
           stats->profile.merge.qualified_rows);
#endif /* HASHJOIN_PROFILE_TIME */
    }

  /* parallel subquery or partitioned hash join */
  if (xasl_p->px_executor != NULL || part_cnt > 1)
    {
      fprintf (fp, "%*cSUBQUERY (uncorrelated)\n", indent, ' ');

      if (xasl_p->px_executor)
    {
      fprintf (fp,
           "%*c         (parallel workers: %d, time: %d, fetch: %lld, fetch_time: %lld, ioread: %lld)\n",
           indent, ' ', xasl_p->px_executor->get_parallelism () + 1,
           TO_MSEC (xasl_p->px_executor->get_stats ().elapsed_time),
           (long long int) xasl_p->px_executor->get_stats ().fetches,
           (long long int) xasl_p->px_executor->get_stats ().fetch_time,
           (long long int) xasl_p->px_executor->get_stats ().ioreads);
    }

      qdump_print_stats_text (fp, outer_xasl, indent);
      qdump_print_stats_text (fp, inner_xasl, indent);
    }
}

static void
qdump_print_hashjoin_stats_json (xasl_node * xasl_p, json_t * parent)
{
  json_t *split, *part_array, *parallel, *build, *probe, *merge, *subquery;
  json_t *input, *profile;

  XASL_NODE *outer_xasl, *inner_xasl;

  HASHJOIN_PROC_NODE *proc;
  HASHJOIN_STATS_GROUP *stats_group;
  HASHJOIN_STATS *stats, *part_stats, *current_stats;
  UINT32 part_cnt, part_index;

  char hash_method_str[32];
  char time_str[100];
  int len;
  bool need_separator;

  assert (xasl_p != NULL);
  assert (parent != NULL);

  proc = &xasl_p->proc.hashjoin;
  stats_group = &proc->stats_group;
  stats = &stats_group->stats;
  part_stats = stats_group->context_stats;
  part_cnt = stats_group->context_cnt;
  assert (part_stats == NULL || part_cnt > 1);

  outer_xasl = proc->outer.xasl;
  inner_xasl = proc->inner.xasl;
  assert (outer_xasl != NULL);
  assert (inner_xasl != NULL);

  if (part_cnt > 1)
    {
      len = 0;
      need_separator = false;

      if (stats->use_hash_memory)
    {
      len += sprintf (hash_method_str + len, "%s", qdump_hashjoin_type_string (HASH_METH_IN_MEM));
      need_separator = true;
    }

      if (stats->use_hash_hybrid)
    {
      len +=
        sprintf (hash_method_str + len, "%s%s", (need_separator) ? "+" : "",
             qdump_hashjoin_type_string (HASH_METH_HYBRID));
      need_separator = true;
    }

      if (stats->use_hash_file)
    {
      len +=
        sprintf (hash_method_str + len, "%s%s", (need_separator) ? "+" : "",
             qdump_hashjoin_type_string (HASH_METH_HASH_FILE));
      need_separator = true;
    }

      if (stats->use_hash_skip)
    {
      len +=
        sprintf (hash_method_str + len, "%s%s", (need_separator) ? "+" : "",
             qdump_hashjoin_type_string (HASH_METH_NOT_USE));
      need_separator = true;
    }

      hash_method_str[len] = '\0';
    }

  if (part_cnt <= 1)
    {
      XASL_NODE *build_xasl, *probe_xasl;

      if (stats->swap_join_inputs)
    {
      build_xasl = outer_xasl;
      probe_xasl = inner_xasl;
    }
      else
    {
      build_xasl = inner_xasl;
      probe_xasl = outer_xasl;
    }

      /* no parallel subquery */
      if (xasl_p->px_executor == NULL)
    {
      TSC_ADD_TIMEVAL (stats->build.elapsed_time, build_xasl->xasl_stats.elapsed_time);
      stats->build.fetches += build_xasl->xasl_stats.fetches;
      stats->build.ioreads += build_xasl->xasl_stats.ioreads;

      TSC_ADD_TIMEVAL (stats->probe.elapsed_time, probe_xasl->xasl_stats.elapsed_time);
      stats->probe.fetches += probe_xasl->xasl_stats.fetches;
      stats->probe.ioreads += probe_xasl->xasl_stats.ioreads;
    }

      build = json_object ();
      json_object_set_new (build, "time", json_integer (TO_MSEC (stats->build.elapsed_time)));
      json_object_set_new (build, "fetch", json_integer (stats->build.fetches));
      json_object_set_new (build, "ioread", json_integer (stats->build.ioreads));
      json_object_set_new (build, "rows", json_integer (stats->build.qualified_rows));
      json_object_set_new (build, "method", json_string (qdump_hashjoin_type_string (stats->hash_method)));

#if HASHJOIN_COLLISION_RATE
      if (stats->use_hash_file)
    {
      /* nothing to do */
    }
      else
    {
      json_object_set_new (build, "collision_rate", json_real (stats->collision_rate * 100));
    }
#endif /* HASHJOIN_COLLISION_RATE */
      json_object_set_new (parent, "build", build);

#if HASHJOIN_PROFILE_TIME
      profile = json_object ();
      json_object_set_new (profile, "F", json_integer (TO_MSEC (stats->profile.build.fetch)));
      json_object_set_new (profile, "H", json_integer (TO_MSEC (stats->profile.build.hash)));
      json_object_set_new (profile, "I", json_integer (TO_MSEC (stats->profile.build.insert)));
      json_object_set_new (build, "profile", profile);
#endif /* HASHJOIN_PROFILE_TIME */

      /* no parallel subquery */
      if (xasl_p->px_executor == NULL)
    {
      input = json_object ();
      qdump_print_stats_json (build_xasl, input);
      json_object_set_new (build, "input", input);
    }

      probe = json_object ();
      json_object_set_new (probe, "time", json_integer (TO_MSEC (stats->probe.elapsed_time)));
      json_object_set_new (probe, "fetch", json_integer (stats->probe.fetches));
      json_object_set_new (probe, "ioread", json_integer (stats->probe.ioreads));
      json_object_set_new (probe, "readrows", json_integer (stats->probe.read_rows));
      json_object_set_new (probe, "readkeys", json_integer (stats->probe.read_keys));
      json_object_set_new (probe, "rows", json_integer (stats->probe.qualified_rows));
      json_object_set_new (parent, "probe", probe);

#if HASHJOIN_PROFILE_TIME
      profile = json_object ();
      json_object_set_new (profile, "F", json_integer (TO_MSEC (stats->profile.probe.fetch)));
      json_object_set_new (profile, "H", json_integer (TO_MSEC (stats->profile.probe.hash)));
      json_object_set_new (profile, "S", json_integer (TO_MSEC (stats->profile.probe.search)));
      json_object_set_new (profile, "M", json_integer (TO_MSEC (stats->profile.probe.match)));
      json_object_set_new (profile, "A", json_integer (TO_MSEC (stats->profile.probe.add)));
      json_object_set_new (probe, "profile", profile);
#endif /* HASHJOIN_PROFILE_TIME */

      /* no parallel subquery */
      if (xasl_p->px_executor == NULL)
    {
      input = json_object ();
      qdump_print_stats_json (probe_xasl, input);
      json_object_set_new (probe, "input", input);
    }
    }
  else
    {
      split = json_object ();
      json_object_set_new (split, "time", json_integer (TO_MSEC (stats->split.elapsed_time)));
      json_object_set_new (split, "fetch", json_integer (stats->split.fetches));
      json_object_set_new (split, "ioread", json_integer (stats->split.ioreads));
      json_object_set_new (split, "partitions", json_integer (part_cnt));
      json_object_set_new (parent, "split", split);

      build = json_object ();
      if (stats->num_parallel_threads > 1)
    {
      len =
        sprintf (time_str, "%d..%d", TO_MSEC (stats->build.range_time.min), TO_MSEC (stats->build.range_time.max));
      time_str[len] = '\0';

      json_object_set_new (build, "time", json_string (time_str));
    }
      else
    {
      json_object_set_new (build, "time", json_integer (TO_MSEC (stats->build.elapsed_time)));
    }
      json_object_set_new (build, "fetch", json_integer (stats->build.fetches));
      json_object_set_new (build, "ioread", json_integer (stats->build.ioreads));
      json_object_set_new (build, "rows", json_integer (stats->build.qualified_rows));
      json_object_set_new (build, "method", json_string (hash_method_str));

#if HASHJOIN_COLLISION_RATE
      if (stats->use_hash_file)
    {
      /* nothing to do */
    }
      else
    {
      json_object_set_new (build, "collision_rate", json_real (stats->collision_rate * 100));
    }
#endif /* HASHJOIN_COLLISION_RATE */

#if HASHJOIN_DUMP_PARTITION
      part_array = json_array ();

      for (part_index = 0; part_index < part_cnt; part_index++)
    {
      current_stats = &part_stats[part_index];
      assert (current_stats != NULL);

      input = json_object ();
      json_object_set_new (input, "time", json_integer (TO_MSEC (current_stats->build.elapsed_time)));
      json_object_set_new (input, "fetch", json_integer (current_stats->build.fetches));
      json_object_set_new (input, "ioread", json_integer (current_stats->build.ioreads));
      json_object_set_new (input, "rows", json_integer (current_stats->build.qualified_rows));
      json_object_set_new (input, "method", json_string (qdump_hashjoin_type_string (current_stats->hash_method)));
      json_array_append_new (part_array, input);

#if HASHJOIN_PROFILE_TIME
      profile = json_object ();
      json_object_set_new (profile, "F", json_integer (TO_MSEC (current_stats->profile.build.fetch)));
      json_object_set_new (profile, "H", json_integer (TO_MSEC (current_stats->profile.build.hash)));
      json_object_set_new (profile, "I", json_integer (TO_MSEC (current_stats->profile.build.insert)));
      json_object_set_new (input, "profile", profile);
#endif /* HASHJOIN_PROFILE_TIME */
    }

      json_object_set_new (build, "partition_list", part_array);
#endif /* HASHJOIN_DUMP_PARTITION */

      probe = json_object ();
      if (stats->num_parallel_threads > 1)
    {
      len =
        sprintf (time_str, "%d..%d", TO_MSEC (stats->probe.range_time.min), TO_MSEC (stats->probe.range_time.max));
      time_str[len] = '\0';

      json_object_set_new (probe, "time", json_string (time_str));
    }
      else
    {
      json_object_set_new (probe, "time", json_integer (TO_MSEC (stats->probe.elapsed_time)));
    }
      json_object_set_new (probe, "fetch", json_integer (stats->probe.fetches));
      json_object_set_new (probe, "ioread", json_integer (stats->probe.ioreads));
      json_object_set_new (probe, "readrows", json_integer (stats->probe.read_rows));
      json_object_set_new (probe, "readkeys", json_integer (stats->probe.read_keys));
      json_object_set_new (probe, "rows", json_integer (stats->probe.qualified_rows));

#if HASHJOIN_DUMP_PARTITION
      part_array = json_array ();

      for (part_index = 0; part_index < part_cnt; part_index++)
    {
      current_stats = &part_stats[part_index];
      assert (current_stats != NULL);

      input = json_object ();
      json_object_set_new (input, "part_id", json_integer (part_index + 1));
      json_object_set_new (input, "time", json_integer (TO_MSEC (current_stats->probe.elapsed_time)));
      json_object_set_new (input, "fetch", json_integer (current_stats->probe.fetches));
      json_object_set_new (input, "ioread", json_integer (current_stats->probe.ioreads));
      json_object_set_new (input, "readrows", json_integer (current_stats->probe.read_rows));
      json_object_set_new (input, "readkeys", json_integer (current_stats->probe.read_keys));
      json_object_set_new (input, "rows", json_integer (current_stats->probe.qualified_rows));
      json_array_append_new (part_array, input);

#if HASHJOIN_PROFILE_TIME
      profile = json_object ();
      json_object_set_new (profile, "F", json_integer (TO_MSEC (current_stats->profile.probe.fetch)));
      json_object_set_new (profile, "H", json_integer (TO_MSEC (current_stats->profile.probe.hash)));
      json_object_set_new (profile, "S", json_integer (TO_MSEC (current_stats->profile.probe.search)));
      json_object_set_new (profile, "M", json_integer (TO_MSEC (current_stats->profile.probe.match)));
      json_object_set_new (profile, "A", json_integer (TO_MSEC (current_stats->profile.probe.add)));
      json_object_set_new (input, "profile", profile);
#endif /* HASHJOIN_PROFILE_TIME */
    }

      json_object_set_new (probe, "partition_list", part_array);
#endif /* HASHJOIN_DUMP_PARTITION */

      if (stats->num_parallel_threads > 1)
    {
      parallel = json_object ();
      json_object_set_new (parallel, "time", json_integer (TO_MSEC (stats->parallel.elapsed_time)));

      json_object_set_new (parent, "parallel", parallel);
      json_object_set_new (parallel, "build", build);
      json_object_set_new (parallel, "probe", probe);
    }
      else
    {
      json_object_set_new (parent, "build", build);
      json_object_set_new (parent, "probe", probe);
    }

#if HASHJOIN_PROFILE_TIME
      merge = json_object ();
      json_object_set_new (merge, "time", json_integer (TO_MSEC (stats->profile.merge.elapsed_time)));
      json_object_set_new (merge, "fetch", json_integer (stats->profile.merge.fetches));
      json_object_set_new (merge, "ioread", json_integer (stats->profile.merge.ioreads));
      json_object_set_new (merge, "rows", json_integer (stats->profile.merge.qualified_rows));
      json_object_set_new (parent, "merge", merge);
#endif /* HASHJOIN_PROFILE_TIME */
    }

  /* parallel subquery or partitioned hash join */
  if (xasl_p->px_executor != NULL || part_cnt > 1)
    {
      subquery = json_array ();

      if (xasl_p->px_executor)
    {
      input = json_object ();
      qdump_print_px_subquery_stats_json (xasl_p->px_executor, input);
      json_array_append_new (subquery, input);
    }

      input = json_object ();
      qdump_print_stats_json (outer_xasl, input);
      json_array_append_new (subquery, input);

      input = json_object ();
      qdump_print_stats_json (inner_xasl, input);
      json_array_append_new (subquery, input);

      json_object_set_new (parent, "SUBQUERY (uncorrelated)", subquery);
    }
}

#endif /* SERVER_MODE */